roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787
788     /**
789      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Object} an existing reader (eg. copied from another store)
1092  * @cfg {Array} data The multi-dimensional array of data
1093  * @constructor
1094  * @param {Object} config
1095  */
1096 Roo.data.SimpleStore = function(config)
1097 {
1098     Roo.data.SimpleStore.superclass.constructor.call(this, {
1099         isLocal : true,
1100         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1101                 id: config.id
1102             },
1103             Roo.data.Record.create(config.fields)
1104         ),
1105         proxy : new Roo.data.MemoryProxy(config.data)
1106     });
1107     this.load();
1108 };
1109 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1110  * Based on:
1111  * Ext JS Library 1.1.1
1112  * Copyright(c) 2006-2007, Ext JS, LLC.
1113  *
1114  * Originally Released Under LGPL - original licence link has changed is not relivant.
1115  *
1116  * Fork - LGPL
1117  * <script type="text/javascript">
1118  */
1119
1120 /**
1121 /**
1122  * @extends Roo.data.Store
1123  * @class Roo.data.JsonStore
1124  * Small helper class to make creating Stores for JSON data easier. <br/>
1125 <pre><code>
1126 var store = new Roo.data.JsonStore({
1127     url: 'get-images.php',
1128     root: 'images',
1129     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1130 });
1131 </code></pre>
1132  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1133  * JsonReader and HttpProxy (unless inline data is provided).</b>
1134  * @cfg {Array} fields An array of field definition objects, or field name strings.
1135  * @constructor
1136  * @param {Object} config
1137  */
1138 Roo.data.JsonStore = function(c){
1139     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1140         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1141         reader: new Roo.data.JsonReader(c, c.fields)
1142     }));
1143 };
1144 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1145  * Based on:
1146  * Ext JS Library 1.1.1
1147  * Copyright(c) 2006-2007, Ext JS, LLC.
1148  *
1149  * Originally Released Under LGPL - original licence link has changed is not relivant.
1150  *
1151  * Fork - LGPL
1152  * <script type="text/javascript">
1153  */
1154
1155  
1156 Roo.data.Field = function(config){
1157     if(typeof config == "string"){
1158         config = {name: config};
1159     }
1160     Roo.apply(this, config);
1161     
1162     if(!this.type){
1163         this.type = "auto";
1164     }
1165     
1166     var st = Roo.data.SortTypes;
1167     // named sortTypes are supported, here we look them up
1168     if(typeof this.sortType == "string"){
1169         this.sortType = st[this.sortType];
1170     }
1171     
1172     // set default sortType for strings and dates
1173     if(!this.sortType){
1174         switch(this.type){
1175             case "string":
1176                 this.sortType = st.asUCString;
1177                 break;
1178             case "date":
1179                 this.sortType = st.asDate;
1180                 break;
1181             default:
1182                 this.sortType = st.none;
1183         }
1184     }
1185
1186     // define once
1187     var stripRe = /[\$,%]/g;
1188
1189     // prebuilt conversion function for this field, instead of
1190     // switching every time we're reading a value
1191     if(!this.convert){
1192         var cv, dateFormat = this.dateFormat;
1193         switch(this.type){
1194             case "":
1195             case "auto":
1196             case undefined:
1197                 cv = function(v){ return v; };
1198                 break;
1199             case "string":
1200                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1201                 break;
1202             case "int":
1203                 cv = function(v){
1204                     return v !== undefined && v !== null && v !== '' ?
1205                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1206                     };
1207                 break;
1208             case "float":
1209                 cv = function(v){
1210                     return v !== undefined && v !== null && v !== '' ?
1211                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1212                     };
1213                 break;
1214             case "bool":
1215             case "boolean":
1216                 cv = function(v){ return v === true || v === "true" || v == 1; };
1217                 break;
1218             case "date":
1219                 cv = function(v){
1220                     if(!v){
1221                         return '';
1222                     }
1223                     if(v instanceof Date){
1224                         return v;
1225                     }
1226                     if(dateFormat){
1227                         if(dateFormat == "timestamp"){
1228                             return new Date(v*1000);
1229                         }
1230                         return Date.parseDate(v, dateFormat);
1231                     }
1232                     var parsed = Date.parse(v);
1233                     return parsed ? new Date(parsed) : null;
1234                 };
1235              break;
1236             
1237         }
1238         this.convert = cv;
1239     }
1240 };
1241
1242 Roo.data.Field.prototype = {
1243     dateFormat: null,
1244     defaultValue: "",
1245     mapping: null,
1246     sortType : null,
1247     sortDir : "ASC"
1248 };/*
1249  * Based on:
1250  * Ext JS Library 1.1.1
1251  * Copyright(c) 2006-2007, Ext JS, LLC.
1252  *
1253  * Originally Released Under LGPL - original licence link has changed is not relivant.
1254  *
1255  * Fork - LGPL
1256  * <script type="text/javascript">
1257  */
1258  
1259 // Base class for reading structured data from a data source.  This class is intended to be
1260 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1261
1262 /**
1263  * @class Roo.data.DataReader
1264  * Base class for reading structured data from a data source.  This class is intended to be
1265  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1266  */
1267
1268 Roo.data.DataReader = function(meta, recordType){
1269     
1270     this.meta = meta;
1271     
1272     this.recordType = recordType instanceof Array ? 
1273         Roo.data.Record.create(recordType) : recordType;
1274 };
1275
1276 Roo.data.DataReader.prototype = {
1277     
1278     
1279     readerType : 'Data',
1280      /**
1281      * Create an empty record
1282      * @param {Object} data (optional) - overlay some values
1283      * @return {Roo.data.Record} record created.
1284      */
1285     newRow :  function(d) {
1286         var da =  {};
1287         this.recordType.prototype.fields.each(function(c) {
1288             switch( c.type) {
1289                 case 'int' : da[c.name] = 0; break;
1290                 case 'date' : da[c.name] = new Date(); break;
1291                 case 'float' : da[c.name] = 0.0; break;
1292                 case 'boolean' : da[c.name] = false; break;
1293                 default : da[c.name] = ""; break;
1294             }
1295             
1296         });
1297         return new this.recordType(Roo.apply(da, d));
1298     }
1299     
1300     
1301 };/*
1302  * Based on:
1303  * Ext JS Library 1.1.1
1304  * Copyright(c) 2006-2007, Ext JS, LLC.
1305  *
1306  * Originally Released Under LGPL - original licence link has changed is not relivant.
1307  *
1308  * Fork - LGPL
1309  * <script type="text/javascript">
1310  */
1311
1312 /**
1313  * @class Roo.data.DataProxy
1314  * @extends Roo.data.Observable
1315  * This class is an abstract base class for implementations which provide retrieval of
1316  * unformatted data objects.<br>
1317  * <p>
1318  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1319  * (of the appropriate type which knows how to parse the data object) to provide a block of
1320  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1321  * <p>
1322  * Custom implementations must implement the load method as described in
1323  * {@link Roo.data.HttpProxy#load}.
1324  */
1325 Roo.data.DataProxy = function(){
1326     this.addEvents({
1327         /**
1328          * @event beforeload
1329          * Fires before a network request is made to retrieve a data object.
1330          * @param {Object} This DataProxy object.
1331          * @param {Object} params The params parameter to the load function.
1332          */
1333         beforeload : true,
1334         /**
1335          * @event load
1336          * Fires before the load method's callback is called.
1337          * @param {Object} This DataProxy object.
1338          * @param {Object} o The data object.
1339          * @param {Object} arg The callback argument object passed to the load function.
1340          */
1341         load : true,
1342         /**
1343          * @event loadexception
1344          * Fires if an Exception occurs during data retrieval.
1345          * @param {Object} This DataProxy object.
1346          * @param {Object} o The data object.
1347          * @param {Object} arg The callback argument object passed to the load function.
1348          * @param {Object} e The Exception.
1349          */
1350         loadexception : true
1351     });
1352     Roo.data.DataProxy.superclass.constructor.call(this);
1353 };
1354
1355 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1356
1357     /**
1358      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1359      */
1360 /*
1361  * Based on:
1362  * Ext JS Library 1.1.1
1363  * Copyright(c) 2006-2007, Ext JS, LLC.
1364  *
1365  * Originally Released Under LGPL - original licence link has changed is not relivant.
1366  *
1367  * Fork - LGPL
1368  * <script type="text/javascript">
1369  */
1370 /**
1371  * @class Roo.data.MemoryProxy
1372  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1373  * to the Reader when its load method is called.
1374  * @constructor
1375  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1376  */
1377 Roo.data.MemoryProxy = function(data){
1378     if (data.data) {
1379         data = data.data;
1380     }
1381     Roo.data.MemoryProxy.superclass.constructor.call(this);
1382     this.data = data;
1383 };
1384
1385 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1386     
1387     /**
1388      * Load data from the requested source (in this case an in-memory
1389      * data object passed to the constructor), read the data object into
1390      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1391      * process that block using the passed callback.
1392      * @param {Object} params This parameter is not used by the MemoryProxy class.
1393      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1394      * object into a block of Roo.data.Records.
1395      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1396      * The function must be passed <ul>
1397      * <li>The Record block object</li>
1398      * <li>The "arg" argument from the load function</li>
1399      * <li>A boolean success indicator</li>
1400      * </ul>
1401      * @param {Object} scope The scope in which to call the callback
1402      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1403      */
1404     load : function(params, reader, callback, scope, arg){
1405         params = params || {};
1406         var result;
1407         try {
1408             result = reader.readRecords(params.data ? params.data :this.data);
1409         }catch(e){
1410             this.fireEvent("loadexception", this, arg, null, e);
1411             callback.call(scope, null, arg, false);
1412             return;
1413         }
1414         callback.call(scope, result, arg, true);
1415     },
1416     
1417     // private
1418     update : function(params, records){
1419         
1420     }
1421 });/*
1422  * Based on:
1423  * Ext JS Library 1.1.1
1424  * Copyright(c) 2006-2007, Ext JS, LLC.
1425  *
1426  * Originally Released Under LGPL - original licence link has changed is not relivant.
1427  *
1428  * Fork - LGPL
1429  * <script type="text/javascript">
1430  */
1431 /**
1432  * @class Roo.data.HttpProxy
1433  * @extends Roo.data.DataProxy
1434  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1435  * configured to reference a certain URL.<br><br>
1436  * <p>
1437  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1438  * from which the running page was served.<br><br>
1439  * <p>
1440  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1441  * <p>
1442  * Be aware that to enable the browser to parse an XML document, the server must set
1443  * the Content-Type header in the HTTP response to "text/xml".
1444  * @constructor
1445  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1446  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1447  * will be used to make the request.
1448  */
1449 Roo.data.HttpProxy = function(conn){
1450     Roo.data.HttpProxy.superclass.constructor.call(this);
1451     // is conn a conn config or a real conn?
1452     this.conn = conn;
1453     this.useAjax = !conn || !conn.events;
1454   
1455 };
1456
1457 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1458     // thse are take from connection...
1459     
1460     /**
1461      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1462      */
1463     /**
1464      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1465      * extra parameters to each request made by this object. (defaults to undefined)
1466      */
1467     /**
1468      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1469      *  to each request made by this object. (defaults to undefined)
1470      */
1471     /**
1472      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1473      */
1474     /**
1475      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1476      */
1477      /**
1478      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1479      * @type Boolean
1480      */
1481   
1482
1483     /**
1484      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1485      * @type Boolean
1486      */
1487     /**
1488      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1489      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1490      * a finer-grained basis than the DataProxy events.
1491      */
1492     getConnection : function(){
1493         return this.useAjax ? Roo.Ajax : this.conn;
1494     },
1495
1496     /**
1497      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1498      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1499      * process that block using the passed callback.
1500      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1501      * for the request to the remote server.
1502      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1503      * object into a block of Roo.data.Records.
1504      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1505      * The function must be passed <ul>
1506      * <li>The Record block object</li>
1507      * <li>The "arg" argument from the load function</li>
1508      * <li>A boolean success indicator</li>
1509      * </ul>
1510      * @param {Object} scope The scope in which to call the callback
1511      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1512      */
1513     load : function(params, reader, callback, scope, arg){
1514         if(this.fireEvent("beforeload", this, params) !== false){
1515             var  o = {
1516                 params : params || {},
1517                 request: {
1518                     callback : callback,
1519                     scope : scope,
1520                     arg : arg
1521                 },
1522                 reader: reader,
1523                 callback : this.loadResponse,
1524                 scope: this
1525             };
1526             if(this.useAjax){
1527                 Roo.applyIf(o, this.conn);
1528                 if(this.activeRequest){
1529                     Roo.Ajax.abort(this.activeRequest);
1530                 }
1531                 this.activeRequest = Roo.Ajax.request(o);
1532             }else{
1533                 this.conn.request(o);
1534             }
1535         }else{
1536             callback.call(scope||this, null, arg, false);
1537         }
1538     },
1539
1540     // private
1541     loadResponse : function(o, success, response){
1542         delete this.activeRequest;
1543         if(!success){
1544             this.fireEvent("loadexception", this, o, response);
1545             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1546             return;
1547         }
1548         var result;
1549         try {
1550             result = o.reader.read(response);
1551         }catch(e){
1552             this.fireEvent("loadexception", this, o, response, e);
1553             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1554             return;
1555         }
1556         
1557         this.fireEvent("load", this, o, o.request.arg);
1558         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1559     },
1560
1561     // private
1562     update : function(dataSet){
1563
1564     },
1565
1566     // private
1567     updateResponse : function(dataSet){
1568
1569     }
1570 });/*
1571  * Based on:
1572  * Ext JS Library 1.1.1
1573  * Copyright(c) 2006-2007, Ext JS, LLC.
1574  *
1575  * Originally Released Under LGPL - original licence link has changed is not relivant.
1576  *
1577  * Fork - LGPL
1578  * <script type="text/javascript">
1579  */
1580
1581 /**
1582  * @class Roo.data.ScriptTagProxy
1583  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1584  * other than the originating domain of the running page.<br><br>
1585  * <p>
1586  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1587  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1588  * <p>
1589  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1590  * source code that is used as the source inside a &lt;script> tag.<br><br>
1591  * <p>
1592  * In order for the browser to process the returned data, the server must wrap the data object
1593  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1594  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1595  * depending on whether the callback name was passed:
1596  * <p>
1597  * <pre><code>
1598 boolean scriptTag = false;
1599 String cb = request.getParameter("callback");
1600 if (cb != null) {
1601     scriptTag = true;
1602     response.setContentType("text/javascript");
1603 } else {
1604     response.setContentType("application/x-json");
1605 }
1606 Writer out = response.getWriter();
1607 if (scriptTag) {
1608     out.write(cb + "(");
1609 }
1610 out.print(dataBlock.toJsonString());
1611 if (scriptTag) {
1612     out.write(");");
1613 }
1614 </pre></code>
1615  *
1616  * @constructor
1617  * @param {Object} config A configuration object.
1618  */
1619 Roo.data.ScriptTagProxy = function(config){
1620     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1621     Roo.apply(this, config);
1622     this.head = document.getElementsByTagName("head")[0];
1623 };
1624
1625 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1626
1627 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1628     /**
1629      * @cfg {String} url The URL from which to request the data object.
1630      */
1631     /**
1632      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1633      */
1634     timeout : 30000,
1635     /**
1636      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1637      * the server the name of the callback function set up by the load call to process the returned data object.
1638      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1639      * javascript output which calls this named function passing the data object as its only parameter.
1640      */
1641     callbackParam : "callback",
1642     /**
1643      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1644      * name to the request.
1645      */
1646     nocache : true,
1647
1648     /**
1649      * Load data from the configured URL, read the data object into
1650      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1651      * process that block using the passed callback.
1652      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1653      * for the request to the remote server.
1654      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1655      * object into a block of Roo.data.Records.
1656      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1657      * The function must be passed <ul>
1658      * <li>The Record block object</li>
1659      * <li>The "arg" argument from the load function</li>
1660      * <li>A boolean success indicator</li>
1661      * </ul>
1662      * @param {Object} scope The scope in which to call the callback
1663      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1664      */
1665     load : function(params, reader, callback, scope, arg){
1666         if(this.fireEvent("beforeload", this, params) !== false){
1667
1668             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1669
1670             var url = this.url;
1671             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1672             if(this.nocache){
1673                 url += "&_dc=" + (new Date().getTime());
1674             }
1675             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1676             var trans = {
1677                 id : transId,
1678                 cb : "stcCallback"+transId,
1679                 scriptId : "stcScript"+transId,
1680                 params : params,
1681                 arg : arg,
1682                 url : url,
1683                 callback : callback,
1684                 scope : scope,
1685                 reader : reader
1686             };
1687             var conn = this;
1688
1689             window[trans.cb] = function(o){
1690                 conn.handleResponse(o, trans);
1691             };
1692
1693             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1694
1695             if(this.autoAbort !== false){
1696                 this.abort();
1697             }
1698
1699             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1700
1701             var script = document.createElement("script");
1702             script.setAttribute("src", url);
1703             script.setAttribute("type", "text/javascript");
1704             script.setAttribute("id", trans.scriptId);
1705             this.head.appendChild(script);
1706
1707             this.trans = trans;
1708         }else{
1709             callback.call(scope||this, null, arg, false);
1710         }
1711     },
1712
1713     // private
1714     isLoading : function(){
1715         return this.trans ? true : false;
1716     },
1717
1718     /**
1719      * Abort the current server request.
1720      */
1721     abort : function(){
1722         if(this.isLoading()){
1723             this.destroyTrans(this.trans);
1724         }
1725     },
1726
1727     // private
1728     destroyTrans : function(trans, isLoaded){
1729         this.head.removeChild(document.getElementById(trans.scriptId));
1730         clearTimeout(trans.timeoutId);
1731         if(isLoaded){
1732             window[trans.cb] = undefined;
1733             try{
1734                 delete window[trans.cb];
1735             }catch(e){}
1736         }else{
1737             // if hasn't been loaded, wait for load to remove it to prevent script error
1738             window[trans.cb] = function(){
1739                 window[trans.cb] = undefined;
1740                 try{
1741                     delete window[trans.cb];
1742                 }catch(e){}
1743             };
1744         }
1745     },
1746
1747     // private
1748     handleResponse : function(o, trans){
1749         this.trans = false;
1750         this.destroyTrans(trans, true);
1751         var result;
1752         try {
1753             result = trans.reader.readRecords(o);
1754         }catch(e){
1755             this.fireEvent("loadexception", this, o, trans.arg, e);
1756             trans.callback.call(trans.scope||window, null, trans.arg, false);
1757             return;
1758         }
1759         this.fireEvent("load", this, o, trans.arg);
1760         trans.callback.call(trans.scope||window, result, trans.arg, true);
1761     },
1762
1763     // private
1764     handleFailure : function(trans){
1765         this.trans = false;
1766         this.destroyTrans(trans, false);
1767         this.fireEvent("loadexception", this, null, trans.arg);
1768         trans.callback.call(trans.scope||window, null, trans.arg, false);
1769     }
1770 });/*
1771  * Based on:
1772  * Ext JS Library 1.1.1
1773  * Copyright(c) 2006-2007, Ext JS, LLC.
1774  *
1775  * Originally Released Under LGPL - original licence link has changed is not relivant.
1776  *
1777  * Fork - LGPL
1778  * <script type="text/javascript">
1779  */
1780
1781 /**
1782  * @class Roo.data.JsonReader
1783  * @extends Roo.data.DataReader
1784  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1785  * based on mappings in a provided Roo.data.Record constructor.
1786  * 
1787  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1788  * in the reply previously. 
1789  * 
1790  * <p>
1791  * Example code:
1792  * <pre><code>
1793 var RecordDef = Roo.data.Record.create([
1794     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1795     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1796 ]);
1797 var myReader = new Roo.data.JsonReader({
1798     totalProperty: "results",    // The property which contains the total dataset size (optional)
1799     root: "rows",                // The property which contains an Array of row objects
1800     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1801 }, RecordDef);
1802 </code></pre>
1803  * <p>
1804  * This would consume a JSON file like this:
1805  * <pre><code>
1806 { 'results': 2, 'rows': [
1807     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1808     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1809 }
1810 </code></pre>
1811  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1812  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1813  * paged from the remote server.
1814  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1815  * @cfg {String} root name of the property which contains the Array of row objects.
1816  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1817  * @cfg {Array} fields Array of field definition objects
1818  * @constructor
1819  * Create a new JsonReader
1820  * @param {Object} meta Metadata configuration options
1821  * @param {Object} recordType Either an Array of field definition objects,
1822  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1823  */
1824 Roo.data.JsonReader = function(meta, recordType){
1825     
1826     meta = meta || {};
1827     // set some defaults:
1828     Roo.applyIf(meta, {
1829         totalProperty: 'total',
1830         successProperty : 'success',
1831         root : 'data',
1832         id : 'id'
1833     });
1834     
1835     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1836 };
1837 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1838     
1839     readerType : 'Json',
1840     
1841     /**
1842      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1843      * Used by Store query builder to append _requestMeta to params.
1844      * 
1845      */
1846     metaFromRemote : false,
1847     /**
1848      * This method is only used by a DataProxy which has retrieved data from a remote server.
1849      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1850      * @return {Object} data A data block which is used by an Roo.data.Store object as
1851      * a cache of Roo.data.Records.
1852      */
1853     read : function(response){
1854         var json = response.responseText;
1855        
1856         var o = /* eval:var:o */ eval("("+json+")");
1857         if(!o) {
1858             throw {message: "JsonReader.read: Json object not found"};
1859         }
1860         
1861         if(o.metaData){
1862             
1863             delete this.ef;
1864             this.metaFromRemote = true;
1865             this.meta = o.metaData;
1866             this.recordType = Roo.data.Record.create(o.metaData.fields);
1867             this.onMetaChange(this.meta, this.recordType, o);
1868         }
1869         return this.readRecords(o);
1870     },
1871
1872     // private function a store will implement
1873     onMetaChange : function(meta, recordType, o){
1874
1875     },
1876
1877     /**
1878          * @ignore
1879          */
1880     simpleAccess: function(obj, subsc) {
1881         return obj[subsc];
1882     },
1883
1884         /**
1885          * @ignore
1886          */
1887     getJsonAccessor: function(){
1888         var re = /[\[\.]/;
1889         return function(expr) {
1890             try {
1891                 return(re.test(expr))
1892                     ? new Function("obj", "return obj." + expr)
1893                     : function(obj){
1894                         return obj[expr];
1895                     };
1896             } catch(e){}
1897             return Roo.emptyFn;
1898         };
1899     }(),
1900
1901     /**
1902      * Create a data block containing Roo.data.Records from an XML document.
1903      * @param {Object} o An object which contains an Array of row objects in the property specified
1904      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1905      * which contains the total size of the dataset.
1906      * @return {Object} data A data block which is used by an Roo.data.Store object as
1907      * a cache of Roo.data.Records.
1908      */
1909     readRecords : function(o){
1910         /**
1911          * After any data loads, the raw JSON data is available for further custom processing.
1912          * @type Object
1913          */
1914         this.o = o;
1915         var s = this.meta, Record = this.recordType,
1916             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1917
1918 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1919         if (!this.ef) {
1920             if(s.totalProperty) {
1921                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1922                 }
1923                 if(s.successProperty) {
1924                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1925                 }
1926                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1927                 if (s.id) {
1928                         var g = this.getJsonAccessor(s.id);
1929                         this.getId = function(rec) {
1930                                 var r = g(rec);  
1931                                 return (r === undefined || r === "") ? null : r;
1932                         };
1933                 } else {
1934                         this.getId = function(){return null;};
1935                 }
1936             this.ef = [];
1937             for(var jj = 0; jj < fl; jj++){
1938                 f = fi[jj];
1939                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1940                 this.ef[jj] = this.getJsonAccessor(map);
1941             }
1942         }
1943
1944         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1945         if(s.totalProperty){
1946             var vt = parseInt(this.getTotal(o), 10);
1947             if(!isNaN(vt)){
1948                 totalRecords = vt;
1949             }
1950         }
1951         if(s.successProperty){
1952             var vs = this.getSuccess(o);
1953             if(vs === false || vs === 'false'){
1954                 success = false;
1955             }
1956         }
1957         var records = [];
1958         for(var i = 0; i < c; i++){
1959                 var n = root[i];
1960             var values = {};
1961             var id = this.getId(n);
1962             for(var j = 0; j < fl; j++){
1963                 f = fi[j];
1964             var v = this.ef[j](n);
1965             if (!f.convert) {
1966                 Roo.log('missing convert for ' + f.name);
1967                 Roo.log(f);
1968                 continue;
1969             }
1970             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1971             }
1972             var record = new Record(values, id);
1973             record.json = n;
1974             records[i] = record;
1975         }
1976         return {
1977             raw : o,
1978             success : success,
1979             records : records,
1980             totalRecords : totalRecords
1981         };
1982     }
1983 });/*
1984  * Based on:
1985  * Ext JS Library 1.1.1
1986  * Copyright(c) 2006-2007, Ext JS, LLC.
1987  *
1988  * Originally Released Under LGPL - original licence link has changed is not relivant.
1989  *
1990  * Fork - LGPL
1991  * <script type="text/javascript">
1992  */
1993
1994 /**
1995  * @class Roo.data.XmlReader
1996  * @extends Roo.data.DataReader
1997  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1998  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1999  * <p>
2000  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2001  * header in the HTTP response must be set to "text/xml".</em>
2002  * <p>
2003  * Example code:
2004  * <pre><code>
2005 var RecordDef = Roo.data.Record.create([
2006    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2007    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2008 ]);
2009 var myReader = new Roo.data.XmlReader({
2010    totalRecords: "results", // The element which contains the total dataset size (optional)
2011    record: "row",           // The repeated element which contains row information
2012    id: "id"                 // The element within the row that provides an ID for the record (optional)
2013 }, RecordDef);
2014 </code></pre>
2015  * <p>
2016  * This would consume an XML file like this:
2017  * <pre><code>
2018 &lt;?xml?>
2019 &lt;dataset>
2020  &lt;results>2&lt;/results>
2021  &lt;row>
2022    &lt;id>1&lt;/id>
2023    &lt;name>Bill&lt;/name>
2024    &lt;occupation>Gardener&lt;/occupation>
2025  &lt;/row>
2026  &lt;row>
2027    &lt;id>2&lt;/id>
2028    &lt;name>Ben&lt;/name>
2029    &lt;occupation>Horticulturalist&lt;/occupation>
2030  &lt;/row>
2031 &lt;/dataset>
2032 </code></pre>
2033  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2034  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2035  * paged from the remote server.
2036  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2037  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2038  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2039  * a record identifier value.
2040  * @constructor
2041  * Create a new XmlReader
2042  * @param {Object} meta Metadata configuration options
2043  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2044  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2045  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2046  */
2047 Roo.data.XmlReader = function(meta, recordType){
2048     meta = meta || {};
2049     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2050 };
2051 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2052     
2053     readerType : 'Xml',
2054     
2055     /**
2056      * This method is only used by a DataProxy which has retrieved data from a remote server.
2057          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2058          * to contain a method called 'responseXML' that returns an XML document object.
2059      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2060      * a cache of Roo.data.Records.
2061      */
2062     read : function(response){
2063         var doc = response.responseXML;
2064         if(!doc) {
2065             throw {message: "XmlReader.read: XML Document not available"};
2066         }
2067         return this.readRecords(doc);
2068     },
2069
2070     /**
2071      * Create a data block containing Roo.data.Records from an XML document.
2072          * @param {Object} doc A parsed XML document.
2073      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2074      * a cache of Roo.data.Records.
2075      */
2076     readRecords : function(doc){
2077         /**
2078          * After any data loads/reads, the raw XML Document is available for further custom processing.
2079          * @type XMLDocument
2080          */
2081         this.xmlData = doc;
2082         var root = doc.documentElement || doc;
2083         var q = Roo.DomQuery;
2084         var recordType = this.recordType, fields = recordType.prototype.fields;
2085         var sid = this.meta.id;
2086         var totalRecords = 0, success = true;
2087         if(this.meta.totalRecords){
2088             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2089         }
2090         
2091         if(this.meta.success){
2092             var sv = q.selectValue(this.meta.success, root, true);
2093             success = sv !== false && sv !== 'false';
2094         }
2095         var records = [];
2096         var ns = q.select(this.meta.record, root);
2097         for(var i = 0, len = ns.length; i < len; i++) {
2098                 var n = ns[i];
2099                 var values = {};
2100                 var id = sid ? q.selectValue(sid, n) : undefined;
2101                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2102                     var f = fields.items[j];
2103                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2104                     v = f.convert(v);
2105                     values[f.name] = v;
2106                 }
2107                 var record = new recordType(values, id);
2108                 record.node = n;
2109                 records[records.length] = record;
2110             }
2111
2112             return {
2113                 success : success,
2114                 records : records,
2115                 totalRecords : totalRecords || records.length
2116             };
2117     }
2118 });/*
2119  * Based on:
2120  * Ext JS Library 1.1.1
2121  * Copyright(c) 2006-2007, Ext JS, LLC.
2122  *
2123  * Originally Released Under LGPL - original licence link has changed is not relivant.
2124  *
2125  * Fork - LGPL
2126  * <script type="text/javascript">
2127  */
2128
2129 /**
2130  * @class Roo.data.ArrayReader
2131  * @extends Roo.data.DataReader
2132  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2133  * Each element of that Array represents a row of data fields. The
2134  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2135  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2136  * <p>
2137  * Example code:.
2138  * <pre><code>
2139 var RecordDef = Roo.data.Record.create([
2140     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2141     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2142 ]);
2143 var myReader = new Roo.data.ArrayReader({
2144     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2145 }, RecordDef);
2146 </code></pre>
2147  * <p>
2148  * This would consume an Array like this:
2149  * <pre><code>
2150 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2151   </code></pre>
2152  
2153  * @constructor
2154  * Create a new JsonReader
2155  * @param {Object} meta Metadata configuration options.
2156  * @param {Object|Array} recordType Either an Array of field definition objects
2157  * 
2158  * @cfg {Array} fields Array of field definition objects
2159  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2160  * as specified to {@link Roo.data.Record#create},
2161  * or an {@link Roo.data.Record} object
2162  *
2163  * 
2164  * created using {@link Roo.data.Record#create}.
2165  */
2166 Roo.data.ArrayReader = function(meta, recordType)
2167 {    
2168     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2169 };
2170
2171 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2172     
2173     readerType : 'Array',
2174     /**
2175      * Create a data block containing Roo.data.Records from an XML document.
2176      * @param {Object} o An Array of row objects which represents the dataset.
2177      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2178      * a cache of Roo.data.Records.
2179      */
2180     readRecords : function(o)
2181     {
2182         var sid = this.meta ? this.meta.id : null;
2183         var recordType = this.recordType, fields = recordType.prototype.fields;
2184         var records = [];
2185         var root = o;
2186         for(var i = 0; i < root.length; i++){
2187                 var n = root[i];
2188             var values = {};
2189             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2190             for(var j = 0, jlen = fields.length; j < jlen; j++){
2191                 var f = fields.items[j];
2192                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2193                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2194                 v = f.convert(v);
2195                 values[f.name] = v;
2196             }
2197             var record = new recordType(values, id);
2198             record.json = n;
2199             records[records.length] = record;
2200         }
2201         return {
2202             records : records,
2203             totalRecords : records.length
2204         };
2205     }
2206 });/*
2207  * Based on:
2208  * Ext JS Library 1.1.1
2209  * Copyright(c) 2006-2007, Ext JS, LLC.
2210  *
2211  * Originally Released Under LGPL - original licence link has changed is not relivant.
2212  *
2213  * Fork - LGPL
2214  * <script type="text/javascript">
2215  */
2216
2217
2218 /**
2219  * @class Roo.data.Tree
2220  * @extends Roo.util.Observable
2221  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2222  * in the tree have most standard DOM functionality.
2223  * @constructor
2224  * @param {Node} root (optional) The root node
2225  */
2226 Roo.data.Tree = function(root){
2227    this.nodeHash = {};
2228    /**
2229     * The root node for this tree
2230     * @type Node
2231     */
2232    this.root = null;
2233    if(root){
2234        this.setRootNode(root);
2235    }
2236    this.addEvents({
2237        /**
2238         * @event append
2239         * Fires when a new child node is appended to a node in this tree.
2240         * @param {Tree} tree The owner tree
2241         * @param {Node} parent The parent node
2242         * @param {Node} node The newly appended node
2243         * @param {Number} index The index of the newly appended node
2244         */
2245        "append" : true,
2246        /**
2247         * @event remove
2248         * Fires when a child node is removed from a node in this tree.
2249         * @param {Tree} tree The owner tree
2250         * @param {Node} parent The parent node
2251         * @param {Node} node The child node removed
2252         */
2253        "remove" : true,
2254        /**
2255         * @event move
2256         * Fires when a node is moved to a new location in the tree
2257         * @param {Tree} tree The owner tree
2258         * @param {Node} node The node moved
2259         * @param {Node} oldParent The old parent of this node
2260         * @param {Node} newParent The new parent of this node
2261         * @param {Number} index The index it was moved to
2262         */
2263        "move" : true,
2264        /**
2265         * @event insert
2266         * Fires when a new child node is inserted in a node in this tree.
2267         * @param {Tree} tree The owner tree
2268         * @param {Node} parent The parent node
2269         * @param {Node} node The child node inserted
2270         * @param {Node} refNode The child node the node was inserted before
2271         */
2272        "insert" : true,
2273        /**
2274         * @event beforeappend
2275         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2276         * @param {Tree} tree The owner tree
2277         * @param {Node} parent The parent node
2278         * @param {Node} node The child node to be appended
2279         */
2280        "beforeappend" : true,
2281        /**
2282         * @event beforeremove
2283         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2284         * @param {Tree} tree The owner tree
2285         * @param {Node} parent The parent node
2286         * @param {Node} node The child node to be removed
2287         */
2288        "beforeremove" : true,
2289        /**
2290         * @event beforemove
2291         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2292         * @param {Tree} tree The owner tree
2293         * @param {Node} node The node being moved
2294         * @param {Node} oldParent The parent of the node
2295         * @param {Node} newParent The new parent the node is moving to
2296         * @param {Number} index The index it is being moved to
2297         */
2298        "beforemove" : true,
2299        /**
2300         * @event beforeinsert
2301         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2302         * @param {Tree} tree The owner tree
2303         * @param {Node} parent The parent node
2304         * @param {Node} node The child node to be inserted
2305         * @param {Node} refNode The child node the node is being inserted before
2306         */
2307        "beforeinsert" : true
2308    });
2309
2310     Roo.data.Tree.superclass.constructor.call(this);
2311 };
2312
2313 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2314     pathSeparator: "/",
2315
2316     proxyNodeEvent : function(){
2317         return this.fireEvent.apply(this, arguments);
2318     },
2319
2320     /**
2321      * Returns the root node for this tree.
2322      * @return {Node}
2323      */
2324     getRootNode : function(){
2325         return this.root;
2326     },
2327
2328     /**
2329      * Sets the root node for this tree.
2330      * @param {Node} node
2331      * @return {Node}
2332      */
2333     setRootNode : function(node){
2334         this.root = node;
2335         node.ownerTree = this;
2336         node.isRoot = true;
2337         this.registerNode(node);
2338         return node;
2339     },
2340
2341     /**
2342      * Gets a node in this tree by its id.
2343      * @param {String} id
2344      * @return {Node}
2345      */
2346     getNodeById : function(id){
2347         return this.nodeHash[id];
2348     },
2349
2350     registerNode : function(node){
2351         this.nodeHash[node.id] = node;
2352     },
2353
2354     unregisterNode : function(node){
2355         delete this.nodeHash[node.id];
2356     },
2357
2358     toString : function(){
2359         return "[Tree"+(this.id?" "+this.id:"")+"]";
2360     }
2361 });
2362
2363 /**
2364  * @class Roo.data.Node
2365  * @extends Roo.util.Observable
2366  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2367  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2368  * @constructor
2369  * @param {Object} attributes The attributes/config for the node
2370  */
2371 Roo.data.Node = function(attributes){
2372     /**
2373      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2374      * @type {Object}
2375      */
2376     this.attributes = attributes || {};
2377     this.leaf = this.attributes.leaf;
2378     /**
2379      * The node id. @type String
2380      */
2381     this.id = this.attributes.id;
2382     if(!this.id){
2383         this.id = Roo.id(null, "ynode-");
2384         this.attributes.id = this.id;
2385     }
2386      
2387     
2388     /**
2389      * All child nodes of this node. @type Array
2390      */
2391     this.childNodes = [];
2392     if(!this.childNodes.indexOf){ // indexOf is a must
2393         this.childNodes.indexOf = function(o){
2394             for(var i = 0, len = this.length; i < len; i++){
2395                 if(this[i] == o) {
2396                     return i;
2397                 }
2398             }
2399             return -1;
2400         };
2401     }
2402     /**
2403      * The parent node for this node. @type Node
2404      */
2405     this.parentNode = null;
2406     /**
2407      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2408      */
2409     this.firstChild = null;
2410     /**
2411      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2412      */
2413     this.lastChild = null;
2414     /**
2415      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2416      */
2417     this.previousSibling = null;
2418     /**
2419      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2420      */
2421     this.nextSibling = null;
2422
2423     this.addEvents({
2424        /**
2425         * @event append
2426         * Fires when a new child node is appended
2427         * @param {Tree} tree The owner tree
2428         * @param {Node} this This node
2429         * @param {Node} node The newly appended node
2430         * @param {Number} index The index of the newly appended node
2431         */
2432        "append" : true,
2433        /**
2434         * @event remove
2435         * Fires when a child node is removed
2436         * @param {Tree} tree The owner tree
2437         * @param {Node} this This node
2438         * @param {Node} node The removed node
2439         */
2440        "remove" : true,
2441        /**
2442         * @event move
2443         * Fires when this node is moved to a new location in the tree
2444         * @param {Tree} tree The owner tree
2445         * @param {Node} this This node
2446         * @param {Node} oldParent The old parent of this node
2447         * @param {Node} newParent The new parent of this node
2448         * @param {Number} index The index it was moved to
2449         */
2450        "move" : true,
2451        /**
2452         * @event insert
2453         * Fires when a new child node is inserted.
2454         * @param {Tree} tree The owner tree
2455         * @param {Node} this This node
2456         * @param {Node} node The child node inserted
2457         * @param {Node} refNode The child node the node was inserted before
2458         */
2459        "insert" : true,
2460        /**
2461         * @event beforeappend
2462         * Fires before a new child is appended, return false to cancel the append.
2463         * @param {Tree} tree The owner tree
2464         * @param {Node} this This node
2465         * @param {Node} node The child node to be appended
2466         */
2467        "beforeappend" : true,
2468        /**
2469         * @event beforeremove
2470         * Fires before a child is removed, return false to cancel the remove.
2471         * @param {Tree} tree The owner tree
2472         * @param {Node} this This node
2473         * @param {Node} node The child node to be removed
2474         */
2475        "beforeremove" : true,
2476        /**
2477         * @event beforemove
2478         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2479         * @param {Tree} tree The owner tree
2480         * @param {Node} this This node
2481         * @param {Node} oldParent The parent of this node
2482         * @param {Node} newParent The new parent this node is moving to
2483         * @param {Number} index The index it is being moved to
2484         */
2485        "beforemove" : true,
2486        /**
2487         * @event beforeinsert
2488         * Fires before a new child is inserted, return false to cancel the insert.
2489         * @param {Tree} tree The owner tree
2490         * @param {Node} this This node
2491         * @param {Node} node The child node to be inserted
2492         * @param {Node} refNode The child node the node is being inserted before
2493         */
2494        "beforeinsert" : true
2495    });
2496     this.listeners = this.attributes.listeners;
2497     Roo.data.Node.superclass.constructor.call(this);
2498 };
2499
2500 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2501     fireEvent : function(evtName){
2502         // first do standard event for this node
2503         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2504             return false;
2505         }
2506         // then bubble it up to the tree if the event wasn't cancelled
2507         var ot = this.getOwnerTree();
2508         if(ot){
2509             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2510                 return false;
2511             }
2512         }
2513         return true;
2514     },
2515
2516     /**
2517      * Returns true if this node is a leaf
2518      * @return {Boolean}
2519      */
2520     isLeaf : function(){
2521         return this.leaf === true;
2522     },
2523
2524     // private
2525     setFirstChild : function(node){
2526         this.firstChild = node;
2527     },
2528
2529     //private
2530     setLastChild : function(node){
2531         this.lastChild = node;
2532     },
2533
2534
2535     /**
2536      * Returns true if this node is the last child of its parent
2537      * @return {Boolean}
2538      */
2539     isLast : function(){
2540        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2541     },
2542
2543     /**
2544      * Returns true if this node is the first child of its parent
2545      * @return {Boolean}
2546      */
2547     isFirst : function(){
2548        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2549     },
2550
2551     hasChildNodes : function(){
2552         return !this.isLeaf() && this.childNodes.length > 0;
2553     },
2554
2555     /**
2556      * Insert node(s) as the last child node of this node.
2557      * @param {Node/Array} node The node or Array of nodes to append
2558      * @return {Node} The appended node if single append, or null if an array was passed
2559      */
2560     appendChild : function(node){
2561         var multi = false;
2562         if(node instanceof Array){
2563             multi = node;
2564         }else if(arguments.length > 1){
2565             multi = arguments;
2566         }
2567         
2568         // if passed an array or multiple args do them one by one
2569         if(multi){
2570             for(var i = 0, len = multi.length; i < len; i++) {
2571                 this.appendChild(multi[i]);
2572             }
2573         }else{
2574             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2575                 return false;
2576             }
2577             var index = this.childNodes.length;
2578             var oldParent = node.parentNode;
2579             // it's a move, make sure we move it cleanly
2580             if(oldParent){
2581                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2582                     return false;
2583                 }
2584                 oldParent.removeChild(node);
2585             }
2586             
2587             index = this.childNodes.length;
2588             if(index == 0){
2589                 this.setFirstChild(node);
2590             }
2591             this.childNodes.push(node);
2592             node.parentNode = this;
2593             var ps = this.childNodes[index-1];
2594             if(ps){
2595                 node.previousSibling = ps;
2596                 ps.nextSibling = node;
2597             }else{
2598                 node.previousSibling = null;
2599             }
2600             node.nextSibling = null;
2601             this.setLastChild(node);
2602             node.setOwnerTree(this.getOwnerTree());
2603             this.fireEvent("append", this.ownerTree, this, node, index);
2604             if(this.ownerTree) {
2605                 this.ownerTree.fireEvent("appendnode", this, node, index);
2606             }
2607             if(oldParent){
2608                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2609             }
2610             return node;
2611         }
2612     },
2613
2614     /**
2615      * Removes a child node from this node.
2616      * @param {Node} node The node to remove
2617      * @return {Node} The removed node
2618      */
2619     removeChild : function(node){
2620         var index = this.childNodes.indexOf(node);
2621         if(index == -1){
2622             return false;
2623         }
2624         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2625             return false;
2626         }
2627
2628         // remove it from childNodes collection
2629         this.childNodes.splice(index, 1);
2630
2631         // update siblings
2632         if(node.previousSibling){
2633             node.previousSibling.nextSibling = node.nextSibling;
2634         }
2635         if(node.nextSibling){
2636             node.nextSibling.previousSibling = node.previousSibling;
2637         }
2638
2639         // update child refs
2640         if(this.firstChild == node){
2641             this.setFirstChild(node.nextSibling);
2642         }
2643         if(this.lastChild == node){
2644             this.setLastChild(node.previousSibling);
2645         }
2646
2647         node.setOwnerTree(null);
2648         // clear any references from the node
2649         node.parentNode = null;
2650         node.previousSibling = null;
2651         node.nextSibling = null;
2652         this.fireEvent("remove", this.ownerTree, this, node);
2653         return node;
2654     },
2655
2656     /**
2657      * Inserts the first node before the second node in this nodes childNodes collection.
2658      * @param {Node} node The node to insert
2659      * @param {Node} refNode The node to insert before (if null the node is appended)
2660      * @return {Node} The inserted node
2661      */
2662     insertBefore : function(node, refNode){
2663         if(!refNode){ // like standard Dom, refNode can be null for append
2664             return this.appendChild(node);
2665         }
2666         // nothing to do
2667         if(node == refNode){
2668             return false;
2669         }
2670
2671         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2672             return false;
2673         }
2674         var index = this.childNodes.indexOf(refNode);
2675         var oldParent = node.parentNode;
2676         var refIndex = index;
2677
2678         // when moving internally, indexes will change after remove
2679         if(oldParent == this && this.childNodes.indexOf(node) < index){
2680             refIndex--;
2681         }
2682
2683         // it's a move, make sure we move it cleanly
2684         if(oldParent){
2685             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2686                 return false;
2687             }
2688             oldParent.removeChild(node);
2689         }
2690         if(refIndex == 0){
2691             this.setFirstChild(node);
2692         }
2693         this.childNodes.splice(refIndex, 0, node);
2694         node.parentNode = this;
2695         var ps = this.childNodes[refIndex-1];
2696         if(ps){
2697             node.previousSibling = ps;
2698             ps.nextSibling = node;
2699         }else{
2700             node.previousSibling = null;
2701         }
2702         node.nextSibling = refNode;
2703         refNode.previousSibling = node;
2704         node.setOwnerTree(this.getOwnerTree());
2705         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2706         if(oldParent){
2707             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2708         }
2709         return node;
2710     },
2711
2712     /**
2713      * Returns the child node at the specified index.
2714      * @param {Number} index
2715      * @return {Node}
2716      */
2717     item : function(index){
2718         return this.childNodes[index];
2719     },
2720
2721     /**
2722      * Replaces one child node in this node with another.
2723      * @param {Node} newChild The replacement node
2724      * @param {Node} oldChild The node to replace
2725      * @return {Node} The replaced node
2726      */
2727     replaceChild : function(newChild, oldChild){
2728         this.insertBefore(newChild, oldChild);
2729         this.removeChild(oldChild);
2730         return oldChild;
2731     },
2732
2733     /**
2734      * Returns the index of a child node
2735      * @param {Node} node
2736      * @return {Number} The index of the node or -1 if it was not found
2737      */
2738     indexOf : function(child){
2739         return this.childNodes.indexOf(child);
2740     },
2741
2742     /**
2743      * Returns the tree this node is in.
2744      * @return {Tree}
2745      */
2746     getOwnerTree : function(){
2747         // if it doesn't have one, look for one
2748         if(!this.ownerTree){
2749             var p = this;
2750             while(p){
2751                 if(p.ownerTree){
2752                     this.ownerTree = p.ownerTree;
2753                     break;
2754                 }
2755                 p = p.parentNode;
2756             }
2757         }
2758         return this.ownerTree;
2759     },
2760
2761     /**
2762      * Returns depth of this node (the root node has a depth of 0)
2763      * @return {Number}
2764      */
2765     getDepth : function(){
2766         var depth = 0;
2767         var p = this;
2768         while(p.parentNode){
2769             ++depth;
2770             p = p.parentNode;
2771         }
2772         return depth;
2773     },
2774
2775     // private
2776     setOwnerTree : function(tree){
2777         // if it's move, we need to update everyone
2778         if(tree != this.ownerTree){
2779             if(this.ownerTree){
2780                 this.ownerTree.unregisterNode(this);
2781             }
2782             this.ownerTree = tree;
2783             var cs = this.childNodes;
2784             for(var i = 0, len = cs.length; i < len; i++) {
2785                 cs[i].setOwnerTree(tree);
2786             }
2787             if(tree){
2788                 tree.registerNode(this);
2789             }
2790         }
2791     },
2792
2793     /**
2794      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2795      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2796      * @return {String} The path
2797      */
2798     getPath : function(attr){
2799         attr = attr || "id";
2800         var p = this.parentNode;
2801         var b = [this.attributes[attr]];
2802         while(p){
2803             b.unshift(p.attributes[attr]);
2804             p = p.parentNode;
2805         }
2806         var sep = this.getOwnerTree().pathSeparator;
2807         return sep + b.join(sep);
2808     },
2809
2810     /**
2811      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2812      * function call will be the scope provided or the current node. The arguments to the function
2813      * will be the args provided or the current node. If the function returns false at any point,
2814      * the bubble is stopped.
2815      * @param {Function} fn The function to call
2816      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2817      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2818      */
2819     bubble : function(fn, scope, args){
2820         var p = this;
2821         while(p){
2822             if(fn.call(scope || p, args || p) === false){
2823                 break;
2824             }
2825             p = p.parentNode;
2826         }
2827     },
2828
2829     /**
2830      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2831      * function call will be the scope provided or the current node. The arguments to the function
2832      * will be the args provided or the current node. If the function returns false at any point,
2833      * the cascade is stopped on that branch.
2834      * @param {Function} fn The function to call
2835      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2836      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2837      */
2838     cascade : function(fn, scope, args){
2839         if(fn.call(scope || this, args || this) !== false){
2840             var cs = this.childNodes;
2841             for(var i = 0, len = cs.length; i < len; i++) {
2842                 cs[i].cascade(fn, scope, args);
2843             }
2844         }
2845     },
2846
2847     /**
2848      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2849      * function call will be the scope provided or the current node. The arguments to the function
2850      * will be the args provided or the current node. If the function returns false at any point,
2851      * the iteration stops.
2852      * @param {Function} fn The function to call
2853      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2854      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2855      */
2856     eachChild : function(fn, scope, args){
2857         var cs = this.childNodes;
2858         for(var i = 0, len = cs.length; i < len; i++) {
2859                 if(fn.call(scope || this, args || cs[i]) === false){
2860                     break;
2861                 }
2862         }
2863     },
2864
2865     /**
2866      * Finds the first child that has the attribute with the specified value.
2867      * @param {String} attribute The attribute name
2868      * @param {Mixed} value The value to search for
2869      * @return {Node} The found child or null if none was found
2870      */
2871     findChild : function(attribute, value){
2872         var cs = this.childNodes;
2873         for(var i = 0, len = cs.length; i < len; i++) {
2874                 if(cs[i].attributes[attribute] == value){
2875                     return cs[i];
2876                 }
2877         }
2878         return null;
2879     },
2880
2881     /**
2882      * Finds the first child by a custom function. The child matches if the function passed
2883      * returns true.
2884      * @param {Function} fn
2885      * @param {Object} scope (optional)
2886      * @return {Node} The found child or null if none was found
2887      */
2888     findChildBy : function(fn, scope){
2889         var cs = this.childNodes;
2890         for(var i = 0, len = cs.length; i < len; i++) {
2891                 if(fn.call(scope||cs[i], cs[i]) === true){
2892                     return cs[i];
2893                 }
2894         }
2895         return null;
2896     },
2897
2898     /**
2899      * Sorts this nodes children using the supplied sort function
2900      * @param {Function} fn
2901      * @param {Object} scope (optional)
2902      */
2903     sort : function(fn, scope){
2904         var cs = this.childNodes;
2905         var len = cs.length;
2906         if(len > 0){
2907             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2908             cs.sort(sortFn);
2909             for(var i = 0; i < len; i++){
2910                 var n = cs[i];
2911                 n.previousSibling = cs[i-1];
2912                 n.nextSibling = cs[i+1];
2913                 if(i == 0){
2914                     this.setFirstChild(n);
2915                 }
2916                 if(i == len-1){
2917                     this.setLastChild(n);
2918                 }
2919             }
2920         }
2921     },
2922
2923     /**
2924      * Returns true if this node is an ancestor (at any point) of the passed node.
2925      * @param {Node} node
2926      * @return {Boolean}
2927      */
2928     contains : function(node){
2929         return node.isAncestor(this);
2930     },
2931
2932     /**
2933      * Returns true if the passed node is an ancestor (at any point) of this node.
2934      * @param {Node} node
2935      * @return {Boolean}
2936      */
2937     isAncestor : function(node){
2938         var p = this.parentNode;
2939         while(p){
2940             if(p == node){
2941                 return true;
2942             }
2943             p = p.parentNode;
2944         }
2945         return false;
2946     },
2947
2948     toString : function(){
2949         return "[Node"+(this.id?" "+this.id:"")+"]";
2950     }
2951 });/*
2952  * Based on:
2953  * Ext JS Library 1.1.1
2954  * Copyright(c) 2006-2007, Ext JS, LLC.
2955  *
2956  * Originally Released Under LGPL - original licence link has changed is not relivant.
2957  *
2958  * Fork - LGPL
2959  * <script type="text/javascript">
2960  */
2961  (function(){ 
2962 /**
2963  * @class Roo.Layer
2964  * @extends Roo.Element
2965  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2966  * automatic maintaining of shadow/shim positions.
2967  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2968  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2969  * you can pass a string with a CSS class name. False turns off the shadow.
2970  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2971  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2972  * @cfg {String} cls CSS class to add to the element
2973  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2974  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2975  * @constructor
2976  * @param {Object} config An object with config options.
2977  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2978  */
2979
2980 Roo.Layer = function(config, existingEl){
2981     config = config || {};
2982     var dh = Roo.DomHelper;
2983     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2984     if(existingEl){
2985         this.dom = Roo.getDom(existingEl);
2986     }
2987     if(!this.dom){
2988         var o = config.dh || {tag: "div", cls: "x-layer"};
2989         this.dom = dh.append(pel, o);
2990     }
2991     if(config.cls){
2992         this.addClass(config.cls);
2993     }
2994     this.constrain = config.constrain !== false;
2995     this.visibilityMode = Roo.Element.VISIBILITY;
2996     if(config.id){
2997         this.id = this.dom.id = config.id;
2998     }else{
2999         this.id = Roo.id(this.dom);
3000     }
3001     this.zindex = config.zindex || this.getZIndex();
3002     this.position("absolute", this.zindex);
3003     if(config.shadow){
3004         this.shadowOffset = config.shadowOffset || 4;
3005         this.shadow = new Roo.Shadow({
3006             offset : this.shadowOffset,
3007             mode : config.shadow
3008         });
3009     }else{
3010         this.shadowOffset = 0;
3011     }
3012     this.useShim = config.shim !== false && Roo.useShims;
3013     this.useDisplay = config.useDisplay;
3014     this.hide();
3015 };
3016
3017 var supr = Roo.Element.prototype;
3018
3019 // shims are shared among layer to keep from having 100 iframes
3020 var shims = [];
3021
3022 Roo.extend(Roo.Layer, Roo.Element, {
3023
3024     getZIndex : function(){
3025         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3026     },
3027
3028     getShim : function(){
3029         if(!this.useShim){
3030             return null;
3031         }
3032         if(this.shim){
3033             return this.shim;
3034         }
3035         var shim = shims.shift();
3036         if(!shim){
3037             shim = this.createShim();
3038             shim.enableDisplayMode('block');
3039             shim.dom.style.display = 'none';
3040             shim.dom.style.visibility = 'visible';
3041         }
3042         var pn = this.dom.parentNode;
3043         if(shim.dom.parentNode != pn){
3044             pn.insertBefore(shim.dom, this.dom);
3045         }
3046         shim.setStyle('z-index', this.getZIndex()-2);
3047         this.shim = shim;
3048         return shim;
3049     },
3050
3051     hideShim : function(){
3052         if(this.shim){
3053             this.shim.setDisplayed(false);
3054             shims.push(this.shim);
3055             delete this.shim;
3056         }
3057     },
3058
3059     disableShadow : function(){
3060         if(this.shadow){
3061             this.shadowDisabled = true;
3062             this.shadow.hide();
3063             this.lastShadowOffset = this.shadowOffset;
3064             this.shadowOffset = 0;
3065         }
3066     },
3067
3068     enableShadow : function(show){
3069         if(this.shadow){
3070             this.shadowDisabled = false;
3071             this.shadowOffset = this.lastShadowOffset;
3072             delete this.lastShadowOffset;
3073             if(show){
3074                 this.sync(true);
3075             }
3076         }
3077     },
3078
3079     // private
3080     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3081     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3082     sync : function(doShow){
3083         var sw = this.shadow;
3084         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3085             var sh = this.getShim();
3086
3087             var w = this.getWidth(),
3088                 h = this.getHeight();
3089
3090             var l = this.getLeft(true),
3091                 t = this.getTop(true);
3092
3093             if(sw && !this.shadowDisabled){
3094                 if(doShow && !sw.isVisible()){
3095                     sw.show(this);
3096                 }else{
3097                     sw.realign(l, t, w, h);
3098                 }
3099                 if(sh){
3100                     if(doShow){
3101                        sh.show();
3102                     }
3103                     // fit the shim behind the shadow, so it is shimmed too
3104                     var a = sw.adjusts, s = sh.dom.style;
3105                     s.left = (Math.min(l, l+a.l))+"px";
3106                     s.top = (Math.min(t, t+a.t))+"px";
3107                     s.width = (w+a.w)+"px";
3108                     s.height = (h+a.h)+"px";
3109                 }
3110             }else if(sh){
3111                 if(doShow){
3112                    sh.show();
3113                 }
3114                 sh.setSize(w, h);
3115                 sh.setLeftTop(l, t);
3116             }
3117             
3118         }
3119     },
3120
3121     // private
3122     destroy : function(){
3123         this.hideShim();
3124         if(this.shadow){
3125             this.shadow.hide();
3126         }
3127         this.removeAllListeners();
3128         var pn = this.dom.parentNode;
3129         if(pn){
3130             pn.removeChild(this.dom);
3131         }
3132         Roo.Element.uncache(this.id);
3133     },
3134
3135     remove : function(){
3136         this.destroy();
3137     },
3138
3139     // private
3140     beginUpdate : function(){
3141         this.updating = true;
3142     },
3143
3144     // private
3145     endUpdate : function(){
3146         this.updating = false;
3147         this.sync(true);
3148     },
3149
3150     // private
3151     hideUnders : function(negOffset){
3152         if(this.shadow){
3153             this.shadow.hide();
3154         }
3155         this.hideShim();
3156     },
3157
3158     // private
3159     constrainXY : function(){
3160         if(this.constrain){
3161             var vw = Roo.lib.Dom.getViewWidth(),
3162                 vh = Roo.lib.Dom.getViewHeight();
3163             var s = Roo.get(document).getScroll();
3164
3165             var xy = this.getXY();
3166             var x = xy[0], y = xy[1];   
3167             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3168             // only move it if it needs it
3169             var moved = false;
3170             // first validate right/bottom
3171             if((x + w) > vw+s.left){
3172                 x = vw - w - this.shadowOffset;
3173                 moved = true;
3174             }
3175             if((y + h) > vh+s.top){
3176                 y = vh - h - this.shadowOffset;
3177                 moved = true;
3178             }
3179             // then make sure top/left isn't negative
3180             if(x < s.left){
3181                 x = s.left;
3182                 moved = true;
3183             }
3184             if(y < s.top){
3185                 y = s.top;
3186                 moved = true;
3187             }
3188             if(moved){
3189                 if(this.avoidY){
3190                     var ay = this.avoidY;
3191                     if(y <= ay && (y+h) >= ay){
3192                         y = ay-h-5;   
3193                     }
3194                 }
3195                 xy = [x, y];
3196                 this.storeXY(xy);
3197                 supr.setXY.call(this, xy);
3198                 this.sync();
3199             }
3200         }
3201     },
3202
3203     isVisible : function(){
3204         return this.visible;    
3205     },
3206
3207     // private
3208     showAction : function(){
3209         this.visible = true; // track visibility to prevent getStyle calls
3210         if(this.useDisplay === true){
3211             this.setDisplayed("");
3212         }else if(this.lastXY){
3213             supr.setXY.call(this, this.lastXY);
3214         }else if(this.lastLT){
3215             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3216         }
3217     },
3218
3219     // private
3220     hideAction : function(){
3221         this.visible = false;
3222         if(this.useDisplay === true){
3223             this.setDisplayed(false);
3224         }else{
3225             this.setLeftTop(-10000,-10000);
3226         }
3227     },
3228
3229     // overridden Element method
3230     setVisible : function(v, a, d, c, e){
3231         if(v){
3232             this.showAction();
3233         }
3234         if(a && v){
3235             var cb = function(){
3236                 this.sync(true);
3237                 if(c){
3238                     c();
3239                 }
3240             }.createDelegate(this);
3241             supr.setVisible.call(this, true, true, d, cb, e);
3242         }else{
3243             if(!v){
3244                 this.hideUnders(true);
3245             }
3246             var cb = c;
3247             if(a){
3248                 cb = function(){
3249                     this.hideAction();
3250                     if(c){
3251                         c();
3252                     }
3253                 }.createDelegate(this);
3254             }
3255             supr.setVisible.call(this, v, a, d, cb, e);
3256             if(v){
3257                 this.sync(true);
3258             }else if(!a){
3259                 this.hideAction();
3260             }
3261         }
3262     },
3263
3264     storeXY : function(xy){
3265         delete this.lastLT;
3266         this.lastXY = xy;
3267     },
3268
3269     storeLeftTop : function(left, top){
3270         delete this.lastXY;
3271         this.lastLT = [left, top];
3272     },
3273
3274     // private
3275     beforeFx : function(){
3276         this.beforeAction();
3277         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3278     },
3279
3280     // private
3281     afterFx : function(){
3282         Roo.Layer.superclass.afterFx.apply(this, arguments);
3283         this.sync(this.isVisible());
3284     },
3285
3286     // private
3287     beforeAction : function(){
3288         if(!this.updating && this.shadow){
3289             this.shadow.hide();
3290         }
3291     },
3292
3293     // overridden Element method
3294     setLeft : function(left){
3295         this.storeLeftTop(left, this.getTop(true));
3296         supr.setLeft.apply(this, arguments);
3297         this.sync();
3298     },
3299
3300     setTop : function(top){
3301         this.storeLeftTop(this.getLeft(true), top);
3302         supr.setTop.apply(this, arguments);
3303         this.sync();
3304     },
3305
3306     setLeftTop : function(left, top){
3307         this.storeLeftTop(left, top);
3308         supr.setLeftTop.apply(this, arguments);
3309         this.sync();
3310     },
3311
3312     setXY : function(xy, a, d, c, e){
3313         this.fixDisplay();
3314         this.beforeAction();
3315         this.storeXY(xy);
3316         var cb = this.createCB(c);
3317         supr.setXY.call(this, xy, a, d, cb, e);
3318         if(!a){
3319             cb();
3320         }
3321     },
3322
3323     // private
3324     createCB : function(c){
3325         var el = this;
3326         return function(){
3327             el.constrainXY();
3328             el.sync(true);
3329             if(c){
3330                 c();
3331             }
3332         };
3333     },
3334
3335     // overridden Element method
3336     setX : function(x, a, d, c, e){
3337         this.setXY([x, this.getY()], a, d, c, e);
3338     },
3339
3340     // overridden Element method
3341     setY : function(y, a, d, c, e){
3342         this.setXY([this.getX(), y], a, d, c, e);
3343     },
3344
3345     // overridden Element method
3346     setSize : function(w, h, a, d, c, e){
3347         this.beforeAction();
3348         var cb = this.createCB(c);
3349         supr.setSize.call(this, w, h, a, d, cb, e);
3350         if(!a){
3351             cb();
3352         }
3353     },
3354
3355     // overridden Element method
3356     setWidth : function(w, a, d, c, e){
3357         this.beforeAction();
3358         var cb = this.createCB(c);
3359         supr.setWidth.call(this, w, a, d, cb, e);
3360         if(!a){
3361             cb();
3362         }
3363     },
3364
3365     // overridden Element method
3366     setHeight : function(h, a, d, c, e){
3367         this.beforeAction();
3368         var cb = this.createCB(c);
3369         supr.setHeight.call(this, h, a, d, cb, e);
3370         if(!a){
3371             cb();
3372         }
3373     },
3374
3375     // overridden Element method
3376     setBounds : function(x, y, w, h, a, d, c, e){
3377         this.beforeAction();
3378         var cb = this.createCB(c);
3379         if(!a){
3380             this.storeXY([x, y]);
3381             supr.setXY.call(this, [x, y]);
3382             supr.setSize.call(this, w, h, a, d, cb, e);
3383             cb();
3384         }else{
3385             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3386         }
3387         return this;
3388     },
3389     
3390     /**
3391      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3392      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3393      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3394      * @param {Number} zindex The new z-index to set
3395      * @return {this} The Layer
3396      */
3397     setZIndex : function(zindex){
3398         this.zindex = zindex;
3399         this.setStyle("z-index", zindex + 2);
3400         if(this.shadow){
3401             this.shadow.setZIndex(zindex + 1);
3402         }
3403         if(this.shim){
3404             this.shim.setStyle("z-index", zindex);
3405         }
3406     }
3407 });
3408 })();/*
3409  * Based on:
3410  * Ext JS Library 1.1.1
3411  * Copyright(c) 2006-2007, Ext JS, LLC.
3412  *
3413  * Originally Released Under LGPL - original licence link has changed is not relivant.
3414  *
3415  * Fork - LGPL
3416  * <script type="text/javascript">
3417  */
3418
3419
3420 /**
3421  * @class Roo.Shadow
3422  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3423  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3424  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3425  * @constructor
3426  * Create a new Shadow
3427  * @param {Object} config The config object
3428  */
3429 Roo.Shadow = function(config){
3430     Roo.apply(this, config);
3431     if(typeof this.mode != "string"){
3432         this.mode = this.defaultMode;
3433     }
3434     var o = this.offset, a = {h: 0};
3435     var rad = Math.floor(this.offset/2);
3436     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3437         case "drop":
3438             a.w = 0;
3439             a.l = a.t = o;
3440             a.t -= 1;
3441             if(Roo.isIE){
3442                 a.l -= this.offset + rad;
3443                 a.t -= this.offset + rad;
3444                 a.w -= rad;
3445                 a.h -= rad;
3446                 a.t += 1;
3447             }
3448         break;
3449         case "sides":
3450             a.w = (o*2);
3451             a.l = -o;
3452             a.t = o-1;
3453             if(Roo.isIE){
3454                 a.l -= (this.offset - rad);
3455                 a.t -= this.offset + rad;
3456                 a.l += 1;
3457                 a.w -= (this.offset - rad)*2;
3458                 a.w -= rad + 1;
3459                 a.h -= 1;
3460             }
3461         break;
3462         case "frame":
3463             a.w = a.h = (o*2);
3464             a.l = a.t = -o;
3465             a.t += 1;
3466             a.h -= 2;
3467             if(Roo.isIE){
3468                 a.l -= (this.offset - rad);
3469                 a.t -= (this.offset - rad);
3470                 a.l += 1;
3471                 a.w -= (this.offset + rad + 1);
3472                 a.h -= (this.offset + rad);
3473                 a.h += 1;
3474             }
3475         break;
3476     };
3477
3478     this.adjusts = a;
3479 };
3480
3481 Roo.Shadow.prototype = {
3482     /**
3483      * @cfg {String} mode
3484      * The shadow display mode.  Supports the following options:<br />
3485      * sides: Shadow displays on both sides and bottom only<br />
3486      * frame: Shadow displays equally on all four sides<br />
3487      * drop: Traditional bottom-right drop shadow (default)
3488      */
3489     /**
3490      * @cfg {String} offset
3491      * The number of pixels to offset the shadow from the element (defaults to 4)
3492      */
3493     offset: 4,
3494
3495     // private
3496     defaultMode: "drop",
3497
3498     /**
3499      * Displays the shadow under the target element
3500      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3501      */
3502     show : function(target){
3503         target = Roo.get(target);
3504         if(!this.el){
3505             this.el = Roo.Shadow.Pool.pull();
3506             if(this.el.dom.nextSibling != target.dom){
3507                 this.el.insertBefore(target);
3508             }
3509         }
3510         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3511         if(Roo.isIE){
3512             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3513         }
3514         this.realign(
3515             target.getLeft(true),
3516             target.getTop(true),
3517             target.getWidth(),
3518             target.getHeight()
3519         );
3520         this.el.dom.style.display = "block";
3521     },
3522
3523     /**
3524      * Returns true if the shadow is visible, else false
3525      */
3526     isVisible : function(){
3527         return this.el ? true : false;  
3528     },
3529
3530     /**
3531      * Direct alignment when values are already available. Show must be called at least once before
3532      * calling this method to ensure it is initialized.
3533      * @param {Number} left The target element left position
3534      * @param {Number} top The target element top position
3535      * @param {Number} width The target element width
3536      * @param {Number} height The target element height
3537      */
3538     realign : function(l, t, w, h){
3539         if(!this.el){
3540             return;
3541         }
3542         var a = this.adjusts, d = this.el.dom, s = d.style;
3543         var iea = 0;
3544         s.left = (l+a.l)+"px";
3545         s.top = (t+a.t)+"px";
3546         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3547  
3548         if(s.width != sws || s.height != shs){
3549             s.width = sws;
3550             s.height = shs;
3551             if(!Roo.isIE){
3552                 var cn = d.childNodes;
3553                 var sww = Math.max(0, (sw-12))+"px";
3554                 cn[0].childNodes[1].style.width = sww;
3555                 cn[1].childNodes[1].style.width = sww;
3556                 cn[2].childNodes[1].style.width = sww;
3557                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3558             }
3559         }
3560     },
3561
3562     /**
3563      * Hides this shadow
3564      */
3565     hide : function(){
3566         if(this.el){
3567             this.el.dom.style.display = "none";
3568             Roo.Shadow.Pool.push(this.el);
3569             delete this.el;
3570         }
3571     },
3572
3573     /**
3574      * Adjust the z-index of this shadow
3575      * @param {Number} zindex The new z-index
3576      */
3577     setZIndex : function(z){
3578         this.zIndex = z;
3579         if(this.el){
3580             this.el.setStyle("z-index", z);
3581         }
3582     }
3583 };
3584
3585 // Private utility class that manages the internal Shadow cache
3586 Roo.Shadow.Pool = function(){
3587     var p = [];
3588     var markup = Roo.isIE ?
3589                  '<div class="x-ie-shadow"></div>' :
3590                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3591     return {
3592         pull : function(){
3593             var sh = p.shift();
3594             if(!sh){
3595                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3596                 sh.autoBoxAdjust = false;
3597             }
3598             return sh;
3599         },
3600
3601         push : function(sh){
3602             p.push(sh);
3603         }
3604     };
3605 }();/*
3606  * Based on:
3607  * Ext JS Library 1.1.1
3608  * Copyright(c) 2006-2007, Ext JS, LLC.
3609  *
3610  * Originally Released Under LGPL - original licence link has changed is not relivant.
3611  *
3612  * Fork - LGPL
3613  * <script type="text/javascript">
3614  */
3615
3616
3617 /**
3618  * @class Roo.SplitBar
3619  * @extends Roo.util.Observable
3620  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3621  * <br><br>
3622  * Usage:
3623  * <pre><code>
3624 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3625                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3626 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3627 split.minSize = 100;
3628 split.maxSize = 600;
3629 split.animate = true;
3630 split.on('moved', splitterMoved);
3631 </code></pre>
3632  * @constructor
3633  * Create a new SplitBar
3634  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3635  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3636  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3637  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3638                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3639                         position of the SplitBar).
3640  */
3641 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3642     
3643     /** @private */
3644     this.el = Roo.get(dragElement, true);
3645     this.el.dom.unselectable = "on";
3646     /** @private */
3647     this.resizingEl = Roo.get(resizingElement, true);
3648
3649     /**
3650      * @private
3651      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3652      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3653      * @type Number
3654      */
3655     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3656     
3657     /**
3658      * The minimum size of the resizing element. (Defaults to 0)
3659      * @type Number
3660      */
3661     this.minSize = 0;
3662     
3663     /**
3664      * The maximum size of the resizing element. (Defaults to 2000)
3665      * @type Number
3666      */
3667     this.maxSize = 2000;
3668     
3669     /**
3670      * Whether to animate the transition to the new size
3671      * @type Boolean
3672      */
3673     this.animate = false;
3674     
3675     /**
3676      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3677      * @type Boolean
3678      */
3679     this.useShim = false;
3680     
3681     /** @private */
3682     this.shim = null;
3683     
3684     if(!existingProxy){
3685         /** @private */
3686         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3687     }else{
3688         this.proxy = Roo.get(existingProxy).dom;
3689     }
3690     /** @private */
3691     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3692     
3693     /** @private */
3694     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3695     
3696     /** @private */
3697     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3698     
3699     /** @private */
3700     this.dragSpecs = {};
3701     
3702     /**
3703      * @private The adapter to use to positon and resize elements
3704      */
3705     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3706     this.adapter.init(this);
3707     
3708     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3709         /** @private */
3710         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3711         this.el.addClass("x-splitbar-h");
3712     }else{
3713         /** @private */
3714         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3715         this.el.addClass("x-splitbar-v");
3716     }
3717     
3718     this.addEvents({
3719         /**
3720          * @event resize
3721          * Fires when the splitter is moved (alias for {@link #event-moved})
3722          * @param {Roo.SplitBar} this
3723          * @param {Number} newSize the new width or height
3724          */
3725         "resize" : true,
3726         /**
3727          * @event moved
3728          * Fires when the splitter is moved
3729          * @param {Roo.SplitBar} this
3730          * @param {Number} newSize the new width or height
3731          */
3732         "moved" : true,
3733         /**
3734          * @event beforeresize
3735          * Fires before the splitter is dragged
3736          * @param {Roo.SplitBar} this
3737          */
3738         "beforeresize" : true,
3739
3740         "beforeapply" : true
3741     });
3742
3743     Roo.util.Observable.call(this);
3744 };
3745
3746 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3747     onStartProxyDrag : function(x, y){
3748         this.fireEvent("beforeresize", this);
3749         if(!this.overlay){
3750             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3751             o.unselectable();
3752             o.enableDisplayMode("block");
3753             // all splitbars share the same overlay
3754             Roo.SplitBar.prototype.overlay = o;
3755         }
3756         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3757         this.overlay.show();
3758         Roo.get(this.proxy).setDisplayed("block");
3759         var size = this.adapter.getElementSize(this);
3760         this.activeMinSize = this.getMinimumSize();;
3761         this.activeMaxSize = this.getMaximumSize();;
3762         var c1 = size - this.activeMinSize;
3763         var c2 = Math.max(this.activeMaxSize - size, 0);
3764         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3765             this.dd.resetConstraints();
3766             this.dd.setXConstraint(
3767                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3768                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3769             );
3770             this.dd.setYConstraint(0, 0);
3771         }else{
3772             this.dd.resetConstraints();
3773             this.dd.setXConstraint(0, 0);
3774             this.dd.setYConstraint(
3775                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3776                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3777             );
3778          }
3779         this.dragSpecs.startSize = size;
3780         this.dragSpecs.startPoint = [x, y];
3781         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3782     },
3783     
3784     /** 
3785      * @private Called after the drag operation by the DDProxy
3786      */
3787     onEndProxyDrag : function(e){
3788         Roo.get(this.proxy).setDisplayed(false);
3789         var endPoint = Roo.lib.Event.getXY(e);
3790         if(this.overlay){
3791             this.overlay.hide();
3792         }
3793         var newSize;
3794         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3795             newSize = this.dragSpecs.startSize + 
3796                 (this.placement == Roo.SplitBar.LEFT ?
3797                     endPoint[0] - this.dragSpecs.startPoint[0] :
3798                     this.dragSpecs.startPoint[0] - endPoint[0]
3799                 );
3800         }else{
3801             newSize = this.dragSpecs.startSize + 
3802                 (this.placement == Roo.SplitBar.TOP ?
3803                     endPoint[1] - this.dragSpecs.startPoint[1] :
3804                     this.dragSpecs.startPoint[1] - endPoint[1]
3805                 );
3806         }
3807         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3808         if(newSize != this.dragSpecs.startSize){
3809             if(this.fireEvent('beforeapply', this, newSize) !== false){
3810                 this.adapter.setElementSize(this, newSize);
3811                 this.fireEvent("moved", this, newSize);
3812                 this.fireEvent("resize", this, newSize);
3813             }
3814         }
3815     },
3816     
3817     /**
3818      * Get the adapter this SplitBar uses
3819      * @return The adapter object
3820      */
3821     getAdapter : function(){
3822         return this.adapter;
3823     },
3824     
3825     /**
3826      * Set the adapter this SplitBar uses
3827      * @param {Object} adapter A SplitBar adapter object
3828      */
3829     setAdapter : function(adapter){
3830         this.adapter = adapter;
3831         this.adapter.init(this);
3832     },
3833     
3834     /**
3835      * Gets the minimum size for the resizing element
3836      * @return {Number} The minimum size
3837      */
3838     getMinimumSize : function(){
3839         return this.minSize;
3840     },
3841     
3842     /**
3843      * Sets the minimum size for the resizing element
3844      * @param {Number} minSize The minimum size
3845      */
3846     setMinimumSize : function(minSize){
3847         this.minSize = minSize;
3848     },
3849     
3850     /**
3851      * Gets the maximum size for the resizing element
3852      * @return {Number} The maximum size
3853      */
3854     getMaximumSize : function(){
3855         return this.maxSize;
3856     },
3857     
3858     /**
3859      * Sets the maximum size for the resizing element
3860      * @param {Number} maxSize The maximum size
3861      */
3862     setMaximumSize : function(maxSize){
3863         this.maxSize = maxSize;
3864     },
3865     
3866     /**
3867      * Sets the initialize size for the resizing element
3868      * @param {Number} size The initial size
3869      */
3870     setCurrentSize : function(size){
3871         var oldAnimate = this.animate;
3872         this.animate = false;
3873         this.adapter.setElementSize(this, size);
3874         this.animate = oldAnimate;
3875     },
3876     
3877     /**
3878      * Destroy this splitbar. 
3879      * @param {Boolean} removeEl True to remove the element
3880      */
3881     destroy : function(removeEl){
3882         if(this.shim){
3883             this.shim.remove();
3884         }
3885         this.dd.unreg();
3886         this.proxy.parentNode.removeChild(this.proxy);
3887         if(removeEl){
3888             this.el.remove();
3889         }
3890     }
3891 });
3892
3893 /**
3894  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3895  */
3896 Roo.SplitBar.createProxy = function(dir){
3897     var proxy = new Roo.Element(document.createElement("div"));
3898     proxy.unselectable();
3899     var cls = 'x-splitbar-proxy';
3900     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3901     document.body.appendChild(proxy.dom);
3902     return proxy.dom;
3903 };
3904
3905 /** 
3906  * @class Roo.SplitBar.BasicLayoutAdapter
3907  * Default Adapter. It assumes the splitter and resizing element are not positioned
3908  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3909  */
3910 Roo.SplitBar.BasicLayoutAdapter = function(){
3911 };
3912
3913 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3914     // do nothing for now
3915     init : function(s){
3916     
3917     },
3918     /**
3919      * Called before drag operations to get the current size of the resizing element. 
3920      * @param {Roo.SplitBar} s The SplitBar using this adapter
3921      */
3922      getElementSize : function(s){
3923         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3924             return s.resizingEl.getWidth();
3925         }else{
3926             return s.resizingEl.getHeight();
3927         }
3928     },
3929     
3930     /**
3931      * Called after drag operations to set the size of the resizing element.
3932      * @param {Roo.SplitBar} s The SplitBar using this adapter
3933      * @param {Number} newSize The new size to set
3934      * @param {Function} onComplete A function to be invoked when resizing is complete
3935      */
3936     setElementSize : function(s, newSize, onComplete){
3937         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3938             if(!s.animate){
3939                 s.resizingEl.setWidth(newSize);
3940                 if(onComplete){
3941                     onComplete(s, newSize);
3942                 }
3943             }else{
3944                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3945             }
3946         }else{
3947             
3948             if(!s.animate){
3949                 s.resizingEl.setHeight(newSize);
3950                 if(onComplete){
3951                     onComplete(s, newSize);
3952                 }
3953             }else{
3954                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3955             }
3956         }
3957     }
3958 };
3959
3960 /** 
3961  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3962  * @extends Roo.SplitBar.BasicLayoutAdapter
3963  * Adapter that  moves the splitter element to align with the resized sizing element. 
3964  * Used with an absolute positioned SplitBar.
3965  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3966  * document.body, make sure you assign an id to the body element.
3967  */
3968 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3969     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3970     this.container = Roo.get(container);
3971 };
3972
3973 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3974     init : function(s){
3975         this.basic.init(s);
3976     },
3977     
3978     getElementSize : function(s){
3979         return this.basic.getElementSize(s);
3980     },
3981     
3982     setElementSize : function(s, newSize, onComplete){
3983         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3984     },
3985     
3986     moveSplitter : function(s){
3987         var yes = Roo.SplitBar;
3988         switch(s.placement){
3989             case yes.LEFT:
3990                 s.el.setX(s.resizingEl.getRight());
3991                 break;
3992             case yes.RIGHT:
3993                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3994                 break;
3995             case yes.TOP:
3996                 s.el.setY(s.resizingEl.getBottom());
3997                 break;
3998             case yes.BOTTOM:
3999                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
4000                 break;
4001         }
4002     }
4003 };
4004
4005 /**
4006  * Orientation constant - Create a vertical SplitBar
4007  * @static
4008  * @type Number
4009  */
4010 Roo.SplitBar.VERTICAL = 1;
4011
4012 /**
4013  * Orientation constant - Create a horizontal SplitBar
4014  * @static
4015  * @type Number
4016  */
4017 Roo.SplitBar.HORIZONTAL = 2;
4018
4019 /**
4020  * Placement constant - The resizing element is to the left of the splitter element
4021  * @static
4022  * @type Number
4023  */
4024 Roo.SplitBar.LEFT = 1;
4025
4026 /**
4027  * Placement constant - The resizing element is to the right of the splitter element
4028  * @static
4029  * @type Number
4030  */
4031 Roo.SplitBar.RIGHT = 2;
4032
4033 /**
4034  * Placement constant - The resizing element is positioned above the splitter element
4035  * @static
4036  * @type Number
4037  */
4038 Roo.SplitBar.TOP = 3;
4039
4040 /**
4041  * Placement constant - The resizing element is positioned under splitter element
4042  * @static
4043  * @type Number
4044  */
4045 Roo.SplitBar.BOTTOM = 4;
4046 /*
4047  * Based on:
4048  * Ext JS Library 1.1.1
4049  * Copyright(c) 2006-2007, Ext JS, LLC.
4050  *
4051  * Originally Released Under LGPL - original licence link has changed is not relivant.
4052  *
4053  * Fork - LGPL
4054  * <script type="text/javascript">
4055  */
4056
4057 /**
4058  * @class Roo.View
4059  * @extends Roo.util.Observable
4060  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4061  * This class also supports single and multi selection modes. <br>
4062  * Create a data model bound view:
4063  <pre><code>
4064  var store = new Roo.data.Store(...);
4065
4066  var view = new Roo.View({
4067     el : "my-element",
4068     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4069  
4070     singleSelect: true,
4071     selectedClass: "ydataview-selected",
4072     store: store
4073  });
4074
4075  // listen for node click?
4076  view.on("click", function(vw, index, node, e){
4077  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4078  });
4079
4080  // load XML data
4081  dataModel.load("foobar.xml");
4082  </code></pre>
4083  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4084  * <br><br>
4085  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4086  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4087  * 
4088  * Note: old style constructor is still suported (container, template, config)
4089  * 
4090  * @constructor
4091  * Create a new View
4092  * @param {Object} config The config object
4093  * 
4094  */
4095 Roo.View = function(config, depreciated_tpl, depreciated_config){
4096     
4097     this.parent = false;
4098     
4099     if (typeof(depreciated_tpl) == 'undefined') {
4100         // new way.. - universal constructor.
4101         Roo.apply(this, config);
4102         this.el  = Roo.get(this.el);
4103     } else {
4104         // old format..
4105         this.el  = Roo.get(config);
4106         this.tpl = depreciated_tpl;
4107         Roo.apply(this, depreciated_config);
4108     }
4109     this.wrapEl  = this.el.wrap().wrap();
4110     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4111     
4112     
4113     if(typeof(this.tpl) == "string"){
4114         this.tpl = new Roo.Template(this.tpl);
4115     } else {
4116         // support xtype ctors..
4117         this.tpl = new Roo.factory(this.tpl, Roo);
4118     }
4119     
4120     
4121     this.tpl.compile();
4122     
4123     /** @private */
4124     this.addEvents({
4125         /**
4126          * @event beforeclick
4127          * Fires before a click is processed. Returns false to cancel the default action.
4128          * @param {Roo.View} this
4129          * @param {Number} index The index of the target node
4130          * @param {HTMLElement} node The target node
4131          * @param {Roo.EventObject} e The raw event object
4132          */
4133             "beforeclick" : true,
4134         /**
4135          * @event click
4136          * Fires when a template node is clicked.
4137          * @param {Roo.View} this
4138          * @param {Number} index The index of the target node
4139          * @param {HTMLElement} node The target node
4140          * @param {Roo.EventObject} e The raw event object
4141          */
4142             "click" : true,
4143         /**
4144          * @event dblclick
4145          * Fires when a template node is double clicked.
4146          * @param {Roo.View} this
4147          * @param {Number} index The index of the target node
4148          * @param {HTMLElement} node The target node
4149          * @param {Roo.EventObject} e The raw event object
4150          */
4151             "dblclick" : true,
4152         /**
4153          * @event contextmenu
4154          * Fires when a template node is right clicked.
4155          * @param {Roo.View} this
4156          * @param {Number} index The index of the target node
4157          * @param {HTMLElement} node The target node
4158          * @param {Roo.EventObject} e The raw event object
4159          */
4160             "contextmenu" : true,
4161         /**
4162          * @event selectionchange
4163          * Fires when the selected nodes change.
4164          * @param {Roo.View} this
4165          * @param {Array} selections Array of the selected nodes
4166          */
4167             "selectionchange" : true,
4168     
4169         /**
4170          * @event beforeselect
4171          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4172          * @param {Roo.View} this
4173          * @param {HTMLElement} node The node to be selected
4174          * @param {Array} selections Array of currently selected nodes
4175          */
4176             "beforeselect" : true,
4177         /**
4178          * @event preparedata
4179          * Fires on every row to render, to allow you to change the data.
4180          * @param {Roo.View} this
4181          * @param {Object} data to be rendered (change this)
4182          */
4183           "preparedata" : true
4184           
4185           
4186         });
4187
4188
4189
4190     this.el.on({
4191         "click": this.onClick,
4192         "dblclick": this.onDblClick,
4193         "contextmenu": this.onContextMenu,
4194         scope:this
4195     });
4196
4197     this.selections = [];
4198     this.nodes = [];
4199     this.cmp = new Roo.CompositeElementLite([]);
4200     if(this.store){
4201         this.store = Roo.factory(this.store, Roo.data);
4202         this.setStore(this.store, true);
4203     }
4204     
4205     if ( this.footer && this.footer.xtype) {
4206            
4207          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4208         
4209         this.footer.dataSource = this.store;
4210         this.footer.container = fctr;
4211         this.footer = Roo.factory(this.footer, Roo);
4212         fctr.insertFirst(this.el);
4213         
4214         // this is a bit insane - as the paging toolbar seems to detach the el..
4215 //        dom.parentNode.parentNode.parentNode
4216          // they get detached?
4217     }
4218     
4219     
4220     Roo.View.superclass.constructor.call(this);
4221     
4222     
4223 };
4224
4225 Roo.extend(Roo.View, Roo.util.Observable, {
4226     
4227      /**
4228      * @cfg {Roo.data.Store} store Data store to load data from.
4229      */
4230     store : false,
4231     
4232     /**
4233      * @cfg {String|Roo.Element} el The container element.
4234      */
4235     el : '',
4236     
4237     /**
4238      * @cfg {String|Roo.Template} tpl The template used by this View 
4239      */
4240     tpl : false,
4241     /**
4242      * @cfg {String} dataName the named area of the template to use as the data area
4243      *                          Works with domtemplates roo-name="name"
4244      */
4245     dataName: false,
4246     /**
4247      * @cfg {String} selectedClass The css class to add to selected nodes
4248      */
4249     selectedClass : "x-view-selected",
4250      /**
4251      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4252      */
4253     emptyText : "",
4254     
4255     /**
4256      * @cfg {String} text to display on mask (default Loading)
4257      */
4258     mask : false,
4259     /**
4260      * @cfg {Boolean} multiSelect Allow multiple selection
4261      */
4262     multiSelect : false,
4263     /**
4264      * @cfg {Boolean} singleSelect Allow single selection
4265      */
4266     singleSelect:  false,
4267     
4268     /**
4269      * @cfg {Boolean} toggleSelect - selecting 
4270      */
4271     toggleSelect : false,
4272     
4273     /**
4274      * @cfg {Boolean} tickable - selecting 
4275      */
4276     tickable : false,
4277     
4278     /**
4279      * Returns the element this view is bound to.
4280      * @return {Roo.Element}
4281      */
4282     getEl : function(){
4283         return this.wrapEl;
4284     },
4285     
4286     
4287
4288     /**
4289      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4290      */
4291     refresh : function(){
4292         //Roo.log('refresh');
4293         var t = this.tpl;
4294         
4295         // if we are using something like 'domtemplate', then
4296         // the what gets used is:
4297         // t.applySubtemplate(NAME, data, wrapping data..)
4298         // the outer template then get' applied with
4299         //     the store 'extra data'
4300         // and the body get's added to the
4301         //      roo-name="data" node?
4302         //      <span class='roo-tpl-{name}'></span> ?????
4303         
4304         
4305         
4306         this.clearSelections();
4307         this.el.update("");
4308         var html = [];
4309         var records = this.store.getRange();
4310         if(records.length < 1) {
4311             
4312             // is this valid??  = should it render a template??
4313             
4314             this.el.update(this.emptyText);
4315             return;
4316         }
4317         var el = this.el;
4318         if (this.dataName) {
4319             this.el.update(t.apply(this.store.meta)); //????
4320             el = this.el.child('.roo-tpl-' + this.dataName);
4321         }
4322         
4323         for(var i = 0, len = records.length; i < len; i++){
4324             var data = this.prepareData(records[i].data, i, records[i]);
4325             this.fireEvent("preparedata", this, data, i, records[i]);
4326             
4327             var d = Roo.apply({}, data);
4328             
4329             if(this.tickable){
4330                 Roo.apply(d, {'roo-id' : Roo.id()});
4331                 
4332                 var _this = this;
4333             
4334                 Roo.each(this.parent.item, function(item){
4335                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4336                         return;
4337                     }
4338                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4339                 });
4340             }
4341             
4342             html[html.length] = Roo.util.Format.trim(
4343                 this.dataName ?
4344                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4345                     t.apply(d)
4346             );
4347         }
4348         
4349         
4350         
4351         el.update(html.join(""));
4352         this.nodes = el.dom.childNodes;
4353         this.updateIndexes(0);
4354     },
4355     
4356
4357     /**
4358      * Function to override to reformat the data that is sent to
4359      * the template for each node.
4360      * DEPRICATED - use the preparedata event handler.
4361      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4362      * a JSON object for an UpdateManager bound view).
4363      */
4364     prepareData : function(data, index, record)
4365     {
4366         this.fireEvent("preparedata", this, data, index, record);
4367         return data;
4368     },
4369
4370     onUpdate : function(ds, record){
4371         // Roo.log('on update');   
4372         this.clearSelections();
4373         var index = this.store.indexOf(record);
4374         var n = this.nodes[index];
4375         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4376         n.parentNode.removeChild(n);
4377         this.updateIndexes(index, index);
4378     },
4379
4380     
4381     
4382 // --------- FIXME     
4383     onAdd : function(ds, records, index)
4384     {
4385         //Roo.log(['on Add', ds, records, index] );        
4386         this.clearSelections();
4387         if(this.nodes.length == 0){
4388             this.refresh();
4389             return;
4390         }
4391         var n = this.nodes[index];
4392         for(var i = 0, len = records.length; i < len; i++){
4393             var d = this.prepareData(records[i].data, i, records[i]);
4394             if(n){
4395                 this.tpl.insertBefore(n, d);
4396             }else{
4397                 
4398                 this.tpl.append(this.el, d);
4399             }
4400         }
4401         this.updateIndexes(index);
4402     },
4403
4404     onRemove : function(ds, record, index){
4405        // Roo.log('onRemove');
4406         this.clearSelections();
4407         var el = this.dataName  ?
4408             this.el.child('.roo-tpl-' + this.dataName) :
4409             this.el; 
4410         
4411         el.dom.removeChild(this.nodes[index]);
4412         this.updateIndexes(index);
4413     },
4414
4415     /**
4416      * Refresh an individual node.
4417      * @param {Number} index
4418      */
4419     refreshNode : function(index){
4420         this.onUpdate(this.store, this.store.getAt(index));
4421     },
4422
4423     updateIndexes : function(startIndex, endIndex){
4424         var ns = this.nodes;
4425         startIndex = startIndex || 0;
4426         endIndex = endIndex || ns.length - 1;
4427         for(var i = startIndex; i <= endIndex; i++){
4428             ns[i].nodeIndex = i;
4429         }
4430     },
4431
4432     /**
4433      * Changes the data store this view uses and refresh the view.
4434      * @param {Store} store
4435      */
4436     setStore : function(store, initial){
4437         if(!initial && this.store){
4438             this.store.un("datachanged", this.refresh);
4439             this.store.un("add", this.onAdd);
4440             this.store.un("remove", this.onRemove);
4441             this.store.un("update", this.onUpdate);
4442             this.store.un("clear", this.refresh);
4443             this.store.un("beforeload", this.onBeforeLoad);
4444             this.store.un("load", this.onLoad);
4445             this.store.un("loadexception", this.onLoad);
4446         }
4447         if(store){
4448           
4449             store.on("datachanged", this.refresh, this);
4450             store.on("add", this.onAdd, this);
4451             store.on("remove", this.onRemove, this);
4452             store.on("update", this.onUpdate, this);
4453             store.on("clear", this.refresh, this);
4454             store.on("beforeload", this.onBeforeLoad, this);
4455             store.on("load", this.onLoad, this);
4456             store.on("loadexception", this.onLoad, this);
4457         }
4458         
4459         if(store){
4460             this.refresh();
4461         }
4462     },
4463     /**
4464      * onbeforeLoad - masks the loading area.
4465      *
4466      */
4467     onBeforeLoad : function(store,opts)
4468     {
4469          //Roo.log('onBeforeLoad');   
4470         if (!opts.add) {
4471             this.el.update("");
4472         }
4473         this.el.mask(this.mask ? this.mask : "Loading" ); 
4474     },
4475     onLoad : function ()
4476     {
4477         this.el.unmask();
4478     },
4479     
4480
4481     /**
4482      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4483      * @param {HTMLElement} node
4484      * @return {HTMLElement} The template node
4485      */
4486     findItemFromChild : function(node){
4487         var el = this.dataName  ?
4488             this.el.child('.roo-tpl-' + this.dataName,true) :
4489             this.el.dom; 
4490         
4491         if(!node || node.parentNode == el){
4492                     return node;
4493             }
4494             var p = node.parentNode;
4495             while(p && p != el){
4496             if(p.parentNode == el){
4497                 return p;
4498             }
4499             p = p.parentNode;
4500         }
4501             return null;
4502     },
4503
4504     /** @ignore */
4505     onClick : function(e){
4506         var item = this.findItemFromChild(e.getTarget());
4507         if(item){
4508             var index = this.indexOf(item);
4509             if(this.onItemClick(item, index, e) !== false){
4510                 this.fireEvent("click", this, index, item, e);
4511             }
4512         }else{
4513             this.clearSelections();
4514         }
4515     },
4516
4517     /** @ignore */
4518     onContextMenu : function(e){
4519         var item = this.findItemFromChild(e.getTarget());
4520         if(item){
4521             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4522         }
4523     },
4524
4525     /** @ignore */
4526     onDblClick : function(e){
4527         var item = this.findItemFromChild(e.getTarget());
4528         if(item){
4529             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4530         }
4531     },
4532
4533     onItemClick : function(item, index, e)
4534     {
4535         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4536             return false;
4537         }
4538         if (this.toggleSelect) {
4539             var m = this.isSelected(item) ? 'unselect' : 'select';
4540             //Roo.log(m);
4541             var _t = this;
4542             _t[m](item, true, false);
4543             return true;
4544         }
4545         if(this.multiSelect || this.singleSelect){
4546             if(this.multiSelect && e.shiftKey && this.lastSelection){
4547                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4548             }else{
4549                 this.select(item, this.multiSelect && e.ctrlKey);
4550                 this.lastSelection = item;
4551             }
4552             
4553             if(!this.tickable){
4554                 e.preventDefault();
4555             }
4556             
4557         }
4558         return true;
4559     },
4560
4561     /**
4562      * Get the number of selected nodes.
4563      * @return {Number}
4564      */
4565     getSelectionCount : function(){
4566         return this.selections.length;
4567     },
4568
4569     /**
4570      * Get the currently selected nodes.
4571      * @return {Array} An array of HTMLElements
4572      */
4573     getSelectedNodes : function(){
4574         return this.selections;
4575     },
4576
4577     /**
4578      * Get the indexes of the selected nodes.
4579      * @return {Array}
4580      */
4581     getSelectedIndexes : function(){
4582         var indexes = [], s = this.selections;
4583         for(var i = 0, len = s.length; i < len; i++){
4584             indexes.push(s[i].nodeIndex);
4585         }
4586         return indexes;
4587     },
4588
4589     /**
4590      * Clear all selections
4591      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4592      */
4593     clearSelections : function(suppressEvent){
4594         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4595             this.cmp.elements = this.selections;
4596             this.cmp.removeClass(this.selectedClass);
4597             this.selections = [];
4598             if(!suppressEvent){
4599                 this.fireEvent("selectionchange", this, this.selections);
4600             }
4601         }
4602     },
4603
4604     /**
4605      * Returns true if the passed node is selected
4606      * @param {HTMLElement/Number} node The node or node index
4607      * @return {Boolean}
4608      */
4609     isSelected : function(node){
4610         var s = this.selections;
4611         if(s.length < 1){
4612             return false;
4613         }
4614         node = this.getNode(node);
4615         return s.indexOf(node) !== -1;
4616     },
4617
4618     /**
4619      * Selects nodes.
4620      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4621      * @param {Boolean} keepExisting (optional) true to keep existing selections
4622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4623      */
4624     select : function(nodeInfo, keepExisting, suppressEvent){
4625         if(nodeInfo instanceof Array){
4626             if(!keepExisting){
4627                 this.clearSelections(true);
4628             }
4629             for(var i = 0, len = nodeInfo.length; i < len; i++){
4630                 this.select(nodeInfo[i], true, true);
4631             }
4632             return;
4633         } 
4634         var node = this.getNode(nodeInfo);
4635         if(!node || this.isSelected(node)){
4636             return; // already selected.
4637         }
4638         if(!keepExisting){
4639             this.clearSelections(true);
4640         }
4641         
4642         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4643             Roo.fly(node).addClass(this.selectedClass);
4644             this.selections.push(node);
4645             if(!suppressEvent){
4646                 this.fireEvent("selectionchange", this, this.selections);
4647             }
4648         }
4649         
4650         
4651     },
4652       /**
4653      * Unselects nodes.
4654      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4655      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4656      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4657      */
4658     unselect : function(nodeInfo, keepExisting, suppressEvent)
4659     {
4660         if(nodeInfo instanceof Array){
4661             Roo.each(this.selections, function(s) {
4662                 this.unselect(s, nodeInfo);
4663             }, this);
4664             return;
4665         }
4666         var node = this.getNode(nodeInfo);
4667         if(!node || !this.isSelected(node)){
4668             //Roo.log("not selected");
4669             return; // not selected.
4670         }
4671         // fireevent???
4672         var ns = [];
4673         Roo.each(this.selections, function(s) {
4674             if (s == node ) {
4675                 Roo.fly(node).removeClass(this.selectedClass);
4676
4677                 return;
4678             }
4679             ns.push(s);
4680         },this);
4681         
4682         this.selections= ns;
4683         this.fireEvent("selectionchange", this, this.selections);
4684     },
4685
4686     /**
4687      * Gets a template node.
4688      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4689      * @return {HTMLElement} The node or null if it wasn't found
4690      */
4691     getNode : function(nodeInfo){
4692         if(typeof nodeInfo == "string"){
4693             return document.getElementById(nodeInfo);
4694         }else if(typeof nodeInfo == "number"){
4695             return this.nodes[nodeInfo];
4696         }
4697         return nodeInfo;
4698     },
4699
4700     /**
4701      * Gets a range template nodes.
4702      * @param {Number} startIndex
4703      * @param {Number} endIndex
4704      * @return {Array} An array of nodes
4705      */
4706     getNodes : function(start, end){
4707         var ns = this.nodes;
4708         start = start || 0;
4709         end = typeof end == "undefined" ? ns.length - 1 : end;
4710         var nodes = [];
4711         if(start <= end){
4712             for(var i = start; i <= end; i++){
4713                 nodes.push(ns[i]);
4714             }
4715         } else{
4716             for(var i = start; i >= end; i--){
4717                 nodes.push(ns[i]);
4718             }
4719         }
4720         return nodes;
4721     },
4722
4723     /**
4724      * Finds the index of the passed node
4725      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4726      * @return {Number} The index of the node or -1
4727      */
4728     indexOf : function(node){
4729         node = this.getNode(node);
4730         if(typeof node.nodeIndex == "number"){
4731             return node.nodeIndex;
4732         }
4733         var ns = this.nodes;
4734         for(var i = 0, len = ns.length; i < len; i++){
4735             if(ns[i] == node){
4736                 return i;
4737             }
4738         }
4739         return -1;
4740     }
4741 });
4742 /*
4743  * Based on:
4744  * Ext JS Library 1.1.1
4745  * Copyright(c) 2006-2007, Ext JS, LLC.
4746  *
4747  * Originally Released Under LGPL - original licence link has changed is not relivant.
4748  *
4749  * Fork - LGPL
4750  * <script type="text/javascript">
4751  */
4752
4753 /**
4754  * @class Roo.JsonView
4755  * @extends Roo.View
4756  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4757 <pre><code>
4758 var view = new Roo.JsonView({
4759     container: "my-element",
4760     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4761     multiSelect: true, 
4762     jsonRoot: "data" 
4763 });
4764
4765 // listen for node click?
4766 view.on("click", function(vw, index, node, e){
4767     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4768 });
4769
4770 // direct load of JSON data
4771 view.load("foobar.php");
4772
4773 // Example from my blog list
4774 var tpl = new Roo.Template(
4775     '&lt;div class="entry"&gt;' +
4776     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4777     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4778     "&lt;/div&gt;&lt;hr /&gt;"
4779 );
4780
4781 var moreView = new Roo.JsonView({
4782     container :  "entry-list", 
4783     template : tpl,
4784     jsonRoot: "posts"
4785 });
4786 moreView.on("beforerender", this.sortEntries, this);
4787 moreView.load({
4788     url: "/blog/get-posts.php",
4789     params: "allposts=true",
4790     text: "Loading Blog Entries..."
4791 });
4792 </code></pre>
4793
4794 * Note: old code is supported with arguments : (container, template, config)
4795
4796
4797  * @constructor
4798  * Create a new JsonView
4799  * 
4800  * @param {Object} config The config object
4801  * 
4802  */
4803 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4804     
4805     
4806     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4807
4808     var um = this.el.getUpdateManager();
4809     um.setRenderer(this);
4810     um.on("update", this.onLoad, this);
4811     um.on("failure", this.onLoadException, this);
4812
4813     /**
4814      * @event beforerender
4815      * Fires before rendering of the downloaded JSON data.
4816      * @param {Roo.JsonView} this
4817      * @param {Object} data The JSON data loaded
4818      */
4819     /**
4820      * @event load
4821      * Fires when data is loaded.
4822      * @param {Roo.JsonView} this
4823      * @param {Object} data The JSON data loaded
4824      * @param {Object} response The raw Connect response object
4825      */
4826     /**
4827      * @event loadexception
4828      * Fires when loading fails.
4829      * @param {Roo.JsonView} this
4830      * @param {Object} response The raw Connect response object
4831      */
4832     this.addEvents({
4833         'beforerender' : true,
4834         'load' : true,
4835         'loadexception' : true
4836     });
4837 };
4838 Roo.extend(Roo.JsonView, Roo.View, {
4839     /**
4840      * @type {String} The root property in the loaded JSON object that contains the data
4841      */
4842     jsonRoot : "",
4843
4844     /**
4845      * Refreshes the view.
4846      */
4847     refresh : function(){
4848         this.clearSelections();
4849         this.el.update("");
4850         var html = [];
4851         var o = this.jsonData;
4852         if(o && o.length > 0){
4853             for(var i = 0, len = o.length; i < len; i++){
4854                 var data = this.prepareData(o[i], i, o);
4855                 html[html.length] = this.tpl.apply(data);
4856             }
4857         }else{
4858             html.push(this.emptyText);
4859         }
4860         this.el.update(html.join(""));
4861         this.nodes = this.el.dom.childNodes;
4862         this.updateIndexes(0);
4863     },
4864
4865     /**
4866      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4867      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4868      <pre><code>
4869      view.load({
4870          url: "your-url.php",
4871          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4872          callback: yourFunction,
4873          scope: yourObject, //(optional scope)
4874          discardUrl: false,
4875          nocache: false,
4876          text: "Loading...",
4877          timeout: 30,
4878          scripts: false
4879      });
4880      </code></pre>
4881      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4882      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4883      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4884      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4885      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4886      */
4887     load : function(){
4888         var um = this.el.getUpdateManager();
4889         um.update.apply(um, arguments);
4890     },
4891
4892     // note - render is a standard framework call...
4893     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4894     render : function(el, response){
4895         
4896         this.clearSelections();
4897         this.el.update("");
4898         var o;
4899         try{
4900             if (response != '') {
4901                 o = Roo.util.JSON.decode(response.responseText);
4902                 if(this.jsonRoot){
4903                     
4904                     o = o[this.jsonRoot];
4905                 }
4906             }
4907         } catch(e){
4908         }
4909         /**
4910          * The current JSON data or null
4911          */
4912         this.jsonData = o;
4913         this.beforeRender();
4914         this.refresh();
4915     },
4916
4917 /**
4918  * Get the number of records in the current JSON dataset
4919  * @return {Number}
4920  */
4921     getCount : function(){
4922         return this.jsonData ? this.jsonData.length : 0;
4923     },
4924
4925 /**
4926  * Returns the JSON object for the specified node(s)
4927  * @param {HTMLElement/Array} node The node or an array of nodes
4928  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4929  * you get the JSON object for the node
4930  */
4931     getNodeData : function(node){
4932         if(node instanceof Array){
4933             var data = [];
4934             for(var i = 0, len = node.length; i < len; i++){
4935                 data.push(this.getNodeData(node[i]));
4936             }
4937             return data;
4938         }
4939         return this.jsonData[this.indexOf(node)] || null;
4940     },
4941
4942     beforeRender : function(){
4943         this.snapshot = this.jsonData;
4944         if(this.sortInfo){
4945             this.sort.apply(this, this.sortInfo);
4946         }
4947         this.fireEvent("beforerender", this, this.jsonData);
4948     },
4949
4950     onLoad : function(el, o){
4951         this.fireEvent("load", this, this.jsonData, o);
4952     },
4953
4954     onLoadException : function(el, o){
4955         this.fireEvent("loadexception", this, o);
4956     },
4957
4958 /**
4959  * Filter the data by a specific property.
4960  * @param {String} property A property on your JSON objects
4961  * @param {String/RegExp} value Either string that the property values
4962  * should start with, or a RegExp to test against the property
4963  */
4964     filter : function(property, value){
4965         if(this.jsonData){
4966             var data = [];
4967             var ss = this.snapshot;
4968             if(typeof value == "string"){
4969                 var vlen = value.length;
4970                 if(vlen == 0){
4971                     this.clearFilter();
4972                     return;
4973                 }
4974                 value = value.toLowerCase();
4975                 for(var i = 0, len = ss.length; i < len; i++){
4976                     var o = ss[i];
4977                     if(o[property].substr(0, vlen).toLowerCase() == value){
4978                         data.push(o);
4979                     }
4980                 }
4981             } else if(value.exec){ // regex?
4982                 for(var i = 0, len = ss.length; i < len; i++){
4983                     var o = ss[i];
4984                     if(value.test(o[property])){
4985                         data.push(o);
4986                     }
4987                 }
4988             } else{
4989                 return;
4990             }
4991             this.jsonData = data;
4992             this.refresh();
4993         }
4994     },
4995
4996 /**
4997  * Filter by a function. The passed function will be called with each
4998  * object in the current dataset. If the function returns true the value is kept,
4999  * otherwise it is filtered.
5000  * @param {Function} fn
5001  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
5002  */
5003     filterBy : function(fn, scope){
5004         if(this.jsonData){
5005             var data = [];
5006             var ss = this.snapshot;
5007             for(var i = 0, len = ss.length; i < len; i++){
5008                 var o = ss[i];
5009                 if(fn.call(scope || this, o)){
5010                     data.push(o);
5011                 }
5012             }
5013             this.jsonData = data;
5014             this.refresh();
5015         }
5016     },
5017
5018 /**
5019  * Clears the current filter.
5020  */
5021     clearFilter : function(){
5022         if(this.snapshot && this.jsonData != this.snapshot){
5023             this.jsonData = this.snapshot;
5024             this.refresh();
5025         }
5026     },
5027
5028
5029 /**
5030  * Sorts the data for this view and refreshes it.
5031  * @param {String} property A property on your JSON objects to sort on
5032  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5033  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5034  */
5035     sort : function(property, dir, sortType){
5036         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5037         if(this.jsonData){
5038             var p = property;
5039             var dsc = dir && dir.toLowerCase() == "desc";
5040             var f = function(o1, o2){
5041                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5042                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5043                 ;
5044                 if(v1 < v2){
5045                     return dsc ? +1 : -1;
5046                 } else if(v1 > v2){
5047                     return dsc ? -1 : +1;
5048                 } else{
5049                     return 0;
5050                 }
5051             };
5052             this.jsonData.sort(f);
5053             this.refresh();
5054             if(this.jsonData != this.snapshot){
5055                 this.snapshot.sort(f);
5056             }
5057         }
5058     }
5059 });/*
5060  * Based on:
5061  * Ext JS Library 1.1.1
5062  * Copyright(c) 2006-2007, Ext JS, LLC.
5063  *
5064  * Originally Released Under LGPL - original licence link has changed is not relivant.
5065  *
5066  * Fork - LGPL
5067  * <script type="text/javascript">
5068  */
5069  
5070
5071 /**
5072  * @class Roo.ColorPalette
5073  * @extends Roo.Component
5074  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5075  * Here's an example of typical usage:
5076  * <pre><code>
5077 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5078 cp.render('my-div');
5079
5080 cp.on('select', function(palette, selColor){
5081     // do something with selColor
5082 });
5083 </code></pre>
5084  * @constructor
5085  * Create a new ColorPalette
5086  * @param {Object} config The config object
5087  */
5088 Roo.ColorPalette = function(config){
5089     Roo.ColorPalette.superclass.constructor.call(this, config);
5090     this.addEvents({
5091         /**
5092              * @event select
5093              * Fires when a color is selected
5094              * @param {ColorPalette} this
5095              * @param {String} color The 6-digit color hex code (without the # symbol)
5096              */
5097         select: true
5098     });
5099
5100     if(this.handler){
5101         this.on("select", this.handler, this.scope, true);
5102     }
5103 };
5104 Roo.extend(Roo.ColorPalette, Roo.Component, {
5105     /**
5106      * @cfg {String} itemCls
5107      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5108      */
5109     itemCls : "x-color-palette",
5110     /**
5111      * @cfg {String} value
5112      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5113      * the hex codes are case-sensitive.
5114      */
5115     value : null,
5116     clickEvent:'click',
5117     // private
5118     ctype: "Roo.ColorPalette",
5119
5120     /**
5121      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5122      */
5123     allowReselect : false,
5124
5125     /**
5126      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5127      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5128      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5129      * of colors with the width setting until the box is symmetrical.</p>
5130      * <p>You can override individual colors if needed:</p>
5131      * <pre><code>
5132 var cp = new Roo.ColorPalette();
5133 cp.colors[0] = "FF0000";  // change the first box to red
5134 </code></pre>
5135
5136 Or you can provide a custom array of your own for complete control:
5137 <pre><code>
5138 var cp = new Roo.ColorPalette();
5139 cp.colors = ["000000", "993300", "333300"];
5140 </code></pre>
5141      * @type Array
5142      */
5143     colors : [
5144         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5145         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5146         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5147         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5148         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5149     ],
5150
5151     // private
5152     onRender : function(container, position){
5153         var t = new Roo.MasterTemplate(
5154             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5155         );
5156         var c = this.colors;
5157         for(var i = 0, len = c.length; i < len; i++){
5158             t.add([c[i]]);
5159         }
5160         var el = document.createElement("div");
5161         el.className = this.itemCls;
5162         t.overwrite(el);
5163         container.dom.insertBefore(el, position);
5164         this.el = Roo.get(el);
5165         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5166         if(this.clickEvent != 'click'){
5167             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5168         }
5169     },
5170
5171     // private
5172     afterRender : function(){
5173         Roo.ColorPalette.superclass.afterRender.call(this);
5174         if(this.value){
5175             var s = this.value;
5176             this.value = null;
5177             this.select(s);
5178         }
5179     },
5180
5181     // private
5182     handleClick : function(e, t){
5183         e.preventDefault();
5184         if(!this.disabled){
5185             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5186             this.select(c.toUpperCase());
5187         }
5188     },
5189
5190     /**
5191      * Selects the specified color in the palette (fires the select event)
5192      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5193      */
5194     select : function(color){
5195         color = color.replace("#", "");
5196         if(color != this.value || this.allowReselect){
5197             var el = this.el;
5198             if(this.value){
5199                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5200             }
5201             el.child("a.color-"+color).addClass("x-color-palette-sel");
5202             this.value = color;
5203             this.fireEvent("select", this, color);
5204         }
5205     }
5206 });/*
5207  * Based on:
5208  * Ext JS Library 1.1.1
5209  * Copyright(c) 2006-2007, Ext JS, LLC.
5210  *
5211  * Originally Released Under LGPL - original licence link has changed is not relivant.
5212  *
5213  * Fork - LGPL
5214  * <script type="text/javascript">
5215  */
5216  
5217 /**
5218  * @class Roo.DatePicker
5219  * @extends Roo.Component
5220  * Simple date picker class.
5221  * @constructor
5222  * Create a new DatePicker
5223  * @param {Object} config The config object
5224  */
5225 Roo.DatePicker = function(config){
5226     Roo.DatePicker.superclass.constructor.call(this, config);
5227
5228     this.value = config && config.value ?
5229                  config.value.clearTime() : new Date().clearTime();
5230
5231     this.addEvents({
5232         /**
5233              * @event select
5234              * Fires when a date is selected
5235              * @param {DatePicker} this
5236              * @param {Date} date The selected date
5237              */
5238         'select': true,
5239         /**
5240              * @event monthchange
5241              * Fires when the displayed month changes 
5242              * @param {DatePicker} this
5243              * @param {Date} date The selected month
5244              */
5245         'monthchange': true
5246     });
5247
5248     if(this.handler){
5249         this.on("select", this.handler,  this.scope || this);
5250     }
5251     // build the disabledDatesRE
5252     if(!this.disabledDatesRE && this.disabledDates){
5253         var dd = this.disabledDates;
5254         var re = "(?:";
5255         for(var i = 0; i < dd.length; i++){
5256             re += dd[i];
5257             if(i != dd.length-1) {
5258                 re += "|";
5259             }
5260         }
5261         this.disabledDatesRE = new RegExp(re + ")");
5262     }
5263 };
5264
5265 Roo.extend(Roo.DatePicker, Roo.Component, {
5266     /**
5267      * @cfg {String} todayText
5268      * The text to display on the button that selects the current date (defaults to "Today")
5269      */
5270     todayText : "Today",
5271     /**
5272      * @cfg {String} okText
5273      * The text to display on the ok button
5274      */
5275     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5276     /**
5277      * @cfg {String} cancelText
5278      * The text to display on the cancel button
5279      */
5280     cancelText : "Cancel",
5281     /**
5282      * @cfg {String} todayTip
5283      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5284      */
5285     todayTip : "{0} (Spacebar)",
5286     /**
5287      * @cfg {Date} minDate
5288      * Minimum allowable date (JavaScript date object, defaults to null)
5289      */
5290     minDate : null,
5291     /**
5292      * @cfg {Date} maxDate
5293      * Maximum allowable date (JavaScript date object, defaults to null)
5294      */
5295     maxDate : null,
5296     /**
5297      * @cfg {String} minText
5298      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5299      */
5300     minText : "This date is before the minimum date",
5301     /**
5302      * @cfg {String} maxText
5303      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5304      */
5305     maxText : "This date is after the maximum date",
5306     /**
5307      * @cfg {String} format
5308      * The default date format string which can be overriden for localization support.  The format must be
5309      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5310      */
5311     format : "m/d/y",
5312     /**
5313      * @cfg {Array} disabledDays
5314      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5315      */
5316     disabledDays : null,
5317     /**
5318      * @cfg {String} disabledDaysText
5319      * The tooltip to display when the date falls on a disabled day (defaults to "")
5320      */
5321     disabledDaysText : "",
5322     /**
5323      * @cfg {RegExp} disabledDatesRE
5324      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5325      */
5326     disabledDatesRE : null,
5327     /**
5328      * @cfg {String} disabledDatesText
5329      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5330      */
5331     disabledDatesText : "",
5332     /**
5333      * @cfg {Boolean} constrainToViewport
5334      * True to constrain the date picker to the viewport (defaults to true)
5335      */
5336     constrainToViewport : true,
5337     /**
5338      * @cfg {Array} monthNames
5339      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5340      */
5341     monthNames : Date.monthNames,
5342     /**
5343      * @cfg {Array} dayNames
5344      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5345      */
5346     dayNames : Date.dayNames,
5347     /**
5348      * @cfg {String} nextText
5349      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5350      */
5351     nextText: 'Next Month (Control+Right)',
5352     /**
5353      * @cfg {String} prevText
5354      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5355      */
5356     prevText: 'Previous Month (Control+Left)',
5357     /**
5358      * @cfg {String} monthYearText
5359      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5360      */
5361     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5362     /**
5363      * @cfg {Number} startDay
5364      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5365      */
5366     startDay : 0,
5367     /**
5368      * @cfg {Bool} showClear
5369      * Show a clear button (usefull for date form elements that can be blank.)
5370      */
5371     
5372     showClear: false,
5373     
5374     /**
5375      * Sets the value of the date field
5376      * @param {Date} value The date to set
5377      */
5378     setValue : function(value){
5379         var old = this.value;
5380         
5381         if (typeof(value) == 'string') {
5382          
5383             value = Date.parseDate(value, this.format);
5384         }
5385         if (!value) {
5386             value = new Date();
5387         }
5388         
5389         this.value = value.clearTime(true);
5390         if(this.el){
5391             this.update(this.value);
5392         }
5393     },
5394
5395     /**
5396      * Gets the current selected value of the date field
5397      * @return {Date} The selected date
5398      */
5399     getValue : function(){
5400         return this.value;
5401     },
5402
5403     // private
5404     focus : function(){
5405         if(this.el){
5406             this.update(this.activeDate);
5407         }
5408     },
5409
5410     // privateval
5411     onRender : function(container, position){
5412         
5413         var m = [
5414              '<table cellspacing="0">',
5415                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
5416                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5417         var dn = this.dayNames;
5418         for(var i = 0; i < 7; i++){
5419             var d = this.startDay+i;
5420             if(d > 6){
5421                 d = d-7;
5422             }
5423             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5424         }
5425         m[m.length] = "</tr></thead><tbody><tr>";
5426         for(var i = 0; i < 42; i++) {
5427             if(i % 7 == 0 && i != 0){
5428                 m[m.length] = "</tr><tr>";
5429             }
5430             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5431         }
5432         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5433             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5434
5435         var el = document.createElement("div");
5436         el.className = "x-date-picker";
5437         el.innerHTML = m.join("");
5438
5439         container.dom.insertBefore(el, position);
5440
5441         this.el = Roo.get(el);
5442         this.eventEl = Roo.get(el.firstChild);
5443
5444         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5445             handler: this.showPrevMonth,
5446             scope: this,
5447             preventDefault:true,
5448             stopDefault:true
5449         });
5450
5451         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5452             handler: this.showNextMonth,
5453             scope: this,
5454             preventDefault:true,
5455             stopDefault:true
5456         });
5457
5458         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5459
5460         this.monthPicker = this.el.down('div.x-date-mp');
5461         this.monthPicker.enableDisplayMode('block');
5462         
5463         var kn = new Roo.KeyNav(this.eventEl, {
5464             "left" : function(e){
5465                 e.ctrlKey ?
5466                     this.showPrevMonth() :
5467                     this.update(this.activeDate.add("d", -1));
5468             },
5469
5470             "right" : function(e){
5471                 e.ctrlKey ?
5472                     this.showNextMonth() :
5473                     this.update(this.activeDate.add("d", 1));
5474             },
5475
5476             "up" : function(e){
5477                 e.ctrlKey ?
5478                     this.showNextYear() :
5479                     this.update(this.activeDate.add("d", -7));
5480             },
5481
5482             "down" : function(e){
5483                 e.ctrlKey ?
5484                     this.showPrevYear() :
5485                     this.update(this.activeDate.add("d", 7));
5486             },
5487
5488             "pageUp" : function(e){
5489                 this.showNextMonth();
5490             },
5491
5492             "pageDown" : function(e){
5493                 this.showPrevMonth();
5494             },
5495
5496             "enter" : function(e){
5497                 e.stopPropagation();
5498                 return true;
5499             },
5500
5501             scope : this
5502         });
5503
5504         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5505
5506         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5507
5508         this.el.unselectable();
5509         
5510         this.cells = this.el.select("table.x-date-inner tbody td");
5511         this.textNodes = this.el.query("table.x-date-inner tbody span");
5512
5513         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5514             text: "&#160;",
5515             tooltip: this.monthYearText
5516         });
5517
5518         this.mbtn.on('click', this.showMonthPicker, this);
5519         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5520
5521
5522         var today = (new Date()).dateFormat(this.format);
5523         
5524         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5525         if (this.showClear) {
5526             baseTb.add( new Roo.Toolbar.Fill());
5527         }
5528         baseTb.add({
5529             text: String.format(this.todayText, today),
5530             tooltip: String.format(this.todayTip, today),
5531             handler: this.selectToday,
5532             scope: this
5533         });
5534         
5535         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5536             
5537         //});
5538         if (this.showClear) {
5539             
5540             baseTb.add( new Roo.Toolbar.Fill());
5541             baseTb.add({
5542                 text: '&#160;',
5543                 cls: 'x-btn-icon x-btn-clear',
5544                 handler: function() {
5545                     //this.value = '';
5546                     this.fireEvent("select", this, '');
5547                 },
5548                 scope: this
5549             });
5550         }
5551         
5552         
5553         if(Roo.isIE){
5554             this.el.repaint();
5555         }
5556         this.update(this.value);
5557     },
5558
5559     createMonthPicker : function(){
5560         if(!this.monthPicker.dom.firstChild){
5561             var buf = ['<table border="0" cellspacing="0">'];
5562             for(var i = 0; i < 6; i++){
5563                 buf.push(
5564                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5565                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5566                     i == 0 ?
5567                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5568                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5569                 );
5570             }
5571             buf.push(
5572                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5573                     this.okText,
5574                     '</button><button type="button" class="x-date-mp-cancel">',
5575                     this.cancelText,
5576                     '</button></td></tr>',
5577                 '</table>'
5578             );
5579             this.monthPicker.update(buf.join(''));
5580             this.monthPicker.on('click', this.onMonthClick, this);
5581             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5582
5583             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5584             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5585
5586             this.mpMonths.each(function(m, a, i){
5587                 i += 1;
5588                 if((i%2) == 0){
5589                     m.dom.xmonth = 5 + Math.round(i * .5);
5590                 }else{
5591                     m.dom.xmonth = Math.round((i-1) * .5);
5592                 }
5593             });
5594         }
5595     },
5596
5597     showMonthPicker : function(){
5598         this.createMonthPicker();
5599         var size = this.el.getSize();
5600         this.monthPicker.setSize(size);
5601         this.monthPicker.child('table').setSize(size);
5602
5603         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5604         this.updateMPMonth(this.mpSelMonth);
5605         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5606         this.updateMPYear(this.mpSelYear);
5607
5608         this.monthPicker.slideIn('t', {duration:.2});
5609     },
5610
5611     updateMPYear : function(y){
5612         this.mpyear = y;
5613         var ys = this.mpYears.elements;
5614         for(var i = 1; i <= 10; i++){
5615             var td = ys[i-1], y2;
5616             if((i%2) == 0){
5617                 y2 = y + Math.round(i * .5);
5618                 td.firstChild.innerHTML = y2;
5619                 td.xyear = y2;
5620             }else{
5621                 y2 = y - (5-Math.round(i * .5));
5622                 td.firstChild.innerHTML = y2;
5623                 td.xyear = y2;
5624             }
5625             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5626         }
5627     },
5628
5629     updateMPMonth : function(sm){
5630         this.mpMonths.each(function(m, a, i){
5631             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5632         });
5633     },
5634
5635     selectMPMonth: function(m){
5636         
5637     },
5638
5639     onMonthClick : function(e, t){
5640         e.stopEvent();
5641         var el = new Roo.Element(t), pn;
5642         if(el.is('button.x-date-mp-cancel')){
5643             this.hideMonthPicker();
5644         }
5645         else if(el.is('button.x-date-mp-ok')){
5646             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5647             this.hideMonthPicker();
5648         }
5649         else if(pn = el.up('td.x-date-mp-month', 2)){
5650             this.mpMonths.removeClass('x-date-mp-sel');
5651             pn.addClass('x-date-mp-sel');
5652             this.mpSelMonth = pn.dom.xmonth;
5653         }
5654         else if(pn = el.up('td.x-date-mp-year', 2)){
5655             this.mpYears.removeClass('x-date-mp-sel');
5656             pn.addClass('x-date-mp-sel');
5657             this.mpSelYear = pn.dom.xyear;
5658         }
5659         else if(el.is('a.x-date-mp-prev')){
5660             this.updateMPYear(this.mpyear-10);
5661         }
5662         else if(el.is('a.x-date-mp-next')){
5663             this.updateMPYear(this.mpyear+10);
5664         }
5665     },
5666
5667     onMonthDblClick : function(e, t){
5668         e.stopEvent();
5669         var el = new Roo.Element(t), pn;
5670         if(pn = el.up('td.x-date-mp-month', 2)){
5671             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5672             this.hideMonthPicker();
5673         }
5674         else if(pn = el.up('td.x-date-mp-year', 2)){
5675             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5676             this.hideMonthPicker();
5677         }
5678     },
5679
5680     hideMonthPicker : function(disableAnim){
5681         if(this.monthPicker){
5682             if(disableAnim === true){
5683                 this.monthPicker.hide();
5684             }else{
5685                 this.monthPicker.slideOut('t', {duration:.2});
5686             }
5687         }
5688     },
5689
5690     // private
5691     showPrevMonth : function(e){
5692         this.update(this.activeDate.add("mo", -1));
5693     },
5694
5695     // private
5696     showNextMonth : function(e){
5697         this.update(this.activeDate.add("mo", 1));
5698     },
5699
5700     // private
5701     showPrevYear : function(){
5702         this.update(this.activeDate.add("y", -1));
5703     },
5704
5705     // private
5706     showNextYear : function(){
5707         this.update(this.activeDate.add("y", 1));
5708     },
5709
5710     // private
5711     handleMouseWheel : function(e){
5712         var delta = e.getWheelDelta();
5713         if(delta > 0){
5714             this.showPrevMonth();
5715             e.stopEvent();
5716         } else if(delta < 0){
5717             this.showNextMonth();
5718             e.stopEvent();
5719         }
5720     },
5721
5722     // private
5723     handleDateClick : function(e, t){
5724         e.stopEvent();
5725         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5726             this.setValue(new Date(t.dateValue));
5727             this.fireEvent("select", this, this.value);
5728         }
5729     },
5730
5731     // private
5732     selectToday : function(){
5733         this.setValue(new Date().clearTime());
5734         this.fireEvent("select", this, this.value);
5735     },
5736
5737     // private
5738     update : function(date)
5739     {
5740         var vd = this.activeDate;
5741         this.activeDate = date;
5742         if(vd && this.el){
5743             var t = date.getTime();
5744             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5745                 this.cells.removeClass("x-date-selected");
5746                 this.cells.each(function(c){
5747                    if(c.dom.firstChild.dateValue == t){
5748                        c.addClass("x-date-selected");
5749                        setTimeout(function(){
5750                             try{c.dom.firstChild.focus();}catch(e){}
5751                        }, 50);
5752                        return false;
5753                    }
5754                 });
5755                 return;
5756             }
5757         }
5758         
5759         var days = date.getDaysInMonth();
5760         var firstOfMonth = date.getFirstDateOfMonth();
5761         var startingPos = firstOfMonth.getDay()-this.startDay;
5762
5763         if(startingPos <= this.startDay){
5764             startingPos += 7;
5765         }
5766
5767         var pm = date.add("mo", -1);
5768         var prevStart = pm.getDaysInMonth()-startingPos;
5769
5770         var cells = this.cells.elements;
5771         var textEls = this.textNodes;
5772         days += startingPos;
5773
5774         // convert everything to numbers so it's fast
5775         var day = 86400000;
5776         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5777         var today = new Date().clearTime().getTime();
5778         var sel = date.clearTime().getTime();
5779         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5780         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5781         var ddMatch = this.disabledDatesRE;
5782         var ddText = this.disabledDatesText;
5783         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5784         var ddaysText = this.disabledDaysText;
5785         var format = this.format;
5786
5787         var setCellClass = function(cal, cell){
5788             cell.title = "";
5789             var t = d.getTime();
5790             cell.firstChild.dateValue = t;
5791             if(t == today){
5792                 cell.className += " x-date-today";
5793                 cell.title = cal.todayText;
5794             }
5795             if(t == sel){
5796                 cell.className += " x-date-selected";
5797                 setTimeout(function(){
5798                     try{cell.firstChild.focus();}catch(e){}
5799                 }, 50);
5800             }
5801             // disabling
5802             if(t < min) {
5803                 cell.className = " x-date-disabled";
5804                 cell.title = cal.minText;
5805                 return;
5806             }
5807             if(t > max) {
5808                 cell.className = " x-date-disabled";
5809                 cell.title = cal.maxText;
5810                 return;
5811             }
5812             if(ddays){
5813                 if(ddays.indexOf(d.getDay()) != -1){
5814                     cell.title = ddaysText;
5815                     cell.className = " x-date-disabled";
5816                 }
5817             }
5818             if(ddMatch && format){
5819                 var fvalue = d.dateFormat(format);
5820                 if(ddMatch.test(fvalue)){
5821                     cell.title = ddText.replace("%0", fvalue);
5822                     cell.className = " x-date-disabled";
5823                 }
5824             }
5825         };
5826
5827         var i = 0;
5828         for(; i < startingPos; i++) {
5829             textEls[i].innerHTML = (++prevStart);
5830             d.setDate(d.getDate()+1);
5831             cells[i].className = "x-date-prevday";
5832             setCellClass(this, cells[i]);
5833         }
5834         for(; i < days; i++){
5835             intDay = i - startingPos + 1;
5836             textEls[i].innerHTML = (intDay);
5837             d.setDate(d.getDate()+1);
5838             cells[i].className = "x-date-active";
5839             setCellClass(this, cells[i]);
5840         }
5841         var extraDays = 0;
5842         for(; i < 42; i++) {
5843              textEls[i].innerHTML = (++extraDays);
5844              d.setDate(d.getDate()+1);
5845              cells[i].className = "x-date-nextday";
5846              setCellClass(this, cells[i]);
5847         }
5848
5849         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5850         this.fireEvent('monthchange', this, date);
5851         
5852         if(!this.internalRender){
5853             var main = this.el.dom.firstChild;
5854             var w = main.offsetWidth;
5855             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5856             Roo.fly(main).setWidth(w);
5857             this.internalRender = true;
5858             // opera does not respect the auto grow header center column
5859             // then, after it gets a width opera refuses to recalculate
5860             // without a second pass
5861             if(Roo.isOpera && !this.secondPass){
5862                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5863                 this.secondPass = true;
5864                 this.update.defer(10, this, [date]);
5865             }
5866         }
5867         
5868         
5869     }
5870 });        /*
5871  * Based on:
5872  * Ext JS Library 1.1.1
5873  * Copyright(c) 2006-2007, Ext JS, LLC.
5874  *
5875  * Originally Released Under LGPL - original licence link has changed is not relivant.
5876  *
5877  * Fork - LGPL
5878  * <script type="text/javascript">
5879  */
5880 /**
5881  * @class Roo.TabPanel
5882  * @extends Roo.util.Observable
5883  * A lightweight tab container.
5884  * <br><br>
5885  * Usage:
5886  * <pre><code>
5887 // basic tabs 1, built from existing content
5888 var tabs = new Roo.TabPanel("tabs1");
5889 tabs.addTab("script", "View Script");
5890 tabs.addTab("markup", "View Markup");
5891 tabs.activate("script");
5892
5893 // more advanced tabs, built from javascript
5894 var jtabs = new Roo.TabPanel("jtabs");
5895 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5896
5897 // set up the UpdateManager
5898 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5899 var updater = tab2.getUpdateManager();
5900 updater.setDefaultUrl("ajax1.htm");
5901 tab2.on('activate', updater.refresh, updater, true);
5902
5903 // Use setUrl for Ajax loading
5904 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5905 tab3.setUrl("ajax2.htm", null, true);
5906
5907 // Disabled tab
5908 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5909 tab4.disable();
5910
5911 jtabs.activate("jtabs-1");
5912  * </code></pre>
5913  * @constructor
5914  * Create a new TabPanel.
5915  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5916  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5917  */
5918 Roo.TabPanel = function(container, config){
5919     /**
5920     * The container element for this TabPanel.
5921     * @type Roo.Element
5922     */
5923     this.el = Roo.get(container, true);
5924     if(config){
5925         if(typeof config == "boolean"){
5926             this.tabPosition = config ? "bottom" : "top";
5927         }else{
5928             Roo.apply(this, config);
5929         }
5930     }
5931     if(this.tabPosition == "bottom"){
5932         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5933         this.el.addClass("x-tabs-bottom");
5934     }
5935     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5936     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5937     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5938     if(Roo.isIE){
5939         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5940     }
5941     if(this.tabPosition != "bottom"){
5942         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5943          * @type Roo.Element
5944          */
5945         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5946         this.el.addClass("x-tabs-top");
5947     }
5948     this.items = [];
5949
5950     this.bodyEl.setStyle("position", "relative");
5951
5952     this.active = null;
5953     this.activateDelegate = this.activate.createDelegate(this);
5954
5955     this.addEvents({
5956         /**
5957          * @event tabchange
5958          * Fires when the active tab changes
5959          * @param {Roo.TabPanel} this
5960          * @param {Roo.TabPanelItem} activePanel The new active tab
5961          */
5962         "tabchange": true,
5963         /**
5964          * @event beforetabchange
5965          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5966          * @param {Roo.TabPanel} this
5967          * @param {Object} e Set cancel to true on this object to cancel the tab change
5968          * @param {Roo.TabPanelItem} tab The tab being changed to
5969          */
5970         "beforetabchange" : true
5971     });
5972
5973     Roo.EventManager.onWindowResize(this.onResize, this);
5974     this.cpad = this.el.getPadding("lr");
5975     this.hiddenCount = 0;
5976
5977
5978     // toolbar on the tabbar support...
5979     if (this.toolbar) {
5980         var tcfg = this.toolbar;
5981         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5982         this.toolbar = new Roo.Toolbar(tcfg);
5983         if (Roo.isSafari) {
5984             var tbl = tcfg.container.child('table', true);
5985             tbl.setAttribute('width', '100%');
5986         }
5987         
5988     }
5989    
5990
5991
5992     Roo.TabPanel.superclass.constructor.call(this);
5993 };
5994
5995 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5996     /*
5997      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5998      */
5999     tabPosition : "top",
6000     /*
6001      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
6002      */
6003     currentTabWidth : 0,
6004     /*
6005      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
6006      */
6007     minTabWidth : 40,
6008     /*
6009      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
6010      */
6011     maxTabWidth : 250,
6012     /*
6013      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6014      */
6015     preferredTabWidth : 175,
6016     /*
6017      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6018      */
6019     resizeTabs : false,
6020     /*
6021      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6022      */
6023     monitorResize : true,
6024     /*
6025      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6026      */
6027     toolbar : false,
6028
6029     /**
6030      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6031      * @param {String} id The id of the div to use <b>or create</b>
6032      * @param {String} text The text for the tab
6033      * @param {String} content (optional) Content to put in the TabPanelItem body
6034      * @param {Boolean} closable (optional) True to create a close icon on the tab
6035      * @return {Roo.TabPanelItem} The created TabPanelItem
6036      */
6037     addTab : function(id, text, content, closable){
6038         var item = new Roo.TabPanelItem(this, id, text, closable);
6039         this.addTabItem(item);
6040         if(content){
6041             item.setContent(content);
6042         }
6043         return item;
6044     },
6045
6046     /**
6047      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6048      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6049      * @return {Roo.TabPanelItem}
6050      */
6051     getTab : function(id){
6052         return this.items[id];
6053     },
6054
6055     /**
6056      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6057      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6058      */
6059     hideTab : function(id){
6060         var t = this.items[id];
6061         if(!t.isHidden()){
6062            t.setHidden(true);
6063            this.hiddenCount++;
6064            this.autoSizeTabs();
6065         }
6066     },
6067
6068     /**
6069      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6070      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6071      */
6072     unhideTab : function(id){
6073         var t = this.items[id];
6074         if(t.isHidden()){
6075            t.setHidden(false);
6076            this.hiddenCount--;
6077            this.autoSizeTabs();
6078         }
6079     },
6080
6081     /**
6082      * Adds an existing {@link Roo.TabPanelItem}.
6083      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6084      */
6085     addTabItem : function(item){
6086         this.items[item.id] = item;
6087         this.items.push(item);
6088         if(this.resizeTabs){
6089            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6090            this.autoSizeTabs();
6091         }else{
6092             item.autoSize();
6093         }
6094     },
6095
6096     /**
6097      * Removes a {@link Roo.TabPanelItem}.
6098      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6099      */
6100     removeTab : function(id){
6101         var items = this.items;
6102         var tab = items[id];
6103         if(!tab) { return; }
6104         var index = items.indexOf(tab);
6105         if(this.active == tab && items.length > 1){
6106             var newTab = this.getNextAvailable(index);
6107             if(newTab) {
6108                 newTab.activate();
6109             }
6110         }
6111         this.stripEl.dom.removeChild(tab.pnode.dom);
6112         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6113             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6114         }
6115         items.splice(index, 1);
6116         delete this.items[tab.id];
6117         tab.fireEvent("close", tab);
6118         tab.purgeListeners();
6119         this.autoSizeTabs();
6120     },
6121
6122     getNextAvailable : function(start){
6123         var items = this.items;
6124         var index = start;
6125         // look for a next tab that will slide over to
6126         // replace the one being removed
6127         while(index < items.length){
6128             var item = items[++index];
6129             if(item && !item.isHidden()){
6130                 return item;
6131             }
6132         }
6133         // if one isn't found select the previous tab (on the left)
6134         index = start;
6135         while(index >= 0){
6136             var item = items[--index];
6137             if(item && !item.isHidden()){
6138                 return item;
6139             }
6140         }
6141         return null;
6142     },
6143
6144     /**
6145      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6146      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6147      */
6148     disableTab : function(id){
6149         var tab = this.items[id];
6150         if(tab && this.active != tab){
6151             tab.disable();
6152         }
6153     },
6154
6155     /**
6156      * Enables a {@link Roo.TabPanelItem} that is disabled.
6157      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6158      */
6159     enableTab : function(id){
6160         var tab = this.items[id];
6161         tab.enable();
6162     },
6163
6164     /**
6165      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6166      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6167      * @return {Roo.TabPanelItem} The TabPanelItem.
6168      */
6169     activate : function(id){
6170         var tab = this.items[id];
6171         if(!tab){
6172             return null;
6173         }
6174         if(tab == this.active || tab.disabled){
6175             return tab;
6176         }
6177         var e = {};
6178         this.fireEvent("beforetabchange", this, e, tab);
6179         if(e.cancel !== true && !tab.disabled){
6180             if(this.active){
6181                 this.active.hide();
6182             }
6183             this.active = this.items[id];
6184             this.active.show();
6185             this.fireEvent("tabchange", this, this.active);
6186         }
6187         return tab;
6188     },
6189
6190     /**
6191      * Gets the active {@link Roo.TabPanelItem}.
6192      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6193      */
6194     getActiveTab : function(){
6195         return this.active;
6196     },
6197
6198     /**
6199      * Updates the tab body element to fit the height of the container element
6200      * for overflow scrolling
6201      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6202      */
6203     syncHeight : function(targetHeight){
6204         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6205         var bm = this.bodyEl.getMargins();
6206         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6207         this.bodyEl.setHeight(newHeight);
6208         return newHeight;
6209     },
6210
6211     onResize : function(){
6212         if(this.monitorResize){
6213             this.autoSizeTabs();
6214         }
6215     },
6216
6217     /**
6218      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6219      */
6220     beginUpdate : function(){
6221         this.updating = true;
6222     },
6223
6224     /**
6225      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6226      */
6227     endUpdate : function(){
6228         this.updating = false;
6229         this.autoSizeTabs();
6230     },
6231
6232     /**
6233      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6234      */
6235     autoSizeTabs : function(){
6236         var count = this.items.length;
6237         var vcount = count - this.hiddenCount;
6238         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6239             return;
6240         }
6241         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6242         var availWidth = Math.floor(w / vcount);
6243         var b = this.stripBody;
6244         if(b.getWidth() > w){
6245             var tabs = this.items;
6246             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6247             if(availWidth < this.minTabWidth){
6248                 /*if(!this.sleft){    // incomplete scrolling code
6249                     this.createScrollButtons();
6250                 }
6251                 this.showScroll();
6252                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6253             }
6254         }else{
6255             if(this.currentTabWidth < this.preferredTabWidth){
6256                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6257             }
6258         }
6259     },
6260
6261     /**
6262      * Returns the number of tabs in this TabPanel.
6263      * @return {Number}
6264      */
6265      getCount : function(){
6266          return this.items.length;
6267      },
6268
6269     /**
6270      * Resizes all the tabs to the passed width
6271      * @param {Number} The new width
6272      */
6273     setTabWidth : function(width){
6274         this.currentTabWidth = width;
6275         for(var i = 0, len = this.items.length; i < len; i++) {
6276                 if(!this.items[i].isHidden()) {
6277                 this.items[i].setWidth(width);
6278             }
6279         }
6280     },
6281
6282     /**
6283      * Destroys this TabPanel
6284      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6285      */
6286     destroy : function(removeEl){
6287         Roo.EventManager.removeResizeListener(this.onResize, this);
6288         for(var i = 0, len = this.items.length; i < len; i++){
6289             this.items[i].purgeListeners();
6290         }
6291         if(removeEl === true){
6292             this.el.update("");
6293             this.el.remove();
6294         }
6295     }
6296 });
6297
6298 /**
6299  * @class Roo.TabPanelItem
6300  * @extends Roo.util.Observable
6301  * Represents an individual item (tab plus body) in a TabPanel.
6302  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6303  * @param {String} id The id of this TabPanelItem
6304  * @param {String} text The text for the tab of this TabPanelItem
6305  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6306  */
6307 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6308     /**
6309      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6310      * @type Roo.TabPanel
6311      */
6312     this.tabPanel = tabPanel;
6313     /**
6314      * The id for this TabPanelItem
6315      * @type String
6316      */
6317     this.id = id;
6318     /** @private */
6319     this.disabled = false;
6320     /** @private */
6321     this.text = text;
6322     /** @private */
6323     this.loaded = false;
6324     this.closable = closable;
6325
6326     /**
6327      * The body element for this TabPanelItem.
6328      * @type Roo.Element
6329      */
6330     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6331     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6332     this.bodyEl.setStyle("display", "block");
6333     this.bodyEl.setStyle("zoom", "1");
6334     this.hideAction();
6335
6336     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6337     /** @private */
6338     this.el = Roo.get(els.el, true);
6339     this.inner = Roo.get(els.inner, true);
6340     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6341     this.pnode = Roo.get(els.el.parentNode, true);
6342     this.el.on("mousedown", this.onTabMouseDown, this);
6343     this.el.on("click", this.onTabClick, this);
6344     /** @private */
6345     if(closable){
6346         var c = Roo.get(els.close, true);
6347         c.dom.title = this.closeText;
6348         c.addClassOnOver("close-over");
6349         c.on("click", this.closeClick, this);
6350      }
6351
6352     this.addEvents({
6353          /**
6354          * @event activate
6355          * Fires when this tab becomes the active tab.
6356          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6357          * @param {Roo.TabPanelItem} this
6358          */
6359         "activate": true,
6360         /**
6361          * @event beforeclose
6362          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6363          * @param {Roo.TabPanelItem} this
6364          * @param {Object} e Set cancel to true on this object to cancel the close.
6365          */
6366         "beforeclose": true,
6367         /**
6368          * @event close
6369          * Fires when this tab is closed.
6370          * @param {Roo.TabPanelItem} this
6371          */
6372          "close": true,
6373         /**
6374          * @event deactivate
6375          * Fires when this tab is no longer the active tab.
6376          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6377          * @param {Roo.TabPanelItem} this
6378          */
6379          "deactivate" : true
6380     });
6381     this.hidden = false;
6382
6383     Roo.TabPanelItem.superclass.constructor.call(this);
6384 };
6385
6386 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6387     purgeListeners : function(){
6388        Roo.util.Observable.prototype.purgeListeners.call(this);
6389        this.el.removeAllListeners();
6390     },
6391     /**
6392      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6393      */
6394     show : function(){
6395         this.pnode.addClass("on");
6396         this.showAction();
6397         if(Roo.isOpera){
6398             this.tabPanel.stripWrap.repaint();
6399         }
6400         this.fireEvent("activate", this.tabPanel, this);
6401     },
6402
6403     /**
6404      * Returns true if this tab is the active tab.
6405      * @return {Boolean}
6406      */
6407     isActive : function(){
6408         return this.tabPanel.getActiveTab() == this;
6409     },
6410
6411     /**
6412      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6413      */
6414     hide : function(){
6415         this.pnode.removeClass("on");
6416         this.hideAction();
6417         this.fireEvent("deactivate", this.tabPanel, this);
6418     },
6419
6420     hideAction : function(){
6421         this.bodyEl.hide();
6422         this.bodyEl.setStyle("position", "absolute");
6423         this.bodyEl.setLeft("-20000px");
6424         this.bodyEl.setTop("-20000px");
6425     },
6426
6427     showAction : function(){
6428         this.bodyEl.setStyle("position", "relative");
6429         this.bodyEl.setTop("");
6430         this.bodyEl.setLeft("");
6431         this.bodyEl.show();
6432     },
6433
6434     /**
6435      * Set the tooltip for the tab.
6436      * @param {String} tooltip The tab's tooltip
6437      */
6438     setTooltip : function(text){
6439         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6440             this.textEl.dom.qtip = text;
6441             this.textEl.dom.removeAttribute('title');
6442         }else{
6443             this.textEl.dom.title = text;
6444         }
6445     },
6446
6447     onTabClick : function(e){
6448         e.preventDefault();
6449         this.tabPanel.activate(this.id);
6450     },
6451
6452     onTabMouseDown : function(e){
6453         e.preventDefault();
6454         this.tabPanel.activate(this.id);
6455     },
6456
6457     getWidth : function(){
6458         return this.inner.getWidth();
6459     },
6460
6461     setWidth : function(width){
6462         var iwidth = width - this.pnode.getPadding("lr");
6463         this.inner.setWidth(iwidth);
6464         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6465         this.pnode.setWidth(width);
6466     },
6467
6468     /**
6469      * Show or hide the tab
6470      * @param {Boolean} hidden True to hide or false to show.
6471      */
6472     setHidden : function(hidden){
6473         this.hidden = hidden;
6474         this.pnode.setStyle("display", hidden ? "none" : "");
6475     },
6476
6477     /**
6478      * Returns true if this tab is "hidden"
6479      * @return {Boolean}
6480      */
6481     isHidden : function(){
6482         return this.hidden;
6483     },
6484
6485     /**
6486      * Returns the text for this tab
6487      * @return {String}
6488      */
6489     getText : function(){
6490         return this.text;
6491     },
6492
6493     autoSize : function(){
6494         //this.el.beginMeasure();
6495         this.textEl.setWidth(1);
6496         /*
6497          *  #2804 [new] Tabs in Roojs
6498          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6499          */
6500         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6501         //this.el.endMeasure();
6502     },
6503
6504     /**
6505      * Sets the text for the tab (Note: this also sets the tooltip text)
6506      * @param {String} text The tab's text and tooltip
6507      */
6508     setText : function(text){
6509         this.text = text;
6510         this.textEl.update(text);
6511         this.setTooltip(text);
6512         if(!this.tabPanel.resizeTabs){
6513             this.autoSize();
6514         }
6515     },
6516     /**
6517      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6518      */
6519     activate : function(){
6520         this.tabPanel.activate(this.id);
6521     },
6522
6523     /**
6524      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6525      */
6526     disable : function(){
6527         if(this.tabPanel.active != this){
6528             this.disabled = true;
6529             this.pnode.addClass("disabled");
6530         }
6531     },
6532
6533     /**
6534      * Enables this TabPanelItem if it was previously disabled.
6535      */
6536     enable : function(){
6537         this.disabled = false;
6538         this.pnode.removeClass("disabled");
6539     },
6540
6541     /**
6542      * Sets the content for this TabPanelItem.
6543      * @param {String} content The content
6544      * @param {Boolean} loadScripts true to look for and load scripts
6545      */
6546     setContent : function(content, loadScripts){
6547         this.bodyEl.update(content, loadScripts);
6548     },
6549
6550     /**
6551      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6552      * @return {Roo.UpdateManager} The UpdateManager
6553      */
6554     getUpdateManager : function(){
6555         return this.bodyEl.getUpdateManager();
6556     },
6557
6558     /**
6559      * Set a URL to be used to load the content for this TabPanelItem.
6560      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6561      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6562      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6563      * @return {Roo.UpdateManager} The UpdateManager
6564      */
6565     setUrl : function(url, params, loadOnce){
6566         if(this.refreshDelegate){
6567             this.un('activate', this.refreshDelegate);
6568         }
6569         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6570         this.on("activate", this.refreshDelegate);
6571         return this.bodyEl.getUpdateManager();
6572     },
6573
6574     /** @private */
6575     _handleRefresh : function(url, params, loadOnce){
6576         if(!loadOnce || !this.loaded){
6577             var updater = this.bodyEl.getUpdateManager();
6578             updater.update(url, params, this._setLoaded.createDelegate(this));
6579         }
6580     },
6581
6582     /**
6583      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6584      *   Will fail silently if the setUrl method has not been called.
6585      *   This does not activate the panel, just updates its content.
6586      */
6587     refresh : function(){
6588         if(this.refreshDelegate){
6589            this.loaded = false;
6590            this.refreshDelegate();
6591         }
6592     },
6593
6594     /** @private */
6595     _setLoaded : function(){
6596         this.loaded = true;
6597     },
6598
6599     /** @private */
6600     closeClick : function(e){
6601         var o = {};
6602         e.stopEvent();
6603         this.fireEvent("beforeclose", this, o);
6604         if(o.cancel !== true){
6605             this.tabPanel.removeTab(this.id);
6606         }
6607     },
6608     /**
6609      * The text displayed in the tooltip for the close icon.
6610      * @type String
6611      */
6612     closeText : "Close this tab"
6613 });
6614
6615 /** @private */
6616 Roo.TabPanel.prototype.createStrip = function(container){
6617     var strip = document.createElement("div");
6618     strip.className = "x-tabs-wrap";
6619     container.appendChild(strip);
6620     return strip;
6621 };
6622 /** @private */
6623 Roo.TabPanel.prototype.createStripList = function(strip){
6624     // div wrapper for retard IE
6625     // returns the "tr" element.
6626     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6627         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6628         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6629     return strip.firstChild.firstChild.firstChild.firstChild;
6630 };
6631 /** @private */
6632 Roo.TabPanel.prototype.createBody = function(container){
6633     var body = document.createElement("div");
6634     Roo.id(body, "tab-body");
6635     Roo.fly(body).addClass("x-tabs-body");
6636     container.appendChild(body);
6637     return body;
6638 };
6639 /** @private */
6640 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6641     var body = Roo.getDom(id);
6642     if(!body){
6643         body = document.createElement("div");
6644         body.id = id;
6645     }
6646     Roo.fly(body).addClass("x-tabs-item-body");
6647     bodyEl.insertBefore(body, bodyEl.firstChild);
6648     return body;
6649 };
6650 /** @private */
6651 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6652     var td = document.createElement("td");
6653     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6654     //stripEl.appendChild(td);
6655     if(closable){
6656         td.className = "x-tabs-closable";
6657         if(!this.closeTpl){
6658             this.closeTpl = new Roo.Template(
6659                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6660                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6661                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6662             );
6663         }
6664         var el = this.closeTpl.overwrite(td, {"text": text});
6665         var close = el.getElementsByTagName("div")[0];
6666         var inner = el.getElementsByTagName("em")[0];
6667         return {"el": el, "close": close, "inner": inner};
6668     } else {
6669         if(!this.tabTpl){
6670             this.tabTpl = new Roo.Template(
6671                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6672                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6673             );
6674         }
6675         var el = this.tabTpl.overwrite(td, {"text": text});
6676         var inner = el.getElementsByTagName("em")[0];
6677         return {"el": el, "inner": inner};
6678     }
6679 };/*
6680  * Based on:
6681  * Ext JS Library 1.1.1
6682  * Copyright(c) 2006-2007, Ext JS, LLC.
6683  *
6684  * Originally Released Under LGPL - original licence link has changed is not relivant.
6685  *
6686  * Fork - LGPL
6687  * <script type="text/javascript">
6688  */
6689
6690 /**
6691  * @class Roo.Button
6692  * @extends Roo.util.Observable
6693  * Simple Button class
6694  * @cfg {String} text The button text
6695  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6696  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6697  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6698  * @cfg {Object} scope The scope of the handler
6699  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6700  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6701  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6702  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6703  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6704  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6705    applies if enableToggle = true)
6706  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6707  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6708   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6709  * @constructor
6710  * Create a new button
6711  * @param {Object} config The config object
6712  */
6713 Roo.Button = function(renderTo, config)
6714 {
6715     if (!config) {
6716         config = renderTo;
6717         renderTo = config.renderTo || false;
6718     }
6719     
6720     Roo.apply(this, config);
6721     this.addEvents({
6722         /**
6723              * @event click
6724              * Fires when this button is clicked
6725              * @param {Button} this
6726              * @param {EventObject} e The click event
6727              */
6728             "click" : true,
6729         /**
6730              * @event toggle
6731              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6732              * @param {Button} this
6733              * @param {Boolean} pressed
6734              */
6735             "toggle" : true,
6736         /**
6737              * @event mouseover
6738              * Fires when the mouse hovers over the button
6739              * @param {Button} this
6740              * @param {Event} e The event object
6741              */
6742         'mouseover' : true,
6743         /**
6744              * @event mouseout
6745              * Fires when the mouse exits the button
6746              * @param {Button} this
6747              * @param {Event} e The event object
6748              */
6749         'mouseout': true,
6750          /**
6751              * @event render
6752              * Fires when the button is rendered
6753              * @param {Button} this
6754              */
6755         'render': true
6756     });
6757     if(this.menu){
6758         this.menu = Roo.menu.MenuMgr.get(this.menu);
6759     }
6760     // register listeners first!!  - so render can be captured..
6761     Roo.util.Observable.call(this);
6762     if(renderTo){
6763         this.render(renderTo);
6764     }
6765     
6766   
6767 };
6768
6769 Roo.extend(Roo.Button, Roo.util.Observable, {
6770     /**
6771      * 
6772      */
6773     
6774     /**
6775      * Read-only. True if this button is hidden
6776      * @type Boolean
6777      */
6778     hidden : false,
6779     /**
6780      * Read-only. True if this button is disabled
6781      * @type Boolean
6782      */
6783     disabled : false,
6784     /**
6785      * Read-only. True if this button is pressed (only if enableToggle = true)
6786      * @type Boolean
6787      */
6788     pressed : false,
6789
6790     /**
6791      * @cfg {Number} tabIndex 
6792      * The DOM tabIndex for this button (defaults to undefined)
6793      */
6794     tabIndex : undefined,
6795
6796     /**
6797      * @cfg {Boolean} enableToggle
6798      * True to enable pressed/not pressed toggling (defaults to false)
6799      */
6800     enableToggle: false,
6801     /**
6802      * @cfg {Mixed} menu
6803      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6804      */
6805     menu : undefined,
6806     /**
6807      * @cfg {String} menuAlign
6808      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6809      */
6810     menuAlign : "tl-bl?",
6811
6812     /**
6813      * @cfg {String} iconCls
6814      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6815      */
6816     iconCls : undefined,
6817     /**
6818      * @cfg {String} type
6819      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6820      */
6821     type : 'button',
6822
6823     // private
6824     menuClassTarget: 'tr',
6825
6826     /**
6827      * @cfg {String} clickEvent
6828      * The type of event to map to the button's event handler (defaults to 'click')
6829      */
6830     clickEvent : 'click',
6831
6832     /**
6833      * @cfg {Boolean} handleMouseEvents
6834      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6835      */
6836     handleMouseEvents : true,
6837
6838     /**
6839      * @cfg {String} tooltipType
6840      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6841      */
6842     tooltipType : 'qtip',
6843
6844     /**
6845      * @cfg {String} cls
6846      * A CSS class to apply to the button's main element.
6847      */
6848     
6849     /**
6850      * @cfg {Roo.Template} template (Optional)
6851      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6852      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6853      * require code modifications if required elements (e.g. a button) aren't present.
6854      */
6855
6856     // private
6857     render : function(renderTo){
6858         var btn;
6859         if(this.hideParent){
6860             this.parentEl = Roo.get(renderTo);
6861         }
6862         if(!this.dhconfig){
6863             if(!this.template){
6864                 if(!Roo.Button.buttonTemplate){
6865                     // hideous table template
6866                     Roo.Button.buttonTemplate = new Roo.Template(
6867                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6868                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
6869                         "</tr></tbody></table>");
6870                 }
6871                 this.template = Roo.Button.buttonTemplate;
6872             }
6873             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6874             var btnEl = btn.child("button:first");
6875             btnEl.on('focus', this.onFocus, this);
6876             btnEl.on('blur', this.onBlur, this);
6877             if(this.cls){
6878                 btn.addClass(this.cls);
6879             }
6880             if(this.icon){
6881                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6882             }
6883             if(this.iconCls){
6884                 btnEl.addClass(this.iconCls);
6885                 if(!this.cls){
6886                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6887                 }
6888             }
6889             if(this.tabIndex !== undefined){
6890                 btnEl.dom.tabIndex = this.tabIndex;
6891             }
6892             if(this.tooltip){
6893                 if(typeof this.tooltip == 'object'){
6894                     Roo.QuickTips.tips(Roo.apply({
6895                           target: btnEl.id
6896                     }, this.tooltip));
6897                 } else {
6898                     btnEl.dom[this.tooltipType] = this.tooltip;
6899                 }
6900             }
6901         }else{
6902             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6903         }
6904         this.el = btn;
6905         if(this.id){
6906             this.el.dom.id = this.el.id = this.id;
6907         }
6908         if(this.menu){
6909             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6910             this.menu.on("show", this.onMenuShow, this);
6911             this.menu.on("hide", this.onMenuHide, this);
6912         }
6913         btn.addClass("x-btn");
6914         if(Roo.isIE && !Roo.isIE7){
6915             this.autoWidth.defer(1, this);
6916         }else{
6917             this.autoWidth();
6918         }
6919         if(this.handleMouseEvents){
6920             btn.on("mouseover", this.onMouseOver, this);
6921             btn.on("mouseout", this.onMouseOut, this);
6922             btn.on("mousedown", this.onMouseDown, this);
6923         }
6924         btn.on(this.clickEvent, this.onClick, this);
6925         //btn.on("mouseup", this.onMouseUp, this);
6926         if(this.hidden){
6927             this.hide();
6928         }
6929         if(this.disabled){
6930             this.disable();
6931         }
6932         Roo.ButtonToggleMgr.register(this);
6933         if(this.pressed){
6934             this.el.addClass("x-btn-pressed");
6935         }
6936         if(this.repeat){
6937             var repeater = new Roo.util.ClickRepeater(btn,
6938                 typeof this.repeat == "object" ? this.repeat : {}
6939             );
6940             repeater.on("click", this.onClick,  this);
6941         }
6942         
6943         this.fireEvent('render', this);
6944         
6945     },
6946     /**
6947      * Returns the button's underlying element
6948      * @return {Roo.Element} The element
6949      */
6950     getEl : function(){
6951         return this.el;  
6952     },
6953     
6954     /**
6955      * Destroys this Button and removes any listeners.
6956      */
6957     destroy : function(){
6958         Roo.ButtonToggleMgr.unregister(this);
6959         this.el.removeAllListeners();
6960         this.purgeListeners();
6961         this.el.remove();
6962     },
6963
6964     // private
6965     autoWidth : function(){
6966         if(this.el){
6967             this.el.setWidth("auto");
6968             if(Roo.isIE7 && Roo.isStrict){
6969                 var ib = this.el.child('button');
6970                 if(ib && ib.getWidth() > 20){
6971                     ib.clip();
6972                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6973                 }
6974             }
6975             if(this.minWidth){
6976                 if(this.hidden){
6977                     this.el.beginMeasure();
6978                 }
6979                 if(this.el.getWidth() < this.minWidth){
6980                     this.el.setWidth(this.minWidth);
6981                 }
6982                 if(this.hidden){
6983                     this.el.endMeasure();
6984                 }
6985             }
6986         }
6987     },
6988
6989     /**
6990      * Assigns this button's click handler
6991      * @param {Function} handler The function to call when the button is clicked
6992      * @param {Object} scope (optional) Scope for the function passed in
6993      */
6994     setHandler : function(handler, scope){
6995         this.handler = handler;
6996         this.scope = scope;  
6997     },
6998     
6999     /**
7000      * Sets this button's text
7001      * @param {String} text The button text
7002      */
7003     setText : function(text){
7004         this.text = text;
7005         if(this.el){
7006             this.el.child("td.x-btn-center button.x-btn-text").update(text);
7007         }
7008         this.autoWidth();
7009     },
7010     
7011     /**
7012      * Gets the text for this button
7013      * @return {String} The button text
7014      */
7015     getText : function(){
7016         return this.text;  
7017     },
7018     
7019     /**
7020      * Show this button
7021      */
7022     show: function(){
7023         this.hidden = false;
7024         if(this.el){
7025             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7026         }
7027     },
7028     
7029     /**
7030      * Hide this button
7031      */
7032     hide: function(){
7033         this.hidden = true;
7034         if(this.el){
7035             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7036         }
7037     },
7038     
7039     /**
7040      * Convenience function for boolean show/hide
7041      * @param {Boolean} visible True to show, false to hide
7042      */
7043     setVisible: function(visible){
7044         if(visible) {
7045             this.show();
7046         }else{
7047             this.hide();
7048         }
7049     },
7050     
7051     /**
7052      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7053      * @param {Boolean} state (optional) Force a particular state
7054      */
7055     toggle : function(state){
7056         state = state === undefined ? !this.pressed : state;
7057         if(state != this.pressed){
7058             if(state){
7059                 this.el.addClass("x-btn-pressed");
7060                 this.pressed = true;
7061                 this.fireEvent("toggle", this, true);
7062             }else{
7063                 this.el.removeClass("x-btn-pressed");
7064                 this.pressed = false;
7065                 this.fireEvent("toggle", this, false);
7066             }
7067             if(this.toggleHandler){
7068                 this.toggleHandler.call(this.scope || this, this, state);
7069             }
7070         }
7071     },
7072     
7073     /**
7074      * Focus the button
7075      */
7076     focus : function(){
7077         this.el.child('button:first').focus();
7078     },
7079     
7080     /**
7081      * Disable this button
7082      */
7083     disable : function(){
7084         if(this.el){
7085             this.el.addClass("x-btn-disabled");
7086         }
7087         this.disabled = true;
7088     },
7089     
7090     /**
7091      * Enable this button
7092      */
7093     enable : function(){
7094         if(this.el){
7095             this.el.removeClass("x-btn-disabled");
7096         }
7097         this.disabled = false;
7098     },
7099
7100     /**
7101      * Convenience function for boolean enable/disable
7102      * @param {Boolean} enabled True to enable, false to disable
7103      */
7104     setDisabled : function(v){
7105         this[v !== true ? "enable" : "disable"]();
7106     },
7107
7108     // private
7109     onClick : function(e)
7110     {
7111         if(e){
7112             e.preventDefault();
7113         }
7114         if(e.button != 0){
7115             return;
7116         }
7117         if(!this.disabled){
7118             if(this.enableToggle){
7119                 this.toggle();
7120             }
7121             if(this.menu && !this.menu.isVisible()){
7122                 this.menu.show(this.el, this.menuAlign);
7123             }
7124             this.fireEvent("click", this, e);
7125             if(this.handler){
7126                 this.el.removeClass("x-btn-over");
7127                 this.handler.call(this.scope || this, this, e);
7128             }
7129         }
7130     },
7131     // private
7132     onMouseOver : function(e){
7133         if(!this.disabled){
7134             this.el.addClass("x-btn-over");
7135             this.fireEvent('mouseover', this, e);
7136         }
7137     },
7138     // private
7139     onMouseOut : function(e){
7140         if(!e.within(this.el,  true)){
7141             this.el.removeClass("x-btn-over");
7142             this.fireEvent('mouseout', this, e);
7143         }
7144     },
7145     // private
7146     onFocus : function(e){
7147         if(!this.disabled){
7148             this.el.addClass("x-btn-focus");
7149         }
7150     },
7151     // private
7152     onBlur : function(e){
7153         this.el.removeClass("x-btn-focus");
7154     },
7155     // private
7156     onMouseDown : function(e){
7157         if(!this.disabled && e.button == 0){
7158             this.el.addClass("x-btn-click");
7159             Roo.get(document).on('mouseup', this.onMouseUp, this);
7160         }
7161     },
7162     // private
7163     onMouseUp : function(e){
7164         if(e.button == 0){
7165             this.el.removeClass("x-btn-click");
7166             Roo.get(document).un('mouseup', this.onMouseUp, this);
7167         }
7168     },
7169     // private
7170     onMenuShow : function(e){
7171         this.el.addClass("x-btn-menu-active");
7172     },
7173     // private
7174     onMenuHide : function(e){
7175         this.el.removeClass("x-btn-menu-active");
7176     }   
7177 });
7178
7179 // Private utility class used by Button
7180 Roo.ButtonToggleMgr = function(){
7181    var groups = {};
7182    
7183    function toggleGroup(btn, state){
7184        if(state){
7185            var g = groups[btn.toggleGroup];
7186            for(var i = 0, l = g.length; i < l; i++){
7187                if(g[i] != btn){
7188                    g[i].toggle(false);
7189                }
7190            }
7191        }
7192    }
7193    
7194    return {
7195        register : function(btn){
7196            if(!btn.toggleGroup){
7197                return;
7198            }
7199            var g = groups[btn.toggleGroup];
7200            if(!g){
7201                g = groups[btn.toggleGroup] = [];
7202            }
7203            g.push(btn);
7204            btn.on("toggle", toggleGroup);
7205        },
7206        
7207        unregister : function(btn){
7208            if(!btn.toggleGroup){
7209                return;
7210            }
7211            var g = groups[btn.toggleGroup];
7212            if(g){
7213                g.remove(btn);
7214                btn.un("toggle", toggleGroup);
7215            }
7216        }
7217    };
7218 }();/*
7219  * Based on:
7220  * Ext JS Library 1.1.1
7221  * Copyright(c) 2006-2007, Ext JS, LLC.
7222  *
7223  * Originally Released Under LGPL - original licence link has changed is not relivant.
7224  *
7225  * Fork - LGPL
7226  * <script type="text/javascript">
7227  */
7228  
7229 /**
7230  * @class Roo.SplitButton
7231  * @extends Roo.Button
7232  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7233  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7234  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7235  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7236  * @cfg {String} arrowTooltip The title attribute of the arrow
7237  * @constructor
7238  * Create a new menu button
7239  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7240  * @param {Object} config The config object
7241  */
7242 Roo.SplitButton = function(renderTo, config){
7243     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7244     /**
7245      * @event arrowclick
7246      * Fires when this button's arrow is clicked
7247      * @param {SplitButton} this
7248      * @param {EventObject} e The click event
7249      */
7250     this.addEvents({"arrowclick":true});
7251 };
7252
7253 Roo.extend(Roo.SplitButton, Roo.Button, {
7254     render : function(renderTo){
7255         // this is one sweet looking template!
7256         var tpl = new Roo.Template(
7257             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7258             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7259             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
7260             "</tbody></table></td><td>",
7261             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7262             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
7263             "</tbody></table></td></tr></table>"
7264         );
7265         var btn = tpl.append(renderTo, [this.text, this.type], true);
7266         var btnEl = btn.child("button");
7267         if(this.cls){
7268             btn.addClass(this.cls);
7269         }
7270         if(this.icon){
7271             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7272         }
7273         if(this.iconCls){
7274             btnEl.addClass(this.iconCls);
7275             if(!this.cls){
7276                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7277             }
7278         }
7279         this.el = btn;
7280         if(this.handleMouseEvents){
7281             btn.on("mouseover", this.onMouseOver, this);
7282             btn.on("mouseout", this.onMouseOut, this);
7283             btn.on("mousedown", this.onMouseDown, this);
7284             btn.on("mouseup", this.onMouseUp, this);
7285         }
7286         btn.on(this.clickEvent, this.onClick, this);
7287         if(this.tooltip){
7288             if(typeof this.tooltip == 'object'){
7289                 Roo.QuickTips.tips(Roo.apply({
7290                       target: btnEl.id
7291                 }, this.tooltip));
7292             } else {
7293                 btnEl.dom[this.tooltipType] = this.tooltip;
7294             }
7295         }
7296         if(this.arrowTooltip){
7297             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7298         }
7299         if(this.hidden){
7300             this.hide();
7301         }
7302         if(this.disabled){
7303             this.disable();
7304         }
7305         if(this.pressed){
7306             this.el.addClass("x-btn-pressed");
7307         }
7308         if(Roo.isIE && !Roo.isIE7){
7309             this.autoWidth.defer(1, this);
7310         }else{
7311             this.autoWidth();
7312         }
7313         if(this.menu){
7314             this.menu.on("show", this.onMenuShow, this);
7315             this.menu.on("hide", this.onMenuHide, this);
7316         }
7317         this.fireEvent('render', this);
7318     },
7319
7320     // private
7321     autoWidth : function(){
7322         if(this.el){
7323             var tbl = this.el.child("table:first");
7324             var tbl2 = this.el.child("table:last");
7325             this.el.setWidth("auto");
7326             tbl.setWidth("auto");
7327             if(Roo.isIE7 && Roo.isStrict){
7328                 var ib = this.el.child('button:first');
7329                 if(ib && ib.getWidth() > 20){
7330                     ib.clip();
7331                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7332                 }
7333             }
7334             if(this.minWidth){
7335                 if(this.hidden){
7336                     this.el.beginMeasure();
7337                 }
7338                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7339                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7340                 }
7341                 if(this.hidden){
7342                     this.el.endMeasure();
7343                 }
7344             }
7345             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7346         } 
7347     },
7348     /**
7349      * Sets this button's click handler
7350      * @param {Function} handler The function to call when the button is clicked
7351      * @param {Object} scope (optional) Scope for the function passed above
7352      */
7353     setHandler : function(handler, scope){
7354         this.handler = handler;
7355         this.scope = scope;  
7356     },
7357     
7358     /**
7359      * Sets this button's arrow click handler
7360      * @param {Function} handler The function to call when the arrow is clicked
7361      * @param {Object} scope (optional) Scope for the function passed above
7362      */
7363     setArrowHandler : function(handler, scope){
7364         this.arrowHandler = handler;
7365         this.scope = scope;  
7366     },
7367     
7368     /**
7369      * Focus the button
7370      */
7371     focus : function(){
7372         if(this.el){
7373             this.el.child("button:first").focus();
7374         }
7375     },
7376
7377     // private
7378     onClick : function(e){
7379         e.preventDefault();
7380         if(!this.disabled){
7381             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7382                 if(this.menu && !this.menu.isVisible()){
7383                     this.menu.show(this.el, this.menuAlign);
7384                 }
7385                 this.fireEvent("arrowclick", this, e);
7386                 if(this.arrowHandler){
7387                     this.arrowHandler.call(this.scope || this, this, e);
7388                 }
7389             }else{
7390                 this.fireEvent("click", this, e);
7391                 if(this.handler){
7392                     this.handler.call(this.scope || this, this, e);
7393                 }
7394             }
7395         }
7396     },
7397     // private
7398     onMouseDown : function(e){
7399         if(!this.disabled){
7400             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7401         }
7402     },
7403     // private
7404     onMouseUp : function(e){
7405         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7406     }   
7407 });
7408
7409
7410 // backwards compat
7411 Roo.MenuButton = Roo.SplitButton;/*
7412  * Based on:
7413  * Ext JS Library 1.1.1
7414  * Copyright(c) 2006-2007, Ext JS, LLC.
7415  *
7416  * Originally Released Under LGPL - original licence link has changed is not relivant.
7417  *
7418  * Fork - LGPL
7419  * <script type="text/javascript">
7420  */
7421
7422 /**
7423  * @class Roo.Toolbar
7424  * Basic Toolbar class.
7425  * @constructor
7426  * Creates a new Toolbar
7427  * @param {Object} container The config object
7428  */ 
7429 Roo.Toolbar = function(container, buttons, config)
7430 {
7431     /// old consturctor format still supported..
7432     if(container instanceof Array){ // omit the container for later rendering
7433         buttons = container;
7434         config = buttons;
7435         container = null;
7436     }
7437     if (typeof(container) == 'object' && container.xtype) {
7438         config = container;
7439         container = config.container;
7440         buttons = config.buttons || []; // not really - use items!!
7441     }
7442     var xitems = [];
7443     if (config && config.items) {
7444         xitems = config.items;
7445         delete config.items;
7446     }
7447     Roo.apply(this, config);
7448     this.buttons = buttons;
7449     
7450     if(container){
7451         this.render(container);
7452     }
7453     this.xitems = xitems;
7454     Roo.each(xitems, function(b) {
7455         this.add(b);
7456     }, this);
7457     
7458 };
7459
7460 Roo.Toolbar.prototype = {
7461     /**
7462      * @cfg {Array} items
7463      * array of button configs or elements to add (will be converted to a MixedCollection)
7464      */
7465     
7466     /**
7467      * @cfg {String/HTMLElement/Element} container
7468      * The id or element that will contain the toolbar
7469      */
7470     // private
7471     render : function(ct){
7472         this.el = Roo.get(ct);
7473         if(this.cls){
7474             this.el.addClass(this.cls);
7475         }
7476         // using a table allows for vertical alignment
7477         // 100% width is needed by Safari...
7478         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7479         this.tr = this.el.child("tr", true);
7480         var autoId = 0;
7481         this.items = new Roo.util.MixedCollection(false, function(o){
7482             return o.id || ("item" + (++autoId));
7483         });
7484         if(this.buttons){
7485             this.add.apply(this, this.buttons);
7486             delete this.buttons;
7487         }
7488     },
7489
7490     /**
7491      * Adds element(s) to the toolbar -- this function takes a variable number of 
7492      * arguments of mixed type and adds them to the toolbar.
7493      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7494      * <ul>
7495      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7496      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7497      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7498      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7499      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7500      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7501      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7502      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7503      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7504      * </ul>
7505      * @param {Mixed} arg2
7506      * @param {Mixed} etc.
7507      */
7508     add : function(){
7509         var a = arguments, l = a.length;
7510         for(var i = 0; i < l; i++){
7511             this._add(a[i]);
7512         }
7513     },
7514     // private..
7515     _add : function(el) {
7516         
7517         if (el.xtype) {
7518             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7519         }
7520         
7521         if (el.applyTo){ // some kind of form field
7522             return this.addField(el);
7523         } 
7524         if (el.render){ // some kind of Toolbar.Item
7525             return this.addItem(el);
7526         }
7527         if (typeof el == "string"){ // string
7528             if(el == "separator" || el == "-"){
7529                 return this.addSeparator();
7530             }
7531             if (el == " "){
7532                 return this.addSpacer();
7533             }
7534             if(el == "->"){
7535                 return this.addFill();
7536             }
7537             return this.addText(el);
7538             
7539         }
7540         if(el.tagName){ // element
7541             return this.addElement(el);
7542         }
7543         if(typeof el == "object"){ // must be button config?
7544             return this.addButton(el);
7545         }
7546         // and now what?!?!
7547         return false;
7548         
7549     },
7550     
7551     /**
7552      * Add an Xtype element
7553      * @param {Object} xtype Xtype Object
7554      * @return {Object} created Object
7555      */
7556     addxtype : function(e){
7557         return this.add(e);  
7558     },
7559     
7560     /**
7561      * Returns the Element for this toolbar.
7562      * @return {Roo.Element}
7563      */
7564     getEl : function(){
7565         return this.el;  
7566     },
7567     
7568     /**
7569      * Adds a separator
7570      * @return {Roo.Toolbar.Item} The separator item
7571      */
7572     addSeparator : function(){
7573         return this.addItem(new Roo.Toolbar.Separator());
7574     },
7575
7576     /**
7577      * Adds a spacer element
7578      * @return {Roo.Toolbar.Spacer} The spacer item
7579      */
7580     addSpacer : function(){
7581         return this.addItem(new Roo.Toolbar.Spacer());
7582     },
7583
7584     /**
7585      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7586      * @return {Roo.Toolbar.Fill} The fill item
7587      */
7588     addFill : function(){
7589         return this.addItem(new Roo.Toolbar.Fill());
7590     },
7591
7592     /**
7593      * Adds any standard HTML element to the toolbar
7594      * @param {String/HTMLElement/Element} el The element or id of the element to add
7595      * @return {Roo.Toolbar.Item} The element's item
7596      */
7597     addElement : function(el){
7598         return this.addItem(new Roo.Toolbar.Item(el));
7599     },
7600     /**
7601      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7602      * @type Roo.util.MixedCollection  
7603      */
7604     items : false,
7605      
7606     /**
7607      * Adds any Toolbar.Item or subclass
7608      * @param {Roo.Toolbar.Item} item
7609      * @return {Roo.Toolbar.Item} The item
7610      */
7611     addItem : function(item){
7612         var td = this.nextBlock();
7613         item.render(td);
7614         this.items.add(item);
7615         return item;
7616     },
7617     
7618     /**
7619      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7620      * @param {Object/Array} config A button config or array of configs
7621      * @return {Roo.Toolbar.Button/Array}
7622      */
7623     addButton : function(config){
7624         if(config instanceof Array){
7625             var buttons = [];
7626             for(var i = 0, len = config.length; i < len; i++) {
7627                 buttons.push(this.addButton(config[i]));
7628             }
7629             return buttons;
7630         }
7631         var b = config;
7632         if(!(config instanceof Roo.Toolbar.Button)){
7633             b = config.split ?
7634                 new Roo.Toolbar.SplitButton(config) :
7635                 new Roo.Toolbar.Button(config);
7636         }
7637         var td = this.nextBlock();
7638         b.render(td);
7639         this.items.add(b);
7640         return b;
7641     },
7642     
7643     /**
7644      * Adds text to the toolbar
7645      * @param {String} text The text to add
7646      * @return {Roo.Toolbar.Item} The element's item
7647      */
7648     addText : function(text){
7649         return this.addItem(new Roo.Toolbar.TextItem(text));
7650     },
7651     
7652     /**
7653      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7654      * @param {Number} index The index where the item is to be inserted
7655      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7656      * @return {Roo.Toolbar.Button/Item}
7657      */
7658     insertButton : function(index, item){
7659         if(item instanceof Array){
7660             var buttons = [];
7661             for(var i = 0, len = item.length; i < len; i++) {
7662                buttons.push(this.insertButton(index + i, item[i]));
7663             }
7664             return buttons;
7665         }
7666         if (!(item instanceof Roo.Toolbar.Button)){
7667            item = new Roo.Toolbar.Button(item);
7668         }
7669         var td = document.createElement("td");
7670         this.tr.insertBefore(td, this.tr.childNodes[index]);
7671         item.render(td);
7672         this.items.insert(index, item);
7673         return item;
7674     },
7675     
7676     /**
7677      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7678      * @param {Object} config
7679      * @return {Roo.Toolbar.Item} The element's item
7680      */
7681     addDom : function(config, returnEl){
7682         var td = this.nextBlock();
7683         Roo.DomHelper.overwrite(td, config);
7684         var ti = new Roo.Toolbar.Item(td.firstChild);
7685         ti.render(td);
7686         this.items.add(ti);
7687         return ti;
7688     },
7689
7690     /**
7691      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7692      * @type Roo.util.MixedCollection  
7693      */
7694     fields : false,
7695     
7696     /**
7697      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7698      * Note: the field should not have been rendered yet. For a field that has already been
7699      * rendered, use {@link #addElement}.
7700      * @param {Roo.form.Field} field
7701      * @return {Roo.ToolbarItem}
7702      */
7703      
7704       
7705     addField : function(field) {
7706         if (!this.fields) {
7707             var autoId = 0;
7708             this.fields = new Roo.util.MixedCollection(false, function(o){
7709                 return o.id || ("item" + (++autoId));
7710             });
7711
7712         }
7713         
7714         var td = this.nextBlock();
7715         field.render(td);
7716         var ti = new Roo.Toolbar.Item(td.firstChild);
7717         ti.render(td);
7718         this.items.add(ti);
7719         this.fields.add(field);
7720         return ti;
7721     },
7722     /**
7723      * Hide the toolbar
7724      * @method hide
7725      */
7726      
7727       
7728     hide : function()
7729     {
7730         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7731         this.el.child('div').hide();
7732     },
7733     /**
7734      * Show the toolbar
7735      * @method show
7736      */
7737     show : function()
7738     {
7739         this.el.child('div').show();
7740     },
7741       
7742     // private
7743     nextBlock : function(){
7744         var td = document.createElement("td");
7745         this.tr.appendChild(td);
7746         return td;
7747     },
7748
7749     // private
7750     destroy : function(){
7751         if(this.items){ // rendered?
7752             Roo.destroy.apply(Roo, this.items.items);
7753         }
7754         if(this.fields){ // rendered?
7755             Roo.destroy.apply(Roo, this.fields.items);
7756         }
7757         Roo.Element.uncache(this.el, this.tr);
7758     }
7759 };
7760
7761 /**
7762  * @class Roo.Toolbar.Item
7763  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7764  * @constructor
7765  * Creates a new Item
7766  * @param {HTMLElement} el 
7767  */
7768 Roo.Toolbar.Item = function(el){
7769     var cfg = {};
7770     if (typeof (el.xtype) != 'undefined') {
7771         cfg = el;
7772         el = cfg.el;
7773     }
7774     
7775     this.el = Roo.getDom(el);
7776     this.id = Roo.id(this.el);
7777     this.hidden = false;
7778     
7779     this.addEvents({
7780          /**
7781              * @event render
7782              * Fires when the button is rendered
7783              * @param {Button} this
7784              */
7785         'render': true
7786     });
7787     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7788 };
7789 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7790 //Roo.Toolbar.Item.prototype = {
7791     
7792     /**
7793      * Get this item's HTML Element
7794      * @return {HTMLElement}
7795      */
7796     getEl : function(){
7797        return this.el;  
7798     },
7799
7800     // private
7801     render : function(td){
7802         
7803          this.td = td;
7804         td.appendChild(this.el);
7805         
7806         this.fireEvent('render', this);
7807     },
7808     
7809     /**
7810      * Removes and destroys this item.
7811      */
7812     destroy : function(){
7813         this.td.parentNode.removeChild(this.td);
7814     },
7815     
7816     /**
7817      * Shows this item.
7818      */
7819     show: function(){
7820         this.hidden = false;
7821         this.td.style.display = "";
7822     },
7823     
7824     /**
7825      * Hides this item.
7826      */
7827     hide: function(){
7828         this.hidden = true;
7829         this.td.style.display = "none";
7830     },
7831     
7832     /**
7833      * Convenience function for boolean show/hide.
7834      * @param {Boolean} visible true to show/false to hide
7835      */
7836     setVisible: function(visible){
7837         if(visible) {
7838             this.show();
7839         }else{
7840             this.hide();
7841         }
7842     },
7843     
7844     /**
7845      * Try to focus this item.
7846      */
7847     focus : function(){
7848         Roo.fly(this.el).focus();
7849     },
7850     
7851     /**
7852      * Disables this item.
7853      */
7854     disable : function(){
7855         Roo.fly(this.td).addClass("x-item-disabled");
7856         this.disabled = true;
7857         this.el.disabled = true;
7858     },
7859     
7860     /**
7861      * Enables this item.
7862      */
7863     enable : function(){
7864         Roo.fly(this.td).removeClass("x-item-disabled");
7865         this.disabled = false;
7866         this.el.disabled = false;
7867     }
7868 });
7869
7870
7871 /**
7872  * @class Roo.Toolbar.Separator
7873  * @extends Roo.Toolbar.Item
7874  * A simple toolbar separator class
7875  * @constructor
7876  * Creates a new Separator
7877  */
7878 Roo.Toolbar.Separator = function(cfg){
7879     
7880     var s = document.createElement("span");
7881     s.className = "ytb-sep";
7882     if (cfg) {
7883         cfg.el = s;
7884     }
7885     
7886     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7887 };
7888 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7889     enable:Roo.emptyFn,
7890     disable:Roo.emptyFn,
7891     focus:Roo.emptyFn
7892 });
7893
7894 /**
7895  * @class Roo.Toolbar.Spacer
7896  * @extends Roo.Toolbar.Item
7897  * A simple element that adds extra horizontal space to a toolbar.
7898  * @constructor
7899  * Creates a new Spacer
7900  */
7901 Roo.Toolbar.Spacer = function(cfg){
7902     var s = document.createElement("div");
7903     s.className = "ytb-spacer";
7904     if (cfg) {
7905         cfg.el = s;
7906     }
7907     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7908 };
7909 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7910     enable:Roo.emptyFn,
7911     disable:Roo.emptyFn,
7912     focus:Roo.emptyFn
7913 });
7914
7915 /**
7916  * @class Roo.Toolbar.Fill
7917  * @extends Roo.Toolbar.Spacer
7918  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7919  * @constructor
7920  * Creates a new Spacer
7921  */
7922 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7923     // private
7924     render : function(td){
7925         td.style.width = '100%';
7926         Roo.Toolbar.Fill.superclass.render.call(this, td);
7927     }
7928 });
7929
7930 /**
7931  * @class Roo.Toolbar.TextItem
7932  * @extends Roo.Toolbar.Item
7933  * A simple class that renders text directly into a toolbar.
7934  * @constructor
7935  * Creates a new TextItem
7936  * @param {String} text
7937  */
7938 Roo.Toolbar.TextItem = function(cfg){
7939     var  text = cfg || "";
7940     if (typeof(cfg) == 'object') {
7941         text = cfg.text || "";
7942     }  else {
7943         cfg = null;
7944     }
7945     var s = document.createElement("span");
7946     s.className = "ytb-text";
7947     s.innerHTML = text;
7948     if (cfg) {
7949         cfg.el  = s;
7950     }
7951     
7952     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7953 };
7954 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7955     
7956      
7957     enable:Roo.emptyFn,
7958     disable:Roo.emptyFn,
7959     focus:Roo.emptyFn
7960 });
7961
7962 /**
7963  * @class Roo.Toolbar.Button
7964  * @extends Roo.Button
7965  * A button that renders into a toolbar.
7966  * @constructor
7967  * Creates a new Button
7968  * @param {Object} config A standard {@link Roo.Button} config object
7969  */
7970 Roo.Toolbar.Button = function(config){
7971     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7972 };
7973 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7974     render : function(td){
7975         this.td = td;
7976         Roo.Toolbar.Button.superclass.render.call(this, td);
7977     },
7978     
7979     /**
7980      * Removes and destroys this button
7981      */
7982     destroy : function(){
7983         Roo.Toolbar.Button.superclass.destroy.call(this);
7984         this.td.parentNode.removeChild(this.td);
7985     },
7986     
7987     /**
7988      * Shows this button
7989      */
7990     show: function(){
7991         this.hidden = false;
7992         this.td.style.display = "";
7993     },
7994     
7995     /**
7996      * Hides this button
7997      */
7998     hide: function(){
7999         this.hidden = true;
8000         this.td.style.display = "none";
8001     },
8002
8003     /**
8004      * Disables this item
8005      */
8006     disable : function(){
8007         Roo.fly(this.td).addClass("x-item-disabled");
8008         this.disabled = true;
8009     },
8010
8011     /**
8012      * Enables this item
8013      */
8014     enable : function(){
8015         Roo.fly(this.td).removeClass("x-item-disabled");
8016         this.disabled = false;
8017     }
8018 });
8019 // backwards compat
8020 Roo.ToolbarButton = Roo.Toolbar.Button;
8021
8022 /**
8023  * @class Roo.Toolbar.SplitButton
8024  * @extends Roo.SplitButton
8025  * A menu button that renders into a toolbar.
8026  * @constructor
8027  * Creates a new SplitButton
8028  * @param {Object} config A standard {@link Roo.SplitButton} config object
8029  */
8030 Roo.Toolbar.SplitButton = function(config){
8031     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8032 };
8033 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8034     render : function(td){
8035         this.td = td;
8036         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8037     },
8038     
8039     /**
8040      * Removes and destroys this button
8041      */
8042     destroy : function(){
8043         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8044         this.td.parentNode.removeChild(this.td);
8045     },
8046     
8047     /**
8048      * Shows this button
8049      */
8050     show: function(){
8051         this.hidden = false;
8052         this.td.style.display = "";
8053     },
8054     
8055     /**
8056      * Hides this button
8057      */
8058     hide: function(){
8059         this.hidden = true;
8060         this.td.style.display = "none";
8061     }
8062 });
8063
8064 // backwards compat
8065 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8066  * Based on:
8067  * Ext JS Library 1.1.1
8068  * Copyright(c) 2006-2007, Ext JS, LLC.
8069  *
8070  * Originally Released Under LGPL - original licence link has changed is not relivant.
8071  *
8072  * Fork - LGPL
8073  * <script type="text/javascript">
8074  */
8075  
8076 /**
8077  * @class Roo.PagingToolbar
8078  * @extends Roo.Toolbar
8079  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8080  * @constructor
8081  * Create a new PagingToolbar
8082  * @param {Object} config The config object
8083  */
8084 Roo.PagingToolbar = function(el, ds, config)
8085 {
8086     // old args format still supported... - xtype is prefered..
8087     if (typeof(el) == 'object' && el.xtype) {
8088         // created from xtype...
8089         config = el;
8090         ds = el.dataSource;
8091         el = config.container;
8092     }
8093     var items = [];
8094     if (config.items) {
8095         items = config.items;
8096         config.items = [];
8097     }
8098     
8099     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8100     this.ds = ds;
8101     this.cursor = 0;
8102     this.renderButtons(this.el);
8103     this.bind(ds);
8104     
8105     // supprot items array.
8106    
8107     Roo.each(items, function(e) {
8108         this.add(Roo.factory(e));
8109     },this);
8110     
8111 };
8112
8113 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8114     /**
8115      * @cfg {Roo.data.Store} dataSource
8116      * The underlying data store providing the paged data
8117      */
8118     /**
8119      * @cfg {String/HTMLElement/Element} container
8120      * container The id or element that will contain the toolbar
8121      */
8122     /**
8123      * @cfg {Boolean} displayInfo
8124      * True to display the displayMsg (defaults to false)
8125      */
8126     /**
8127      * @cfg {Number} pageSize
8128      * The number of records to display per page (defaults to 20)
8129      */
8130     pageSize: 20,
8131     /**
8132      * @cfg {String} displayMsg
8133      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8134      */
8135     displayMsg : 'Displaying {0} - {1} of {2}',
8136     /**
8137      * @cfg {String} emptyMsg
8138      * The message to display when no records are found (defaults to "No data to display")
8139      */
8140     emptyMsg : 'No data to display',
8141     /**
8142      * Customizable piece of the default paging text (defaults to "Page")
8143      * @type String
8144      */
8145     beforePageText : "Page",
8146     /**
8147      * Customizable piece of the default paging text (defaults to "of %0")
8148      * @type String
8149      */
8150     afterPageText : "of {0}",
8151     /**
8152      * Customizable piece of the default paging text (defaults to "First Page")
8153      * @type String
8154      */
8155     firstText : "First Page",
8156     /**
8157      * Customizable piece of the default paging text (defaults to "Previous Page")
8158      * @type String
8159      */
8160     prevText : "Previous Page",
8161     /**
8162      * Customizable piece of the default paging text (defaults to "Next Page")
8163      * @type String
8164      */
8165     nextText : "Next Page",
8166     /**
8167      * Customizable piece of the default paging text (defaults to "Last Page")
8168      * @type String
8169      */
8170     lastText : "Last Page",
8171     /**
8172      * Customizable piece of the default paging text (defaults to "Refresh")
8173      * @type String
8174      */
8175     refreshText : "Refresh",
8176
8177     // private
8178     renderButtons : function(el){
8179         Roo.PagingToolbar.superclass.render.call(this, el);
8180         this.first = this.addButton({
8181             tooltip: this.firstText,
8182             cls: "x-btn-icon x-grid-page-first",
8183             disabled: true,
8184             handler: this.onClick.createDelegate(this, ["first"])
8185         });
8186         this.prev = this.addButton({
8187             tooltip: this.prevText,
8188             cls: "x-btn-icon x-grid-page-prev",
8189             disabled: true,
8190             handler: this.onClick.createDelegate(this, ["prev"])
8191         });
8192         //this.addSeparator();
8193         this.add(this.beforePageText);
8194         this.field = Roo.get(this.addDom({
8195            tag: "input",
8196            type: "text",
8197            size: "3",
8198            value: "1",
8199            cls: "x-grid-page-number"
8200         }).el);
8201         this.field.on("keydown", this.onPagingKeydown, this);
8202         this.field.on("focus", function(){this.dom.select();});
8203         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8204         this.field.setHeight(18);
8205         //this.addSeparator();
8206         this.next = this.addButton({
8207             tooltip: this.nextText,
8208             cls: "x-btn-icon x-grid-page-next",
8209             disabled: true,
8210             handler: this.onClick.createDelegate(this, ["next"])
8211         });
8212         this.last = this.addButton({
8213             tooltip: this.lastText,
8214             cls: "x-btn-icon x-grid-page-last",
8215             disabled: true,
8216             handler: this.onClick.createDelegate(this, ["last"])
8217         });
8218         //this.addSeparator();
8219         this.loading = this.addButton({
8220             tooltip: this.refreshText,
8221             cls: "x-btn-icon x-grid-loading",
8222             handler: this.onClick.createDelegate(this, ["refresh"])
8223         });
8224
8225         if(this.displayInfo){
8226             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8227         }
8228     },
8229
8230     // private
8231     updateInfo : function(){
8232         if(this.displayEl){
8233             var count = this.ds.getCount();
8234             var msg = count == 0 ?
8235                 this.emptyMsg :
8236                 String.format(
8237                     this.displayMsg,
8238                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8239                 );
8240             this.displayEl.update(msg);
8241         }
8242     },
8243
8244     // private
8245     onLoad : function(ds, r, o){
8246        this.cursor = o.params ? o.params.start : 0;
8247        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8248
8249        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8250        this.field.dom.value = ap;
8251        this.first.setDisabled(ap == 1);
8252        this.prev.setDisabled(ap == 1);
8253        this.next.setDisabled(ap == ps);
8254        this.last.setDisabled(ap == ps);
8255        this.loading.enable();
8256        this.updateInfo();
8257     },
8258
8259     // private
8260     getPageData : function(){
8261         var total = this.ds.getTotalCount();
8262         return {
8263             total : total,
8264             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8265             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8266         };
8267     },
8268
8269     // private
8270     onLoadError : function(){
8271         this.loading.enable();
8272     },
8273
8274     // private
8275     onPagingKeydown : function(e){
8276         var k = e.getKey();
8277         var d = this.getPageData();
8278         if(k == e.RETURN){
8279             var v = this.field.dom.value, pageNum;
8280             if(!v || isNaN(pageNum = parseInt(v, 10))){
8281                 this.field.dom.value = d.activePage;
8282                 return;
8283             }
8284             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8285             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8286             e.stopEvent();
8287         }
8288         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
8289         {
8290           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8291           this.field.dom.value = pageNum;
8292           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8293           e.stopEvent();
8294         }
8295         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8296         {
8297           var v = this.field.dom.value, pageNum; 
8298           var increment = (e.shiftKey) ? 10 : 1;
8299           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8300             increment *= -1;
8301           }
8302           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8303             this.field.dom.value = d.activePage;
8304             return;
8305           }
8306           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8307           {
8308             this.field.dom.value = parseInt(v, 10) + increment;
8309             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8310             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8311           }
8312           e.stopEvent();
8313         }
8314     },
8315
8316     // private
8317     beforeLoad : function(){
8318         if(this.loading){
8319             this.loading.disable();
8320         }
8321     },
8322
8323     // private
8324     onClick : function(which){
8325         var ds = this.ds;
8326         switch(which){
8327             case "first":
8328                 ds.load({params:{start: 0, limit: this.pageSize}});
8329             break;
8330             case "prev":
8331                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8332             break;
8333             case "next":
8334                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8335             break;
8336             case "last":
8337                 var total = ds.getTotalCount();
8338                 var extra = total % this.pageSize;
8339                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8340                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8341             break;
8342             case "refresh":
8343                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8344             break;
8345         }
8346     },
8347
8348     /**
8349      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8350      * @param {Roo.data.Store} store The data store to unbind
8351      */
8352     unbind : function(ds){
8353         ds.un("beforeload", this.beforeLoad, this);
8354         ds.un("load", this.onLoad, this);
8355         ds.un("loadexception", this.onLoadError, this);
8356         ds.un("remove", this.updateInfo, this);
8357         ds.un("add", this.updateInfo, this);
8358         this.ds = undefined;
8359     },
8360
8361     /**
8362      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8363      * @param {Roo.data.Store} store The data store to bind
8364      */
8365     bind : function(ds){
8366         ds.on("beforeload", this.beforeLoad, this);
8367         ds.on("load", this.onLoad, this);
8368         ds.on("loadexception", this.onLoadError, this);
8369         ds.on("remove", this.updateInfo, this);
8370         ds.on("add", this.updateInfo, this);
8371         this.ds = ds;
8372     }
8373 });/*
8374  * Based on:
8375  * Ext JS Library 1.1.1
8376  * Copyright(c) 2006-2007, Ext JS, LLC.
8377  *
8378  * Originally Released Under LGPL - original licence link has changed is not relivant.
8379  *
8380  * Fork - LGPL
8381  * <script type="text/javascript">
8382  */
8383
8384 /**
8385  * @class Roo.Resizable
8386  * @extends Roo.util.Observable
8387  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8388  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8389  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
8390  * the element will be wrapped for you automatically.</p>
8391  * <p>Here is the list of valid resize handles:</p>
8392  * <pre>
8393 Value   Description
8394 ------  -------------------
8395  'n'     north
8396  's'     south
8397  'e'     east
8398  'w'     west
8399  'nw'    northwest
8400  'sw'    southwest
8401  'se'    southeast
8402  'ne'    northeast
8403  'hd'    horizontal drag
8404  'all'   all
8405 </pre>
8406  * <p>Here's an example showing the creation of a typical Resizable:</p>
8407  * <pre><code>
8408 var resizer = new Roo.Resizable("element-id", {
8409     handles: 'all',
8410     minWidth: 200,
8411     minHeight: 100,
8412     maxWidth: 500,
8413     maxHeight: 400,
8414     pinned: true
8415 });
8416 resizer.on("resize", myHandler);
8417 </code></pre>
8418  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8419  * resizer.east.setDisplayed(false);</p>
8420  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8421  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8422  * resize operation's new size (defaults to [0, 0])
8423  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8424  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8425  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8426  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8427  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8428  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8429  * @cfg {Number} width The width of the element in pixels (defaults to null)
8430  * @cfg {Number} height The height of the element in pixels (defaults to null)
8431  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8432  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8433  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8434  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8435  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8436  * in favor of the handles config option (defaults to false)
8437  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8438  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8439  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8440  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8441  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8442  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8443  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8444  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8445  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8446  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8447  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8448  * @constructor
8449  * Create a new resizable component
8450  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8451  * @param {Object} config configuration options
8452   */
8453 Roo.Resizable = function(el, config)
8454 {
8455     this.el = Roo.get(el);
8456
8457     if(config && config.wrap){
8458         config.resizeChild = this.el;
8459         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8460         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8461         this.el.setStyle("overflow", "hidden");
8462         this.el.setPositioning(config.resizeChild.getPositioning());
8463         config.resizeChild.clearPositioning();
8464         if(!config.width || !config.height){
8465             var csize = config.resizeChild.getSize();
8466             this.el.setSize(csize.width, csize.height);
8467         }
8468         if(config.pinned && !config.adjustments){
8469             config.adjustments = "auto";
8470         }
8471     }
8472
8473     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8474     this.proxy.unselectable();
8475     this.proxy.enableDisplayMode('block');
8476
8477     Roo.apply(this, config);
8478
8479     if(this.pinned){
8480         this.disableTrackOver = true;
8481         this.el.addClass("x-resizable-pinned");
8482     }
8483     // if the element isn't positioned, make it relative
8484     var position = this.el.getStyle("position");
8485     if(position != "absolute" && position != "fixed"){
8486         this.el.setStyle("position", "relative");
8487     }
8488     if(!this.handles){ // no handles passed, must be legacy style
8489         this.handles = 's,e,se';
8490         if(this.multiDirectional){
8491             this.handles += ',n,w';
8492         }
8493     }
8494     if(this.handles == "all"){
8495         this.handles = "n s e w ne nw se sw";
8496     }
8497     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8498     var ps = Roo.Resizable.positions;
8499     for(var i = 0, len = hs.length; i < len; i++){
8500         if(hs[i] && ps[hs[i]]){
8501             var pos = ps[hs[i]];
8502             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8503         }
8504     }
8505     // legacy
8506     this.corner = this.southeast;
8507     
8508     // updateBox = the box can move..
8509     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8510         this.updateBox = true;
8511     }
8512
8513     this.activeHandle = null;
8514
8515     if(this.resizeChild){
8516         if(typeof this.resizeChild == "boolean"){
8517             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8518         }else{
8519             this.resizeChild = Roo.get(this.resizeChild, true);
8520         }
8521     }
8522     
8523     if(this.adjustments == "auto"){
8524         var rc = this.resizeChild;
8525         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8526         if(rc && (hw || hn)){
8527             rc.position("relative");
8528             rc.setLeft(hw ? hw.el.getWidth() : 0);
8529             rc.setTop(hn ? hn.el.getHeight() : 0);
8530         }
8531         this.adjustments = [
8532             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8533             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8534         ];
8535     }
8536
8537     if(this.draggable){
8538         this.dd = this.dynamic ?
8539             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8540         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8541     }
8542
8543     // public events
8544     this.addEvents({
8545         /**
8546          * @event beforeresize
8547          * Fired before resize is allowed. Set enabled to false to cancel resize.
8548          * @param {Roo.Resizable} this
8549          * @param {Roo.EventObject} e The mousedown event
8550          */
8551         "beforeresize" : true,
8552         /**
8553          * @event resizing
8554          * Fired a resizing.
8555          * @param {Roo.Resizable} this
8556          * @param {Number} x The new x position
8557          * @param {Number} y The new y position
8558          * @param {Number} w The new w width
8559          * @param {Number} h The new h hight
8560          * @param {Roo.EventObject} e The mouseup event
8561          */
8562         "resizing" : true,
8563         /**
8564          * @event resize
8565          * Fired after a resize.
8566          * @param {Roo.Resizable} this
8567          * @param {Number} width The new width
8568          * @param {Number} height The new height
8569          * @param {Roo.EventObject} e The mouseup event
8570          */
8571         "resize" : true
8572     });
8573
8574     if(this.width !== null && this.height !== null){
8575         this.resizeTo(this.width, this.height);
8576     }else{
8577         this.updateChildSize();
8578     }
8579     if(Roo.isIE){
8580         this.el.dom.style.zoom = 1;
8581     }
8582     Roo.Resizable.superclass.constructor.call(this);
8583 };
8584
8585 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8586         resizeChild : false,
8587         adjustments : [0, 0],
8588         minWidth : 5,
8589         minHeight : 5,
8590         maxWidth : 10000,
8591         maxHeight : 10000,
8592         enabled : true,
8593         animate : false,
8594         duration : .35,
8595         dynamic : false,
8596         handles : false,
8597         multiDirectional : false,
8598         disableTrackOver : false,
8599         easing : 'easeOutStrong',
8600         widthIncrement : 0,
8601         heightIncrement : 0,
8602         pinned : false,
8603         width : null,
8604         height : null,
8605         preserveRatio : false,
8606         transparent: false,
8607         minX: 0,
8608         minY: 0,
8609         draggable: false,
8610
8611         /**
8612          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8613          */
8614         constrainTo: undefined,
8615         /**
8616          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8617          */
8618         resizeRegion: undefined,
8619
8620
8621     /**
8622      * Perform a manual resize
8623      * @param {Number} width
8624      * @param {Number} height
8625      */
8626     resizeTo : function(width, height){
8627         this.el.setSize(width, height);
8628         this.updateChildSize();
8629         this.fireEvent("resize", this, width, height, null);
8630     },
8631
8632     // private
8633     startSizing : function(e, handle){
8634         this.fireEvent("beforeresize", this, e);
8635         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8636
8637             if(!this.overlay){
8638                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8639                 this.overlay.unselectable();
8640                 this.overlay.enableDisplayMode("block");
8641                 this.overlay.on("mousemove", this.onMouseMove, this);
8642                 this.overlay.on("mouseup", this.onMouseUp, this);
8643             }
8644             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8645
8646             this.resizing = true;
8647             this.startBox = this.el.getBox();
8648             this.startPoint = e.getXY();
8649             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8650                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8651
8652             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8653             this.overlay.show();
8654
8655             if(this.constrainTo) {
8656                 var ct = Roo.get(this.constrainTo);
8657                 this.resizeRegion = ct.getRegion().adjust(
8658                     ct.getFrameWidth('t'),
8659                     ct.getFrameWidth('l'),
8660                     -ct.getFrameWidth('b'),
8661                     -ct.getFrameWidth('r')
8662                 );
8663             }
8664
8665             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8666             this.proxy.show();
8667             this.proxy.setBox(this.startBox);
8668             if(!this.dynamic){
8669                 this.proxy.setStyle('visibility', 'visible');
8670             }
8671         }
8672     },
8673
8674     // private
8675     onMouseDown : function(handle, e){
8676         if(this.enabled){
8677             e.stopEvent();
8678             this.activeHandle = handle;
8679             this.startSizing(e, handle);
8680         }
8681     },
8682
8683     // private
8684     onMouseUp : function(e){
8685         var size = this.resizeElement();
8686         this.resizing = false;
8687         this.handleOut();
8688         this.overlay.hide();
8689         this.proxy.hide();
8690         this.fireEvent("resize", this, size.width, size.height, e);
8691     },
8692
8693     // private
8694     updateChildSize : function(){
8695         
8696         if(this.resizeChild){
8697             var el = this.el;
8698             var child = this.resizeChild;
8699             var adj = this.adjustments;
8700             if(el.dom.offsetWidth){
8701                 var b = el.getSize(true);
8702                 child.setSize(b.width+adj[0], b.height+adj[1]);
8703             }
8704             // Second call here for IE
8705             // The first call enables instant resizing and
8706             // the second call corrects scroll bars if they
8707             // exist
8708             if(Roo.isIE){
8709                 setTimeout(function(){
8710                     if(el.dom.offsetWidth){
8711                         var b = el.getSize(true);
8712                         child.setSize(b.width+adj[0], b.height+adj[1]);
8713                     }
8714                 }, 10);
8715             }
8716         }
8717     },
8718
8719     // private
8720     snap : function(value, inc, min){
8721         if(!inc || !value) {
8722             return value;
8723         }
8724         var newValue = value;
8725         var m = value % inc;
8726         if(m > 0){
8727             if(m > (inc/2)){
8728                 newValue = value + (inc-m);
8729             }else{
8730                 newValue = value - m;
8731             }
8732         }
8733         return Math.max(min, newValue);
8734     },
8735
8736     // private
8737     resizeElement : function(){
8738         var box = this.proxy.getBox();
8739         if(this.updateBox){
8740             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8741         }else{
8742             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8743         }
8744         this.updateChildSize();
8745         if(!this.dynamic){
8746             this.proxy.hide();
8747         }
8748         return box;
8749     },
8750
8751     // private
8752     constrain : function(v, diff, m, mx){
8753         if(v - diff < m){
8754             diff = v - m;
8755         }else if(v - diff > mx){
8756             diff = mx - v;
8757         }
8758         return diff;
8759     },
8760
8761     // private
8762     onMouseMove : function(e){
8763         
8764         if(this.enabled){
8765             try{// try catch so if something goes wrong the user doesn't get hung
8766
8767             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8768                 return;
8769             }
8770
8771             //var curXY = this.startPoint;
8772             var curSize = this.curSize || this.startBox;
8773             var x = this.startBox.x, y = this.startBox.y;
8774             var ox = x, oy = y;
8775             var w = curSize.width, h = curSize.height;
8776             var ow = w, oh = h;
8777             var mw = this.minWidth, mh = this.minHeight;
8778             var mxw = this.maxWidth, mxh = this.maxHeight;
8779             var wi = this.widthIncrement;
8780             var hi = this.heightIncrement;
8781
8782             var eventXY = e.getXY();
8783             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8784             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8785
8786             var pos = this.activeHandle.position;
8787
8788             switch(pos){
8789                 case "east":
8790                     w += diffX;
8791                     w = Math.min(Math.max(mw, w), mxw);
8792                     break;
8793              
8794                 case "south":
8795                     h += diffY;
8796                     h = Math.min(Math.max(mh, h), mxh);
8797                     break;
8798                 case "southeast":
8799                     w += diffX;
8800                     h += diffY;
8801                     w = Math.min(Math.max(mw, w), mxw);
8802                     h = Math.min(Math.max(mh, h), mxh);
8803                     break;
8804                 case "north":
8805                     diffY = this.constrain(h, diffY, mh, mxh);
8806                     y += diffY;
8807                     h -= diffY;
8808                     break;
8809                 case "hdrag":
8810                     
8811                     if (wi) {
8812                         var adiffX = Math.abs(diffX);
8813                         var sub = (adiffX % wi); // how much 
8814                         if (sub > (wi/2)) { // far enough to snap
8815                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8816                         } else {
8817                             // remove difference.. 
8818                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8819                         }
8820                     }
8821                     x += diffX;
8822                     x = Math.max(this.minX, x);
8823                     break;
8824                 case "west":
8825                     diffX = this.constrain(w, diffX, mw, mxw);
8826                     x += diffX;
8827                     w -= diffX;
8828                     break;
8829                 case "northeast":
8830                     w += diffX;
8831                     w = Math.min(Math.max(mw, w), mxw);
8832                     diffY = this.constrain(h, diffY, mh, mxh);
8833                     y += diffY;
8834                     h -= diffY;
8835                     break;
8836                 case "northwest":
8837                     diffX = this.constrain(w, diffX, mw, mxw);
8838                     diffY = this.constrain(h, diffY, mh, mxh);
8839                     y += diffY;
8840                     h -= diffY;
8841                     x += diffX;
8842                     w -= diffX;
8843                     break;
8844                case "southwest":
8845                     diffX = this.constrain(w, diffX, mw, mxw);
8846                     h += diffY;
8847                     h = Math.min(Math.max(mh, h), mxh);
8848                     x += diffX;
8849                     w -= diffX;
8850                     break;
8851             }
8852
8853             var sw = this.snap(w, wi, mw);
8854             var sh = this.snap(h, hi, mh);
8855             if(sw != w || sh != h){
8856                 switch(pos){
8857                     case "northeast":
8858                         y -= sh - h;
8859                     break;
8860                     case "north":
8861                         y -= sh - h;
8862                         break;
8863                     case "southwest":
8864                         x -= sw - w;
8865                     break;
8866                     case "west":
8867                         x -= sw - w;
8868                         break;
8869                     case "northwest":
8870                         x -= sw - w;
8871                         y -= sh - h;
8872                     break;
8873                 }
8874                 w = sw;
8875                 h = sh;
8876             }
8877
8878             if(this.preserveRatio){
8879                 switch(pos){
8880                     case "southeast":
8881                     case "east":
8882                         h = oh * (w/ow);
8883                         h = Math.min(Math.max(mh, h), mxh);
8884                         w = ow * (h/oh);
8885                        break;
8886                     case "south":
8887                         w = ow * (h/oh);
8888                         w = Math.min(Math.max(mw, w), mxw);
8889                         h = oh * (w/ow);
8890                         break;
8891                     case "northeast":
8892                         w = ow * (h/oh);
8893                         w = Math.min(Math.max(mw, w), mxw);
8894                         h = oh * (w/ow);
8895                     break;
8896                     case "north":
8897                         var tw = w;
8898                         w = ow * (h/oh);
8899                         w = Math.min(Math.max(mw, w), mxw);
8900                         h = oh * (w/ow);
8901                         x += (tw - w) / 2;
8902                         break;
8903                     case "southwest":
8904                         h = oh * (w/ow);
8905                         h = Math.min(Math.max(mh, h), mxh);
8906                         var tw = w;
8907                         w = ow * (h/oh);
8908                         x += tw - w;
8909                         break;
8910                     case "west":
8911                         var th = h;
8912                         h = oh * (w/ow);
8913                         h = Math.min(Math.max(mh, h), mxh);
8914                         y += (th - h) / 2;
8915                         var tw = w;
8916                         w = ow * (h/oh);
8917                         x += tw - w;
8918                        break;
8919                     case "northwest":
8920                         var tw = w;
8921                         var th = h;
8922                         h = oh * (w/ow);
8923                         h = Math.min(Math.max(mh, h), mxh);
8924                         w = ow * (h/oh);
8925                         y += th - h;
8926                         x += tw - w;
8927                        break;
8928
8929                 }
8930             }
8931             if (pos == 'hdrag') {
8932                 w = ow;
8933             }
8934             this.proxy.setBounds(x, y, w, h);
8935             if(this.dynamic){
8936                 this.resizeElement();
8937             }
8938             }catch(e){}
8939         }
8940         this.fireEvent("resizing", this, x, y, w, h, e);
8941     },
8942
8943     // private
8944     handleOver : function(){
8945         if(this.enabled){
8946             this.el.addClass("x-resizable-over");
8947         }
8948     },
8949
8950     // private
8951     handleOut : function(){
8952         if(!this.resizing){
8953             this.el.removeClass("x-resizable-over");
8954         }
8955     },
8956
8957     /**
8958      * Returns the element this component is bound to.
8959      * @return {Roo.Element}
8960      */
8961     getEl : function(){
8962         return this.el;
8963     },
8964
8965     /**
8966      * Returns the resizeChild element (or null).
8967      * @return {Roo.Element}
8968      */
8969     getResizeChild : function(){
8970         return this.resizeChild;
8971     },
8972     groupHandler : function()
8973     {
8974         
8975     },
8976     /**
8977      * Destroys this resizable. If the element was wrapped and
8978      * removeEl is not true then the element remains.
8979      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8980      */
8981     destroy : function(removeEl){
8982         this.proxy.remove();
8983         if(this.overlay){
8984             this.overlay.removeAllListeners();
8985             this.overlay.remove();
8986         }
8987         var ps = Roo.Resizable.positions;
8988         for(var k in ps){
8989             if(typeof ps[k] != "function" && this[ps[k]]){
8990                 var h = this[ps[k]];
8991                 h.el.removeAllListeners();
8992                 h.el.remove();
8993             }
8994         }
8995         if(removeEl){
8996             this.el.update("");
8997             this.el.remove();
8998         }
8999     }
9000 });
9001
9002 // private
9003 // hash to map config positions to true positions
9004 Roo.Resizable.positions = {
9005     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
9006     hd: "hdrag"
9007 };
9008
9009 // private
9010 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
9011     if(!this.tpl){
9012         // only initialize the template if resizable is used
9013         var tpl = Roo.DomHelper.createTemplate(
9014             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9015         );
9016         tpl.compile();
9017         Roo.Resizable.Handle.prototype.tpl = tpl;
9018     }
9019     this.position = pos;
9020     this.rz = rz;
9021     // show north drag fro topdra
9022     var handlepos = pos == 'hdrag' ? 'north' : pos;
9023     
9024     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9025     if (pos == 'hdrag') {
9026         this.el.setStyle('cursor', 'pointer');
9027     }
9028     this.el.unselectable();
9029     if(transparent){
9030         this.el.setOpacity(0);
9031     }
9032     this.el.on("mousedown", this.onMouseDown, this);
9033     if(!disableTrackOver){
9034         this.el.on("mouseover", this.onMouseOver, this);
9035         this.el.on("mouseout", this.onMouseOut, this);
9036     }
9037 };
9038
9039 // private
9040 Roo.Resizable.Handle.prototype = {
9041     afterResize : function(rz){
9042         Roo.log('after?');
9043         // do nothing
9044     },
9045     // private
9046     onMouseDown : function(e){
9047         this.rz.onMouseDown(this, e);
9048     },
9049     // private
9050     onMouseOver : function(e){
9051         this.rz.handleOver(this, e);
9052     },
9053     // private
9054     onMouseOut : function(e){
9055         this.rz.handleOut(this, e);
9056     }
9057 };/*
9058  * Based on:
9059  * Ext JS Library 1.1.1
9060  * Copyright(c) 2006-2007, Ext JS, LLC.
9061  *
9062  * Originally Released Under LGPL - original licence link has changed is not relivant.
9063  *
9064  * Fork - LGPL
9065  * <script type="text/javascript">
9066  */
9067
9068 /**
9069  * @class Roo.Editor
9070  * @extends Roo.Component
9071  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9072  * @constructor
9073  * Create a new Editor
9074  * @param {Roo.form.Field} field The Field object (or descendant)
9075  * @param {Object} config The config object
9076  */
9077 Roo.Editor = function(field, config){
9078     Roo.Editor.superclass.constructor.call(this, config);
9079     this.field = field;
9080     this.addEvents({
9081         /**
9082              * @event beforestartedit
9083              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9084              * false from the handler of this event.
9085              * @param {Editor} this
9086              * @param {Roo.Element} boundEl The underlying element bound to this editor
9087              * @param {Mixed} value The field value being set
9088              */
9089         "beforestartedit" : true,
9090         /**
9091              * @event startedit
9092              * Fires when this editor is displayed
9093              * @param {Roo.Element} boundEl The underlying element bound to this editor
9094              * @param {Mixed} value The starting field value
9095              */
9096         "startedit" : true,
9097         /**
9098              * @event beforecomplete
9099              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9100              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9101              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9102              * event will not fire since no edit actually occurred.
9103              * @param {Editor} this
9104              * @param {Mixed} value The current field value
9105              * @param {Mixed} startValue The original field value
9106              */
9107         "beforecomplete" : true,
9108         /**
9109              * @event complete
9110              * Fires after editing is complete and any changed value has been written to the underlying field.
9111              * @param {Editor} this
9112              * @param {Mixed} value The current field value
9113              * @param {Mixed} startValue The original field value
9114              */
9115         "complete" : true,
9116         /**
9117          * @event specialkey
9118          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9119          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9120          * @param {Roo.form.Field} this
9121          * @param {Roo.EventObject} e The event object
9122          */
9123         "specialkey" : true
9124     });
9125 };
9126
9127 Roo.extend(Roo.Editor, Roo.Component, {
9128     /**
9129      * @cfg {Boolean/String} autosize
9130      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9131      * or "height" to adopt the height only (defaults to false)
9132      */
9133     /**
9134      * @cfg {Boolean} revertInvalid
9135      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9136      * validation fails (defaults to true)
9137      */
9138     /**
9139      * @cfg {Boolean} ignoreNoChange
9140      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9141      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9142      * will never be ignored.
9143      */
9144     /**
9145      * @cfg {Boolean} hideEl
9146      * False to keep the bound element visible while the editor is displayed (defaults to true)
9147      */
9148     /**
9149      * @cfg {Mixed} value
9150      * The data value of the underlying field (defaults to "")
9151      */
9152     value : "",
9153     /**
9154      * @cfg {String} alignment
9155      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9156      */
9157     alignment: "c-c?",
9158     /**
9159      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9160      * for bottom-right shadow (defaults to "frame")
9161      */
9162     shadow : "frame",
9163     /**
9164      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9165      */
9166     constrain : false,
9167     /**
9168      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9169      */
9170     completeOnEnter : false,
9171     /**
9172      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9173      */
9174     cancelOnEsc : false,
9175     /**
9176      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9177      */
9178     updateEl : false,
9179
9180     // private
9181     onRender : function(ct, position){
9182         this.el = new Roo.Layer({
9183             shadow: this.shadow,
9184             cls: "x-editor",
9185             parentEl : ct,
9186             shim : this.shim,
9187             shadowOffset:4,
9188             id: this.id,
9189             constrain: this.constrain
9190         });
9191         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9192         if(this.field.msgTarget != 'title'){
9193             this.field.msgTarget = 'qtip';
9194         }
9195         this.field.render(this.el);
9196         if(Roo.isGecko){
9197             this.field.el.dom.setAttribute('autocomplete', 'off');
9198         }
9199         this.field.on("specialkey", this.onSpecialKey, this);
9200         if(this.swallowKeys){
9201             this.field.el.swallowEvent(['keydown','keypress']);
9202         }
9203         this.field.show();
9204         this.field.on("blur", this.onBlur, this);
9205         if(this.field.grow){
9206             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9207         }
9208     },
9209
9210     onSpecialKey : function(field, e)
9211     {
9212         //Roo.log('editor onSpecialKey');
9213         if(this.completeOnEnter && e.getKey() == e.ENTER){
9214             e.stopEvent();
9215             this.completeEdit();
9216             return;
9217         }
9218         // do not fire special key otherwise it might hide close the editor...
9219         if(e.getKey() == e.ENTER){    
9220             return;
9221         }
9222         if(this.cancelOnEsc && e.getKey() == e.ESC){
9223             this.cancelEdit();
9224             return;
9225         } 
9226         this.fireEvent('specialkey', field, e);
9227     
9228     },
9229
9230     /**
9231      * Starts the editing process and shows the editor.
9232      * @param {String/HTMLElement/Element} el The element to edit
9233      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9234       * to the innerHTML of el.
9235      */
9236     startEdit : function(el, value){
9237         if(this.editing){
9238             this.completeEdit();
9239         }
9240         this.boundEl = Roo.get(el);
9241         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9242         if(!this.rendered){
9243             this.render(this.parentEl || document.body);
9244         }
9245         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9246             return;
9247         }
9248         this.startValue = v;
9249         this.field.setValue(v);
9250         if(this.autoSize){
9251             var sz = this.boundEl.getSize();
9252             switch(this.autoSize){
9253                 case "width":
9254                 this.setSize(sz.width,  "");
9255                 break;
9256                 case "height":
9257                 this.setSize("",  sz.height);
9258                 break;
9259                 default:
9260                 this.setSize(sz.width,  sz.height);
9261             }
9262         }
9263         this.el.alignTo(this.boundEl, this.alignment);
9264         this.editing = true;
9265         if(Roo.QuickTips){
9266             Roo.QuickTips.disable();
9267         }
9268         this.show();
9269     },
9270
9271     /**
9272      * Sets the height and width of this editor.
9273      * @param {Number} width The new width
9274      * @param {Number} height The new height
9275      */
9276     setSize : function(w, h){
9277         this.field.setSize(w, h);
9278         if(this.el){
9279             this.el.sync();
9280         }
9281     },
9282
9283     /**
9284      * Realigns the editor to the bound field based on the current alignment config value.
9285      */
9286     realign : function(){
9287         this.el.alignTo(this.boundEl, this.alignment);
9288     },
9289
9290     /**
9291      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9292      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9293      */
9294     completeEdit : function(remainVisible){
9295         if(!this.editing){
9296             return;
9297         }
9298         var v = this.getValue();
9299         if(this.revertInvalid !== false && !this.field.isValid()){
9300             v = this.startValue;
9301             this.cancelEdit(true);
9302         }
9303         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9304             this.editing = false;
9305             this.hide();
9306             return;
9307         }
9308         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9309             this.editing = false;
9310             if(this.updateEl && this.boundEl){
9311                 this.boundEl.update(v);
9312             }
9313             if(remainVisible !== true){
9314                 this.hide();
9315             }
9316             this.fireEvent("complete", this, v, this.startValue);
9317         }
9318     },
9319
9320     // private
9321     onShow : function(){
9322         this.el.show();
9323         if(this.hideEl !== false){
9324             this.boundEl.hide();
9325         }
9326         this.field.show();
9327         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9328             this.fixIEFocus = true;
9329             this.deferredFocus.defer(50, this);
9330         }else{
9331             this.field.focus();
9332         }
9333         this.fireEvent("startedit", this.boundEl, this.startValue);
9334     },
9335
9336     deferredFocus : function(){
9337         if(this.editing){
9338             this.field.focus();
9339         }
9340     },
9341
9342     /**
9343      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9344      * reverted to the original starting value.
9345      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9346      * cancel (defaults to false)
9347      */
9348     cancelEdit : function(remainVisible){
9349         if(this.editing){
9350             this.setValue(this.startValue);
9351             if(remainVisible !== true){
9352                 this.hide();
9353             }
9354         }
9355     },
9356
9357     // private
9358     onBlur : function(){
9359         if(this.allowBlur !== true && this.editing){
9360             this.completeEdit();
9361         }
9362     },
9363
9364     // private
9365     onHide : function(){
9366         if(this.editing){
9367             this.completeEdit();
9368             return;
9369         }
9370         this.field.blur();
9371         if(this.field.collapse){
9372             this.field.collapse();
9373         }
9374         this.el.hide();
9375         if(this.hideEl !== false){
9376             this.boundEl.show();
9377         }
9378         if(Roo.QuickTips){
9379             Roo.QuickTips.enable();
9380         }
9381     },
9382
9383     /**
9384      * Sets the data value of the editor
9385      * @param {Mixed} value Any valid value supported by the underlying field
9386      */
9387     setValue : function(v){
9388         this.field.setValue(v);
9389     },
9390
9391     /**
9392      * Gets the data value of the editor
9393      * @return {Mixed} The data value
9394      */
9395     getValue : function(){
9396         return this.field.getValue();
9397     }
9398 });/*
9399  * Based on:
9400  * Ext JS Library 1.1.1
9401  * Copyright(c) 2006-2007, Ext JS, LLC.
9402  *
9403  * Originally Released Under LGPL - original licence link has changed is not relivant.
9404  *
9405  * Fork - LGPL
9406  * <script type="text/javascript">
9407  */
9408  
9409 /**
9410  * @class Roo.BasicDialog
9411  * @extends Roo.util.Observable
9412  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9413  * <pre><code>
9414 var dlg = new Roo.BasicDialog("my-dlg", {
9415     height: 200,
9416     width: 300,
9417     minHeight: 100,
9418     minWidth: 150,
9419     modal: true,
9420     proxyDrag: true,
9421     shadow: true
9422 });
9423 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9424 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9425 dlg.addButton('Cancel', dlg.hide, dlg);
9426 dlg.show();
9427 </code></pre>
9428   <b>A Dialog should always be a direct child of the body element.</b>
9429  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9430  * @cfg {String} title Default text to display in the title bar (defaults to null)
9431  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9432  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9433  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9434  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9435  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9436  * (defaults to null with no animation)
9437  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9438  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9439  * property for valid values (defaults to 'all')
9440  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9441  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9442  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9443  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9444  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9445  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9446  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9447  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9448  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9449  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9450  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9451  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9452  * draggable = true (defaults to false)
9453  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9454  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9455  * shadow (defaults to false)
9456  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9457  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9458  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9459  * @cfg {Array} buttons Array of buttons
9460  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9461  * @constructor
9462  * Create a new BasicDialog.
9463  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9464  * @param {Object} config Configuration options
9465  */
9466 Roo.BasicDialog = function(el, config){
9467     this.el = Roo.get(el);
9468     var dh = Roo.DomHelper;
9469     if(!this.el && config && config.autoCreate){
9470         if(typeof config.autoCreate == "object"){
9471             if(!config.autoCreate.id){
9472                 config.autoCreate.id = el;
9473             }
9474             this.el = dh.append(document.body,
9475                         config.autoCreate, true);
9476         }else{
9477             this.el = dh.append(document.body,
9478                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9479         }
9480     }
9481     el = this.el;
9482     el.setDisplayed(true);
9483     el.hide = this.hideAction;
9484     this.id = el.id;
9485     el.addClass("x-dlg");
9486
9487     Roo.apply(this, config);
9488
9489     this.proxy = el.createProxy("x-dlg-proxy");
9490     this.proxy.hide = this.hideAction;
9491     this.proxy.setOpacity(.5);
9492     this.proxy.hide();
9493
9494     if(config.width){
9495         el.setWidth(config.width);
9496     }
9497     if(config.height){
9498         el.setHeight(config.height);
9499     }
9500     this.size = el.getSize();
9501     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9502         this.xy = [config.x,config.y];
9503     }else{
9504         this.xy = el.getCenterXY(true);
9505     }
9506     /** The header element @type Roo.Element */
9507     this.header = el.child("> .x-dlg-hd");
9508     /** The body element @type Roo.Element */
9509     this.body = el.child("> .x-dlg-bd");
9510     /** The footer element @type Roo.Element */
9511     this.footer = el.child("> .x-dlg-ft");
9512
9513     if(!this.header){
9514         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9515     }
9516     if(!this.body){
9517         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9518     }
9519
9520     this.header.unselectable();
9521     if(this.title){
9522         this.header.update(this.title);
9523     }
9524     // this element allows the dialog to be focused for keyboard event
9525     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9526     this.focusEl.swallowEvent("click", true);
9527
9528     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9529
9530     // wrap the body and footer for special rendering
9531     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9532     if(this.footer){
9533         this.bwrap.dom.appendChild(this.footer.dom);
9534     }
9535
9536     this.bg = this.el.createChild({
9537         tag: "div", cls:"x-dlg-bg",
9538         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9539     });
9540     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9541
9542
9543     if(this.autoScroll !== false && !this.autoTabs){
9544         this.body.setStyle("overflow", "auto");
9545     }
9546
9547     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9548
9549     if(this.closable !== false){
9550         this.el.addClass("x-dlg-closable");
9551         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9552         this.close.on("click", this.closeClick, this);
9553         this.close.addClassOnOver("x-dlg-close-over");
9554     }
9555     if(this.collapsible !== false){
9556         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9557         this.collapseBtn.on("click", this.collapseClick, this);
9558         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9559         this.header.on("dblclick", this.collapseClick, this);
9560     }
9561     if(this.resizable !== false){
9562         this.el.addClass("x-dlg-resizable");
9563         this.resizer = new Roo.Resizable(el, {
9564             minWidth: this.minWidth || 80,
9565             minHeight:this.minHeight || 80,
9566             handles: this.resizeHandles || "all",
9567             pinned: true
9568         });
9569         this.resizer.on("beforeresize", this.beforeResize, this);
9570         this.resizer.on("resize", this.onResize, this);
9571     }
9572     if(this.draggable !== false){
9573         el.addClass("x-dlg-draggable");
9574         if (!this.proxyDrag) {
9575             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9576         }
9577         else {
9578             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9579         }
9580         dd.setHandleElId(this.header.id);
9581         dd.endDrag = this.endMove.createDelegate(this);
9582         dd.startDrag = this.startMove.createDelegate(this);
9583         dd.onDrag = this.onDrag.createDelegate(this);
9584         dd.scroll = false;
9585         this.dd = dd;
9586     }
9587     if(this.modal){
9588         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9589         this.mask.enableDisplayMode("block");
9590         this.mask.hide();
9591         this.el.addClass("x-dlg-modal");
9592     }
9593     if(this.shadow){
9594         this.shadow = new Roo.Shadow({
9595             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9596             offset : this.shadowOffset
9597         });
9598     }else{
9599         this.shadowOffset = 0;
9600     }
9601     if(Roo.useShims && this.shim !== false){
9602         this.shim = this.el.createShim();
9603         this.shim.hide = this.hideAction;
9604         this.shim.hide();
9605     }else{
9606         this.shim = false;
9607     }
9608     if(this.autoTabs){
9609         this.initTabs();
9610     }
9611     if (this.buttons) { 
9612         var bts= this.buttons;
9613         this.buttons = [];
9614         Roo.each(bts, function(b) {
9615             this.addButton(b);
9616         }, this);
9617     }
9618     
9619     
9620     this.addEvents({
9621         /**
9622          * @event keydown
9623          * Fires when a key is pressed
9624          * @param {Roo.BasicDialog} this
9625          * @param {Roo.EventObject} e
9626          */
9627         "keydown" : true,
9628         /**
9629          * @event move
9630          * Fires when this dialog is moved by the user.
9631          * @param {Roo.BasicDialog} this
9632          * @param {Number} x The new page X
9633          * @param {Number} y The new page Y
9634          */
9635         "move" : true,
9636         /**
9637          * @event resize
9638          * Fires when this dialog is resized by the user.
9639          * @param {Roo.BasicDialog} this
9640          * @param {Number} width The new width
9641          * @param {Number} height The new height
9642          */
9643         "resize" : true,
9644         /**
9645          * @event beforehide
9646          * Fires before this dialog is hidden.
9647          * @param {Roo.BasicDialog} this
9648          */
9649         "beforehide" : true,
9650         /**
9651          * @event hide
9652          * Fires when this dialog is hidden.
9653          * @param {Roo.BasicDialog} this
9654          */
9655         "hide" : true,
9656         /**
9657          * @event beforeshow
9658          * Fires before this dialog is shown.
9659          * @param {Roo.BasicDialog} this
9660          */
9661         "beforeshow" : true,
9662         /**
9663          * @event show
9664          * Fires when this dialog is shown.
9665          * @param {Roo.BasicDialog} this
9666          */
9667         "show" : true
9668     });
9669     el.on("keydown", this.onKeyDown, this);
9670     el.on("mousedown", this.toFront, this);
9671     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9672     this.el.hide();
9673     Roo.DialogManager.register(this);
9674     Roo.BasicDialog.superclass.constructor.call(this);
9675 };
9676
9677 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9678     shadowOffset: Roo.isIE ? 6 : 5,
9679     minHeight: 80,
9680     minWidth: 200,
9681     minButtonWidth: 75,
9682     defaultButton: null,
9683     buttonAlign: "right",
9684     tabTag: 'div',
9685     firstShow: true,
9686
9687     /**
9688      * Sets the dialog title text
9689      * @param {String} text The title text to display
9690      * @return {Roo.BasicDialog} this
9691      */
9692     setTitle : function(text){
9693         this.header.update(text);
9694         return this;
9695     },
9696
9697     // private
9698     closeClick : function(){
9699         this.hide();
9700     },
9701
9702     // private
9703     collapseClick : function(){
9704         this[this.collapsed ? "expand" : "collapse"]();
9705     },
9706
9707     /**
9708      * Collapses the dialog to its minimized state (only the title bar is visible).
9709      * Equivalent to the user clicking the collapse dialog button.
9710      */
9711     collapse : function(){
9712         if(!this.collapsed){
9713             this.collapsed = true;
9714             this.el.addClass("x-dlg-collapsed");
9715             this.restoreHeight = this.el.getHeight();
9716             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9717         }
9718     },
9719
9720     /**
9721      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9722      * clicking the expand dialog button.
9723      */
9724     expand : function(){
9725         if(this.collapsed){
9726             this.collapsed = false;
9727             this.el.removeClass("x-dlg-collapsed");
9728             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9729         }
9730     },
9731
9732     /**
9733      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9734      * @return {Roo.TabPanel} The tabs component
9735      */
9736     initTabs : function(){
9737         var tabs = this.getTabs();
9738         while(tabs.getTab(0)){
9739             tabs.removeTab(0);
9740         }
9741         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9742             var dom = el.dom;
9743             tabs.addTab(Roo.id(dom), dom.title);
9744             dom.title = "";
9745         });
9746         tabs.activate(0);
9747         return tabs;
9748     },
9749
9750     // private
9751     beforeResize : function(){
9752         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9753     },
9754
9755     // private
9756     onResize : function(){
9757         this.refreshSize();
9758         this.syncBodyHeight();
9759         this.adjustAssets();
9760         this.focus();
9761         this.fireEvent("resize", this, this.size.width, this.size.height);
9762     },
9763
9764     // private
9765     onKeyDown : function(e){
9766         if(this.isVisible()){
9767             this.fireEvent("keydown", this, e);
9768         }
9769     },
9770
9771     /**
9772      * Resizes the dialog.
9773      * @param {Number} width
9774      * @param {Number} height
9775      * @return {Roo.BasicDialog} this
9776      */
9777     resizeTo : function(width, height){
9778         this.el.setSize(width, height);
9779         this.size = {width: width, height: height};
9780         this.syncBodyHeight();
9781         if(this.fixedcenter){
9782             this.center();
9783         }
9784         if(this.isVisible()){
9785             this.constrainXY();
9786             this.adjustAssets();
9787         }
9788         this.fireEvent("resize", this, width, height);
9789         return this;
9790     },
9791
9792
9793     /**
9794      * Resizes the dialog to fit the specified content size.
9795      * @param {Number} width
9796      * @param {Number} height
9797      * @return {Roo.BasicDialog} this
9798      */
9799     setContentSize : function(w, h){
9800         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9801         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9802         //if(!this.el.isBorderBox()){
9803             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9804             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9805         //}
9806         if(this.tabs){
9807             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9808             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9809         }
9810         this.resizeTo(w, h);
9811         return this;
9812     },
9813
9814     /**
9815      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9816      * executed in response to a particular key being pressed while the dialog is active.
9817      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9818      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9819      * @param {Function} fn The function to call
9820      * @param {Object} scope (optional) The scope of the function
9821      * @return {Roo.BasicDialog} this
9822      */
9823     addKeyListener : function(key, fn, scope){
9824         var keyCode, shift, ctrl, alt;
9825         if(typeof key == "object" && !(key instanceof Array)){
9826             keyCode = key["key"];
9827             shift = key["shift"];
9828             ctrl = key["ctrl"];
9829             alt = key["alt"];
9830         }else{
9831             keyCode = key;
9832         }
9833         var handler = function(dlg, e){
9834             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9835                 var k = e.getKey();
9836                 if(keyCode instanceof Array){
9837                     for(var i = 0, len = keyCode.length; i < len; i++){
9838                         if(keyCode[i] == k){
9839                           fn.call(scope || window, dlg, k, e);
9840                           return;
9841                         }
9842                     }
9843                 }else{
9844                     if(k == keyCode){
9845                         fn.call(scope || window, dlg, k, e);
9846                     }
9847                 }
9848             }
9849         };
9850         this.on("keydown", handler);
9851         return this;
9852     },
9853
9854     /**
9855      * Returns the TabPanel component (creates it if it doesn't exist).
9856      * Note: If you wish to simply check for the existence of tabs without creating them,
9857      * check for a null 'tabs' property.
9858      * @return {Roo.TabPanel} The tabs component
9859      */
9860     getTabs : function(){
9861         if(!this.tabs){
9862             this.el.addClass("x-dlg-auto-tabs");
9863             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9864             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9865         }
9866         return this.tabs;
9867     },
9868
9869     /**
9870      * Adds a button to the footer section of the dialog.
9871      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9872      * object or a valid Roo.DomHelper element config
9873      * @param {Function} handler The function called when the button is clicked
9874      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9875      * @return {Roo.Button} The new button
9876      */
9877     addButton : function(config, handler, scope){
9878         var dh = Roo.DomHelper;
9879         if(!this.footer){
9880             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9881         }
9882         if(!this.btnContainer){
9883             var tb = this.footer.createChild({
9884
9885                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9886                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9887             }, null, true);
9888             this.btnContainer = tb.firstChild.firstChild.firstChild;
9889         }
9890         var bconfig = {
9891             handler: handler,
9892             scope: scope,
9893             minWidth: this.minButtonWidth,
9894             hideParent:true
9895         };
9896         if(typeof config == "string"){
9897             bconfig.text = config;
9898         }else{
9899             if(config.tag){
9900                 bconfig.dhconfig = config;
9901             }else{
9902                 Roo.apply(bconfig, config);
9903             }
9904         }
9905         var fc = false;
9906         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9907             bconfig.position = Math.max(0, bconfig.position);
9908             fc = this.btnContainer.childNodes[bconfig.position];
9909         }
9910          
9911         var btn = new Roo.Button(
9912             fc ? 
9913                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9914                 : this.btnContainer.appendChild(document.createElement("td")),
9915             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9916             bconfig
9917         );
9918         this.syncBodyHeight();
9919         if(!this.buttons){
9920             /**
9921              * Array of all the buttons that have been added to this dialog via addButton
9922              * @type Array
9923              */
9924             this.buttons = [];
9925         }
9926         this.buttons.push(btn);
9927         return btn;
9928     },
9929
9930     /**
9931      * Sets the default button to be focused when the dialog is displayed.
9932      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9933      * @return {Roo.BasicDialog} this
9934      */
9935     setDefaultButton : function(btn){
9936         this.defaultButton = btn;
9937         return this;
9938     },
9939
9940     // private
9941     getHeaderFooterHeight : function(safe){
9942         var height = 0;
9943         if(this.header){
9944            height += this.header.getHeight();
9945         }
9946         if(this.footer){
9947            var fm = this.footer.getMargins();
9948             height += (this.footer.getHeight()+fm.top+fm.bottom);
9949         }
9950         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9951         height += this.centerBg.getPadding("tb");
9952         return height;
9953     },
9954
9955     // private
9956     syncBodyHeight : function()
9957     {
9958         var bd = this.body, // the text
9959             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9960             bw = this.bwrap;
9961         var height = this.size.height - this.getHeaderFooterHeight(false);
9962         bd.setHeight(height-bd.getMargins("tb"));
9963         var hh = this.header.getHeight();
9964         var h = this.size.height-hh;
9965         cb.setHeight(h);
9966         
9967         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9968         bw.setHeight(h-cb.getPadding("tb"));
9969         
9970         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9971         bd.setWidth(bw.getWidth(true));
9972         if(this.tabs){
9973             this.tabs.syncHeight();
9974             if(Roo.isIE){
9975                 this.tabs.el.repaint();
9976             }
9977         }
9978     },
9979
9980     /**
9981      * Restores the previous state of the dialog if Roo.state is configured.
9982      * @return {Roo.BasicDialog} this
9983      */
9984     restoreState : function(){
9985         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9986         if(box && box.width){
9987             this.xy = [box.x, box.y];
9988             this.resizeTo(box.width, box.height);
9989         }
9990         return this;
9991     },
9992
9993     // private
9994     beforeShow : function(){
9995         this.expand();
9996         if(this.fixedcenter){
9997             this.xy = this.el.getCenterXY(true);
9998         }
9999         if(this.modal){
10000             Roo.get(document.body).addClass("x-body-masked");
10001             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10002             this.mask.show();
10003         }
10004         this.constrainXY();
10005     },
10006
10007     // private
10008     animShow : function(){
10009         var b = Roo.get(this.animateTarget).getBox();
10010         this.proxy.setSize(b.width, b.height);
10011         this.proxy.setLocation(b.x, b.y);
10012         this.proxy.show();
10013         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10014                     true, .35, this.showEl.createDelegate(this));
10015     },
10016
10017     /**
10018      * Shows the dialog.
10019      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10020      * @return {Roo.BasicDialog} this
10021      */
10022     show : function(animateTarget){
10023         if (this.fireEvent("beforeshow", this) === false){
10024             return;
10025         }
10026         if(this.syncHeightBeforeShow){
10027             this.syncBodyHeight();
10028         }else if(this.firstShow){
10029             this.firstShow = false;
10030             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10031         }
10032         this.animateTarget = animateTarget || this.animateTarget;
10033         if(!this.el.isVisible()){
10034             this.beforeShow();
10035             if(this.animateTarget && Roo.get(this.animateTarget)){
10036                 this.animShow();
10037             }else{
10038                 this.showEl();
10039             }
10040         }
10041         return this;
10042     },
10043
10044     // private
10045     showEl : function(){
10046         this.proxy.hide();
10047         this.el.setXY(this.xy);
10048         this.el.show();
10049         this.adjustAssets(true);
10050         this.toFront();
10051         this.focus();
10052         // IE peekaboo bug - fix found by Dave Fenwick
10053         if(Roo.isIE){
10054             this.el.repaint();
10055         }
10056         this.fireEvent("show", this);
10057     },
10058
10059     /**
10060      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10061      * dialog itself will receive focus.
10062      */
10063     focus : function(){
10064         if(this.defaultButton){
10065             this.defaultButton.focus();
10066         }else{
10067             this.focusEl.focus();
10068         }
10069     },
10070
10071     // private
10072     constrainXY : function(){
10073         if(this.constraintoviewport !== false){
10074             if(!this.viewSize){
10075                 if(this.container){
10076                     var s = this.container.getSize();
10077                     this.viewSize = [s.width, s.height];
10078                 }else{
10079                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10080                 }
10081             }
10082             var s = Roo.get(this.container||document).getScroll();
10083
10084             var x = this.xy[0], y = this.xy[1];
10085             var w = this.size.width, h = this.size.height;
10086             var vw = this.viewSize[0], vh = this.viewSize[1];
10087             // only move it if it needs it
10088             var moved = false;
10089             // first validate right/bottom
10090             if(x + w > vw+s.left){
10091                 x = vw - w;
10092                 moved = true;
10093             }
10094             if(y + h > vh+s.top){
10095                 y = vh - h;
10096                 moved = true;
10097             }
10098             // then make sure top/left isn't negative
10099             if(x < s.left){
10100                 x = s.left;
10101                 moved = true;
10102             }
10103             if(y < s.top){
10104                 y = s.top;
10105                 moved = true;
10106             }
10107             if(moved){
10108                 // cache xy
10109                 this.xy = [x, y];
10110                 if(this.isVisible()){
10111                     this.el.setLocation(x, y);
10112                     this.adjustAssets();
10113                 }
10114             }
10115         }
10116     },
10117
10118     // private
10119     onDrag : function(){
10120         if(!this.proxyDrag){
10121             this.xy = this.el.getXY();
10122             this.adjustAssets();
10123         }
10124     },
10125
10126     // private
10127     adjustAssets : function(doShow){
10128         var x = this.xy[0], y = this.xy[1];
10129         var w = this.size.width, h = this.size.height;
10130         if(doShow === true){
10131             if(this.shadow){
10132                 this.shadow.show(this.el);
10133             }
10134             if(this.shim){
10135                 this.shim.show();
10136             }
10137         }
10138         if(this.shadow && this.shadow.isVisible()){
10139             this.shadow.show(this.el);
10140         }
10141         if(this.shim && this.shim.isVisible()){
10142             this.shim.setBounds(x, y, w, h);
10143         }
10144     },
10145
10146     // private
10147     adjustViewport : function(w, h){
10148         if(!w || !h){
10149             w = Roo.lib.Dom.getViewWidth();
10150             h = Roo.lib.Dom.getViewHeight();
10151         }
10152         // cache the size
10153         this.viewSize = [w, h];
10154         if(this.modal && this.mask.isVisible()){
10155             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10156             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10157         }
10158         if(this.isVisible()){
10159             this.constrainXY();
10160         }
10161     },
10162
10163     /**
10164      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10165      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10166      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10167      */
10168     destroy : function(removeEl){
10169         if(this.isVisible()){
10170             this.animateTarget = null;
10171             this.hide();
10172         }
10173         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10174         if(this.tabs){
10175             this.tabs.destroy(removeEl);
10176         }
10177         Roo.destroy(
10178              this.shim,
10179              this.proxy,
10180              this.resizer,
10181              this.close,
10182              this.mask
10183         );
10184         if(this.dd){
10185             this.dd.unreg();
10186         }
10187         if(this.buttons){
10188            for(var i = 0, len = this.buttons.length; i < len; i++){
10189                this.buttons[i].destroy();
10190            }
10191         }
10192         this.el.removeAllListeners();
10193         if(removeEl === true){
10194             this.el.update("");
10195             this.el.remove();
10196         }
10197         Roo.DialogManager.unregister(this);
10198     },
10199
10200     // private
10201     startMove : function(){
10202         if(this.proxyDrag){
10203             this.proxy.show();
10204         }
10205         if(this.constraintoviewport !== false){
10206             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10207         }
10208     },
10209
10210     // private
10211     endMove : function(){
10212         if(!this.proxyDrag){
10213             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10214         }else{
10215             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10216             this.proxy.hide();
10217         }
10218         this.refreshSize();
10219         this.adjustAssets();
10220         this.focus();
10221         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10222     },
10223
10224     /**
10225      * Brings this dialog to the front of any other visible dialogs
10226      * @return {Roo.BasicDialog} this
10227      */
10228     toFront : function(){
10229         Roo.DialogManager.bringToFront(this);
10230         return this;
10231     },
10232
10233     /**
10234      * Sends this dialog to the back (under) of any other visible dialogs
10235      * @return {Roo.BasicDialog} this
10236      */
10237     toBack : function(){
10238         Roo.DialogManager.sendToBack(this);
10239         return this;
10240     },
10241
10242     /**
10243      * Centers this dialog in the viewport
10244      * @return {Roo.BasicDialog} this
10245      */
10246     center : function(){
10247         var xy = this.el.getCenterXY(true);
10248         this.moveTo(xy[0], xy[1]);
10249         return this;
10250     },
10251
10252     /**
10253      * Moves the dialog's top-left corner to the specified point
10254      * @param {Number} x
10255      * @param {Number} y
10256      * @return {Roo.BasicDialog} this
10257      */
10258     moveTo : function(x, y){
10259         this.xy = [x,y];
10260         if(this.isVisible()){
10261             this.el.setXY(this.xy);
10262             this.adjustAssets();
10263         }
10264         return this;
10265     },
10266
10267     /**
10268      * Aligns the dialog to the specified element
10269      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10270      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10271      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10272      * @return {Roo.BasicDialog} this
10273      */
10274     alignTo : function(element, position, offsets){
10275         this.xy = this.el.getAlignToXY(element, position, offsets);
10276         if(this.isVisible()){
10277             this.el.setXY(this.xy);
10278             this.adjustAssets();
10279         }
10280         return this;
10281     },
10282
10283     /**
10284      * Anchors an element to another element and realigns it when the window is resized.
10285      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10286      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10287      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10288      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10289      * is a number, it is used as the buffer delay (defaults to 50ms).
10290      * @return {Roo.BasicDialog} this
10291      */
10292     anchorTo : function(el, alignment, offsets, monitorScroll){
10293         var action = function(){
10294             this.alignTo(el, alignment, offsets);
10295         };
10296         Roo.EventManager.onWindowResize(action, this);
10297         var tm = typeof monitorScroll;
10298         if(tm != 'undefined'){
10299             Roo.EventManager.on(window, 'scroll', action, this,
10300                 {buffer: tm == 'number' ? monitorScroll : 50});
10301         }
10302         action.call(this);
10303         return this;
10304     },
10305
10306     /**
10307      * Returns true if the dialog is visible
10308      * @return {Boolean}
10309      */
10310     isVisible : function(){
10311         return this.el.isVisible();
10312     },
10313
10314     // private
10315     animHide : function(callback){
10316         var b = Roo.get(this.animateTarget).getBox();
10317         this.proxy.show();
10318         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10319         this.el.hide();
10320         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10321                     this.hideEl.createDelegate(this, [callback]));
10322     },
10323
10324     /**
10325      * Hides the dialog.
10326      * @param {Function} callback (optional) Function to call when the dialog is hidden
10327      * @return {Roo.BasicDialog} this
10328      */
10329     hide : function(callback){
10330         if (this.fireEvent("beforehide", this) === false){
10331             return;
10332         }
10333         if(this.shadow){
10334             this.shadow.hide();
10335         }
10336         if(this.shim) {
10337           this.shim.hide();
10338         }
10339         // sometimes animateTarget seems to get set.. causing problems...
10340         // this just double checks..
10341         if(this.animateTarget && Roo.get(this.animateTarget)) {
10342            this.animHide(callback);
10343         }else{
10344             this.el.hide();
10345             this.hideEl(callback);
10346         }
10347         return this;
10348     },
10349
10350     // private
10351     hideEl : function(callback){
10352         this.proxy.hide();
10353         if(this.modal){
10354             this.mask.hide();
10355             Roo.get(document.body).removeClass("x-body-masked");
10356         }
10357         this.fireEvent("hide", this);
10358         if(typeof callback == "function"){
10359             callback();
10360         }
10361     },
10362
10363     // private
10364     hideAction : function(){
10365         this.setLeft("-10000px");
10366         this.setTop("-10000px");
10367         this.setStyle("visibility", "hidden");
10368     },
10369
10370     // private
10371     refreshSize : function(){
10372         this.size = this.el.getSize();
10373         this.xy = this.el.getXY();
10374         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10375     },
10376
10377     // private
10378     // z-index is managed by the DialogManager and may be overwritten at any time
10379     setZIndex : function(index){
10380         if(this.modal){
10381             this.mask.setStyle("z-index", index);
10382         }
10383         if(this.shim){
10384             this.shim.setStyle("z-index", ++index);
10385         }
10386         if(this.shadow){
10387             this.shadow.setZIndex(++index);
10388         }
10389         this.el.setStyle("z-index", ++index);
10390         if(this.proxy){
10391             this.proxy.setStyle("z-index", ++index);
10392         }
10393         if(this.resizer){
10394             this.resizer.proxy.setStyle("z-index", ++index);
10395         }
10396
10397         this.lastZIndex = index;
10398     },
10399
10400     /**
10401      * Returns the element for this dialog
10402      * @return {Roo.Element} The underlying dialog Element
10403      */
10404     getEl : function(){
10405         return this.el;
10406     }
10407 });
10408
10409 /**
10410  * @class Roo.DialogManager
10411  * Provides global access to BasicDialogs that have been created and
10412  * support for z-indexing (layering) multiple open dialogs.
10413  */
10414 Roo.DialogManager = function(){
10415     var list = {};
10416     var accessList = [];
10417     var front = null;
10418
10419     // private
10420     var sortDialogs = function(d1, d2){
10421         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10422     };
10423
10424     // private
10425     var orderDialogs = function(){
10426         accessList.sort(sortDialogs);
10427         var seed = Roo.DialogManager.zseed;
10428         for(var i = 0, len = accessList.length; i < len; i++){
10429             var dlg = accessList[i];
10430             if(dlg){
10431                 dlg.setZIndex(seed + (i*10));
10432             }
10433         }
10434     };
10435
10436     return {
10437         /**
10438          * The starting z-index for BasicDialogs (defaults to 9000)
10439          * @type Number The z-index value
10440          */
10441         zseed : 9000,
10442
10443         // private
10444         register : function(dlg){
10445             list[dlg.id] = dlg;
10446             accessList.push(dlg);
10447         },
10448
10449         // private
10450         unregister : function(dlg){
10451             delete list[dlg.id];
10452             var i=0;
10453             var len=0;
10454             if(!accessList.indexOf){
10455                 for(  i = 0, len = accessList.length; i < len; i++){
10456                     if(accessList[i] == dlg){
10457                         accessList.splice(i, 1);
10458                         return;
10459                     }
10460                 }
10461             }else{
10462                  i = accessList.indexOf(dlg);
10463                 if(i != -1){
10464                     accessList.splice(i, 1);
10465                 }
10466             }
10467         },
10468
10469         /**
10470          * Gets a registered dialog by id
10471          * @param {String/Object} id The id of the dialog or a dialog
10472          * @return {Roo.BasicDialog} this
10473          */
10474         get : function(id){
10475             return typeof id == "object" ? id : list[id];
10476         },
10477
10478         /**
10479          * Brings the specified dialog to the front
10480          * @param {String/Object} dlg The id of the dialog or a dialog
10481          * @return {Roo.BasicDialog} this
10482          */
10483         bringToFront : function(dlg){
10484             dlg = this.get(dlg);
10485             if(dlg != front){
10486                 front = dlg;
10487                 dlg._lastAccess = new Date().getTime();
10488                 orderDialogs();
10489             }
10490             return dlg;
10491         },
10492
10493         /**
10494          * Sends the specified dialog to the back
10495          * @param {String/Object} dlg The id of the dialog or a dialog
10496          * @return {Roo.BasicDialog} this
10497          */
10498         sendToBack : function(dlg){
10499             dlg = this.get(dlg);
10500             dlg._lastAccess = -(new Date().getTime());
10501             orderDialogs();
10502             return dlg;
10503         },
10504
10505         /**
10506          * Hides all dialogs
10507          */
10508         hideAll : function(){
10509             for(var id in list){
10510                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10511                     list[id].hide();
10512                 }
10513             }
10514         }
10515     };
10516 }();
10517
10518 /**
10519  * @class Roo.LayoutDialog
10520  * @extends Roo.BasicDialog
10521  * Dialog which provides adjustments for working with a layout in a Dialog.
10522  * Add your necessary layout config options to the dialog's config.<br>
10523  * Example usage (including a nested layout):
10524  * <pre><code>
10525 if(!dialog){
10526     dialog = new Roo.LayoutDialog("download-dlg", {
10527         modal: true,
10528         width:600,
10529         height:450,
10530         shadow:true,
10531         minWidth:500,
10532         minHeight:350,
10533         autoTabs:true,
10534         proxyDrag:true,
10535         // layout config merges with the dialog config
10536         center:{
10537             tabPosition: "top",
10538             alwaysShowTabs: true
10539         }
10540     });
10541     dialog.addKeyListener(27, dialog.hide, dialog);
10542     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10543     dialog.addButton("Build It!", this.getDownload, this);
10544
10545     // we can even add nested layouts
10546     var innerLayout = new Roo.BorderLayout("dl-inner", {
10547         east: {
10548             initialSize: 200,
10549             autoScroll:true,
10550             split:true
10551         },
10552         center: {
10553             autoScroll:true
10554         }
10555     });
10556     innerLayout.beginUpdate();
10557     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10558     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10559     innerLayout.endUpdate(true);
10560
10561     var layout = dialog.getLayout();
10562     layout.beginUpdate();
10563     layout.add("center", new Roo.ContentPanel("standard-panel",
10564                         {title: "Download the Source", fitToFrame:true}));
10565     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10566                {title: "Build your own roo.js"}));
10567     layout.getRegion("center").showPanel(sp);
10568     layout.endUpdate();
10569 }
10570 </code></pre>
10571     * @constructor
10572     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10573     * @param {Object} config configuration options
10574   */
10575 Roo.LayoutDialog = function(el, cfg){
10576     
10577     var config=  cfg;
10578     if (typeof(cfg) == 'undefined') {
10579         config = Roo.apply({}, el);
10580         // not sure why we use documentElement here.. - it should always be body.
10581         // IE7 borks horribly if we use documentElement.
10582         // webkit also does not like documentElement - it creates a body element...
10583         el = Roo.get( document.body || document.documentElement ).createChild();
10584         //config.autoCreate = true;
10585     }
10586     
10587     
10588     config.autoTabs = false;
10589     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10590     this.body.setStyle({overflow:"hidden", position:"relative"});
10591     this.layout = new Roo.BorderLayout(this.body.dom, config);
10592     this.layout.monitorWindowResize = false;
10593     this.el.addClass("x-dlg-auto-layout");
10594     // fix case when center region overwrites center function
10595     this.center = Roo.BasicDialog.prototype.center;
10596     this.on("show", this.layout.layout, this.layout, true);
10597     if (config.items) {
10598         var xitems = config.items;
10599         delete config.items;
10600         Roo.each(xitems, this.addxtype, this);
10601     }
10602     
10603     
10604 };
10605 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10606     /**
10607      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10608      * @deprecated
10609      */
10610     endUpdate : function(){
10611         this.layout.endUpdate();
10612     },
10613
10614     /**
10615      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10616      *  @deprecated
10617      */
10618     beginUpdate : function(){
10619         this.layout.beginUpdate();
10620     },
10621
10622     /**
10623      * Get the BorderLayout for this dialog
10624      * @return {Roo.BorderLayout}
10625      */
10626     getLayout : function(){
10627         return this.layout;
10628     },
10629
10630     showEl : function(){
10631         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10632         if(Roo.isIE7){
10633             this.layout.layout();
10634         }
10635     },
10636
10637     // private
10638     // Use the syncHeightBeforeShow config option to control this automatically
10639     syncBodyHeight : function(){
10640         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10641         if(this.layout){this.layout.layout();}
10642     },
10643     
10644       /**
10645      * Add an xtype element (actually adds to the layout.)
10646      * @return {Object} xdata xtype object data.
10647      */
10648     
10649     addxtype : function(c) {
10650         return this.layout.addxtype(c);
10651     }
10652 });/*
10653  * Based on:
10654  * Ext JS Library 1.1.1
10655  * Copyright(c) 2006-2007, Ext JS, LLC.
10656  *
10657  * Originally Released Under LGPL - original licence link has changed is not relivant.
10658  *
10659  * Fork - LGPL
10660  * <script type="text/javascript">
10661  */
10662  
10663 /**
10664  * @class Roo.MessageBox
10665  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10666  * Example usage:
10667  *<pre><code>
10668 // Basic alert:
10669 Roo.Msg.alert('Status', 'Changes saved successfully.');
10670
10671 // Prompt for user data:
10672 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10673     if (btn == 'ok'){
10674         // process text value...
10675     }
10676 });
10677
10678 // Show a dialog using config options:
10679 Roo.Msg.show({
10680    title:'Save Changes?',
10681    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10682    buttons: Roo.Msg.YESNOCANCEL,
10683    fn: processResult,
10684    animEl: 'elId'
10685 });
10686 </code></pre>
10687  * @singleton
10688  */
10689 Roo.MessageBox = function(){
10690     var dlg, opt, mask, waitTimer;
10691     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10692     var buttons, activeTextEl, bwidth;
10693
10694     // private
10695     var handleButton = function(button){
10696         dlg.hide();
10697         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10698     };
10699
10700     // private
10701     var handleHide = function(){
10702         if(opt && opt.cls){
10703             dlg.el.removeClass(opt.cls);
10704         }
10705         if(waitTimer){
10706             Roo.TaskMgr.stop(waitTimer);
10707             waitTimer = null;
10708         }
10709     };
10710
10711     // private
10712     var updateButtons = function(b){
10713         var width = 0;
10714         if(!b){
10715             buttons["ok"].hide();
10716             buttons["cancel"].hide();
10717             buttons["yes"].hide();
10718             buttons["no"].hide();
10719             dlg.footer.dom.style.display = 'none';
10720             return width;
10721         }
10722         dlg.footer.dom.style.display = '';
10723         for(var k in buttons){
10724             if(typeof buttons[k] != "function"){
10725                 if(b[k]){
10726                     buttons[k].show();
10727                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10728                     width += buttons[k].el.getWidth()+15;
10729                 }else{
10730                     buttons[k].hide();
10731                 }
10732             }
10733         }
10734         return width;
10735     };
10736
10737     // private
10738     var handleEsc = function(d, k, e){
10739         if(opt && opt.closable !== false){
10740             dlg.hide();
10741         }
10742         if(e){
10743             e.stopEvent();
10744         }
10745     };
10746
10747     return {
10748         /**
10749          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10750          * @return {Roo.BasicDialog} The BasicDialog element
10751          */
10752         getDialog : function(){
10753            if(!dlg){
10754                 dlg = new Roo.BasicDialog("x-msg-box", {
10755                     autoCreate : true,
10756                     shadow: true,
10757                     draggable: true,
10758                     resizable:false,
10759                     constraintoviewport:false,
10760                     fixedcenter:true,
10761                     collapsible : false,
10762                     shim:true,
10763                     modal: true,
10764                     width:400, height:100,
10765                     buttonAlign:"center",
10766                     closeClick : function(){
10767                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10768                             handleButton("no");
10769                         }else{
10770                             handleButton("cancel");
10771                         }
10772                     }
10773                 });
10774                 dlg.on("hide", handleHide);
10775                 mask = dlg.mask;
10776                 dlg.addKeyListener(27, handleEsc);
10777                 buttons = {};
10778                 var bt = this.buttonText;
10779                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10780                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10781                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10782                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10783                 bodyEl = dlg.body.createChild({
10784
10785                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
10786                 });
10787                 msgEl = bodyEl.dom.firstChild;
10788                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10789                 textboxEl.enableDisplayMode();
10790                 textboxEl.addKeyListener([10,13], function(){
10791                     if(dlg.isVisible() && opt && opt.buttons){
10792                         if(opt.buttons.ok){
10793                             handleButton("ok");
10794                         }else if(opt.buttons.yes){
10795                             handleButton("yes");
10796                         }
10797                     }
10798                 });
10799                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10800                 textareaEl.enableDisplayMode();
10801                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10802                 progressEl.enableDisplayMode();
10803                 var pf = progressEl.dom.firstChild;
10804                 if (pf) {
10805                     pp = Roo.get(pf.firstChild);
10806                     pp.setHeight(pf.offsetHeight);
10807                 }
10808                 
10809             }
10810             return dlg;
10811         },
10812
10813         /**
10814          * Updates the message box body text
10815          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10816          * the XHTML-compliant non-breaking space character '&amp;#160;')
10817          * @return {Roo.MessageBox} This message box
10818          */
10819         updateText : function(text){
10820             if(!dlg.isVisible() && !opt.width){
10821                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10822             }
10823             msgEl.innerHTML = text || '&#160;';
10824       
10825             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10826             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10827             var w = Math.max(
10828                     Math.min(opt.width || cw , this.maxWidth), 
10829                     Math.max(opt.minWidth || this.minWidth, bwidth)
10830             );
10831             if(opt.prompt){
10832                 activeTextEl.setWidth(w);
10833             }
10834             if(dlg.isVisible()){
10835                 dlg.fixedcenter = false;
10836             }
10837             // to big, make it scroll. = But as usual stupid IE does not support
10838             // !important..
10839             
10840             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10841                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10842                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10843             } else {
10844                 bodyEl.dom.style.height = '';
10845                 bodyEl.dom.style.overflowY = '';
10846             }
10847             if (cw > w) {
10848                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10849             } else {
10850                 bodyEl.dom.style.overflowX = '';
10851             }
10852             
10853             dlg.setContentSize(w, bodyEl.getHeight());
10854             if(dlg.isVisible()){
10855                 dlg.fixedcenter = true;
10856             }
10857             return this;
10858         },
10859
10860         /**
10861          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10862          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10863          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10864          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10865          * @return {Roo.MessageBox} This message box
10866          */
10867         updateProgress : function(value, text){
10868             if(text){
10869                 this.updateText(text);
10870             }
10871             if (pp) { // weird bug on my firefox - for some reason this is not defined
10872                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10873             }
10874             return this;
10875         },        
10876
10877         /**
10878          * Returns true if the message box is currently displayed
10879          * @return {Boolean} True if the message box is visible, else false
10880          */
10881         isVisible : function(){
10882             return dlg && dlg.isVisible();  
10883         },
10884
10885         /**
10886          * Hides the message box if it is displayed
10887          */
10888         hide : function(){
10889             if(this.isVisible()){
10890                 dlg.hide();
10891             }  
10892         },
10893
10894         /**
10895          * Displays a new message box, or reinitializes an existing message box, based on the config options
10896          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10897          * The following config object properties are supported:
10898          * <pre>
10899 Property    Type             Description
10900 ----------  ---------------  ------------------------------------------------------------------------------------
10901 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10902                                    closes (defaults to undefined)
10903 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10904                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10905 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10906                                    progress and wait dialogs will ignore this property and always hide the
10907                                    close button as they can only be closed programmatically.
10908 cls               String           A custom CSS class to apply to the message box element
10909 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10910                                    displayed (defaults to 75)
10911 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10912                                    function will be btn (the name of the button that was clicked, if applicable,
10913                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10914                                    Progress and wait dialogs will ignore this option since they do not respond to
10915                                    user actions and can only be closed programmatically, so any required function
10916                                    should be called by the same code after it closes the dialog.
10917 icon              String           A CSS class that provides a background image to be used as an icon for
10918                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10919 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10920 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10921 modal             Boolean          False to allow user interaction with the page while the message box is
10922                                    displayed (defaults to true)
10923 msg               String           A string that will replace the existing message box body text (defaults
10924                                    to the XHTML-compliant non-breaking space character '&#160;')
10925 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10926 progress          Boolean          True to display a progress bar (defaults to false)
10927 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10928 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10929 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10930 title             String           The title text
10931 value             String           The string value to set into the active textbox element if displayed
10932 wait              Boolean          True to display a progress bar (defaults to false)
10933 width             Number           The width of the dialog in pixels
10934 </pre>
10935          *
10936          * Example usage:
10937          * <pre><code>
10938 Roo.Msg.show({
10939    title: 'Address',
10940    msg: 'Please enter your address:',
10941    width: 300,
10942    buttons: Roo.MessageBox.OKCANCEL,
10943    multiline: true,
10944    fn: saveAddress,
10945    animEl: 'addAddressBtn'
10946 });
10947 </code></pre>
10948          * @param {Object} config Configuration options
10949          * @return {Roo.MessageBox} This message box
10950          */
10951         show : function(options)
10952         {
10953             
10954             // this causes nightmares if you show one dialog after another
10955             // especially on callbacks..
10956              
10957             if(this.isVisible()){
10958                 
10959                 this.hide();
10960                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10961                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10962                 Roo.log("New Dialog Message:" +  options.msg )
10963                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10964                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10965                 
10966             }
10967             var d = this.getDialog();
10968             opt = options;
10969             d.setTitle(opt.title || "&#160;");
10970             d.close.setDisplayed(opt.closable !== false);
10971             activeTextEl = textboxEl;
10972             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10973             if(opt.prompt){
10974                 if(opt.multiline){
10975                     textboxEl.hide();
10976                     textareaEl.show();
10977                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10978                         opt.multiline : this.defaultTextHeight);
10979                     activeTextEl = textareaEl;
10980                 }else{
10981                     textboxEl.show();
10982                     textareaEl.hide();
10983                 }
10984             }else{
10985                 textboxEl.hide();
10986                 textareaEl.hide();
10987             }
10988             progressEl.setDisplayed(opt.progress === true);
10989             this.updateProgress(0);
10990             activeTextEl.dom.value = opt.value || "";
10991             if(opt.prompt){
10992                 dlg.setDefaultButton(activeTextEl);
10993             }else{
10994                 var bs = opt.buttons;
10995                 var db = null;
10996                 if(bs && bs.ok){
10997                     db = buttons["ok"];
10998                 }else if(bs && bs.yes){
10999                     db = buttons["yes"];
11000                 }
11001                 dlg.setDefaultButton(db);
11002             }
11003             bwidth = updateButtons(opt.buttons);
11004             this.updateText(opt.msg);
11005             if(opt.cls){
11006                 d.el.addClass(opt.cls);
11007             }
11008             d.proxyDrag = opt.proxyDrag === true;
11009             d.modal = opt.modal !== false;
11010             d.mask = opt.modal !== false ? mask : false;
11011             if(!d.isVisible()){
11012                 // force it to the end of the z-index stack so it gets a cursor in FF
11013                 document.body.appendChild(dlg.el.dom);
11014                 d.animateTarget = null;
11015                 d.show(options.animEl);
11016             }
11017             return this;
11018         },
11019
11020         /**
11021          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11022          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11023          * and closing the message box when the process is complete.
11024          * @param {String} title The title bar text
11025          * @param {String} msg The message box body text
11026          * @return {Roo.MessageBox} This message box
11027          */
11028         progress : function(title, msg){
11029             this.show({
11030                 title : title,
11031                 msg : msg,
11032                 buttons: false,
11033                 progress:true,
11034                 closable:false,
11035                 minWidth: this.minProgressWidth,
11036                 modal : true
11037             });
11038             return this;
11039         },
11040
11041         /**
11042          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11043          * If a callback function is passed it will be called after the user clicks the button, and the
11044          * id of the button that was clicked will be passed as the only parameter to the callback
11045          * (could also be the top-right close button).
11046          * @param {String} title The title bar text
11047          * @param {String} msg The message box body text
11048          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11049          * @param {Object} scope (optional) The scope of the callback function
11050          * @return {Roo.MessageBox} This message box
11051          */
11052         alert : function(title, msg, fn, scope){
11053             this.show({
11054                 title : title,
11055                 msg : msg,
11056                 buttons: this.OK,
11057                 fn: fn,
11058                 scope : scope,
11059                 modal : true
11060             });
11061             return this;
11062         },
11063
11064         /**
11065          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11066          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11067          * You are responsible for closing the message box when the process is complete.
11068          * @param {String} msg The message box body text
11069          * @param {String} title (optional) The title bar text
11070          * @return {Roo.MessageBox} This message box
11071          */
11072         wait : function(msg, title){
11073             this.show({
11074                 title : title,
11075                 msg : msg,
11076                 buttons: false,
11077                 closable:false,
11078                 progress:true,
11079                 modal:true,
11080                 width:300,
11081                 wait:true
11082             });
11083             waitTimer = Roo.TaskMgr.start({
11084                 run: function(i){
11085                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11086                 },
11087                 interval: 1000
11088             });
11089             return this;
11090         },
11091
11092         /**
11093          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11094          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11095          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11096          * @param {String} title The title bar text
11097          * @param {String} msg The message box body text
11098          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11099          * @param {Object} scope (optional) The scope of the callback function
11100          * @return {Roo.MessageBox} This message box
11101          */
11102         confirm : function(title, msg, fn, scope){
11103             this.show({
11104                 title : title,
11105                 msg : msg,
11106                 buttons: this.YESNO,
11107                 fn: fn,
11108                 scope : scope,
11109                 modal : true
11110             });
11111             return this;
11112         },
11113
11114         /**
11115          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11116          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11117          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11118          * (could also be the top-right close button) and the text that was entered will be passed as the two
11119          * parameters to the callback.
11120          * @param {String} title The title bar text
11121          * @param {String} msg The message box body text
11122          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11123          * @param {Object} scope (optional) The scope of the callback function
11124          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11125          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11126          * @return {Roo.MessageBox} This message box
11127          */
11128         prompt : function(title, msg, fn, scope, multiline){
11129             this.show({
11130                 title : title,
11131                 msg : msg,
11132                 buttons: this.OKCANCEL,
11133                 fn: fn,
11134                 minWidth:250,
11135                 scope : scope,
11136                 prompt:true,
11137                 multiline: multiline,
11138                 modal : true
11139             });
11140             return this;
11141         },
11142
11143         /**
11144          * Button config that displays a single OK button
11145          * @type Object
11146          */
11147         OK : {ok:true},
11148         /**
11149          * Button config that displays Yes and No buttons
11150          * @type Object
11151          */
11152         YESNO : {yes:true, no:true},
11153         /**
11154          * Button config that displays OK and Cancel buttons
11155          * @type Object
11156          */
11157         OKCANCEL : {ok:true, cancel:true},
11158         /**
11159          * Button config that displays Yes, No and Cancel buttons
11160          * @type Object
11161          */
11162         YESNOCANCEL : {yes:true, no:true, cancel:true},
11163
11164         /**
11165          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11166          * @type Number
11167          */
11168         defaultTextHeight : 75,
11169         /**
11170          * The maximum width in pixels of the message box (defaults to 600)
11171          * @type Number
11172          */
11173         maxWidth : 600,
11174         /**
11175          * The minimum width in pixels of the message box (defaults to 100)
11176          * @type Number
11177          */
11178         minWidth : 100,
11179         /**
11180          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11181          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11182          * @type Number
11183          */
11184         minProgressWidth : 250,
11185         /**
11186          * An object containing the default button text strings that can be overriden for localized language support.
11187          * Supported properties are: ok, cancel, yes and no.
11188          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11189          * @type Object
11190          */
11191         buttonText : {
11192             ok : "OK",
11193             cancel : "Cancel",
11194             yes : "Yes",
11195             no : "No"
11196         }
11197     };
11198 }();
11199
11200 /**
11201  * Shorthand for {@link Roo.MessageBox}
11202  */
11203 Roo.Msg = Roo.MessageBox;/*
11204  * Based on:
11205  * Ext JS Library 1.1.1
11206  * Copyright(c) 2006-2007, Ext JS, LLC.
11207  *
11208  * Originally Released Under LGPL - original licence link has changed is not relivant.
11209  *
11210  * Fork - LGPL
11211  * <script type="text/javascript">
11212  */
11213 /**
11214  * @class Roo.QuickTips
11215  * Provides attractive and customizable tooltips for any element.
11216  * @singleton
11217  */
11218 Roo.QuickTips = function(){
11219     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11220     var ce, bd, xy, dd;
11221     var visible = false, disabled = true, inited = false;
11222     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11223     
11224     var onOver = function(e){
11225         if(disabled){
11226             return;
11227         }
11228         var t = e.getTarget();
11229         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11230             return;
11231         }
11232         if(ce && t == ce.el){
11233             clearTimeout(hideProc);
11234             return;
11235         }
11236         if(t && tagEls[t.id]){
11237             tagEls[t.id].el = t;
11238             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11239             return;
11240         }
11241         var ttp, et = Roo.fly(t);
11242         var ns = cfg.namespace;
11243         if(tm.interceptTitles && t.title){
11244             ttp = t.title;
11245             t.qtip = ttp;
11246             t.removeAttribute("title");
11247             e.preventDefault();
11248         }else{
11249             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11250         }
11251         if(ttp){
11252             showProc = show.defer(tm.showDelay, tm, [{
11253                 el: t, 
11254                 text: ttp.replace(/\\n/g,'<br/>'),
11255                 width: et.getAttributeNS(ns, cfg.width),
11256                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11257                 title: et.getAttributeNS(ns, cfg.title),
11258                     cls: et.getAttributeNS(ns, cfg.cls)
11259             }]);
11260         }
11261     };
11262     
11263     var onOut = function(e){
11264         clearTimeout(showProc);
11265         var t = e.getTarget();
11266         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11267             hideProc = setTimeout(hide, tm.hideDelay);
11268         }
11269     };
11270     
11271     var onMove = function(e){
11272         if(disabled){
11273             return;
11274         }
11275         xy = e.getXY();
11276         xy[1] += 18;
11277         if(tm.trackMouse && ce){
11278             el.setXY(xy);
11279         }
11280     };
11281     
11282     var onDown = function(e){
11283         clearTimeout(showProc);
11284         clearTimeout(hideProc);
11285         if(!e.within(el)){
11286             if(tm.hideOnClick){
11287                 hide();
11288                 tm.disable();
11289                 tm.enable.defer(100, tm);
11290             }
11291         }
11292     };
11293     
11294     var getPad = function(){
11295         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11296     };
11297
11298     var show = function(o){
11299         if(disabled){
11300             return;
11301         }
11302         clearTimeout(dismissProc);
11303         ce = o;
11304         if(removeCls){ // in case manually hidden
11305             el.removeClass(removeCls);
11306             removeCls = null;
11307         }
11308         if(ce.cls){
11309             el.addClass(ce.cls);
11310             removeCls = ce.cls;
11311         }
11312         if(ce.title){
11313             tipTitle.update(ce.title);
11314             tipTitle.show();
11315         }else{
11316             tipTitle.update('');
11317             tipTitle.hide();
11318         }
11319         el.dom.style.width  = tm.maxWidth+'px';
11320         //tipBody.dom.style.width = '';
11321         tipBodyText.update(o.text);
11322         var p = getPad(), w = ce.width;
11323         if(!w){
11324             var td = tipBodyText.dom;
11325             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11326             if(aw > tm.maxWidth){
11327                 w = tm.maxWidth;
11328             }else if(aw < tm.minWidth){
11329                 w = tm.minWidth;
11330             }else{
11331                 w = aw;
11332             }
11333         }
11334         //tipBody.setWidth(w);
11335         el.setWidth(parseInt(w, 10) + p);
11336         if(ce.autoHide === false){
11337             close.setDisplayed(true);
11338             if(dd){
11339                 dd.unlock();
11340             }
11341         }else{
11342             close.setDisplayed(false);
11343             if(dd){
11344                 dd.lock();
11345             }
11346         }
11347         if(xy){
11348             el.avoidY = xy[1]-18;
11349             el.setXY(xy);
11350         }
11351         if(tm.animate){
11352             el.setOpacity(.1);
11353             el.setStyle("visibility", "visible");
11354             el.fadeIn({callback: afterShow});
11355         }else{
11356             afterShow();
11357         }
11358     };
11359     
11360     var afterShow = function(){
11361         if(ce){
11362             el.show();
11363             esc.enable();
11364             if(tm.autoDismiss && ce.autoHide !== false){
11365                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11366             }
11367         }
11368     };
11369     
11370     var hide = function(noanim){
11371         clearTimeout(dismissProc);
11372         clearTimeout(hideProc);
11373         ce = null;
11374         if(el.isVisible()){
11375             esc.disable();
11376             if(noanim !== true && tm.animate){
11377                 el.fadeOut({callback: afterHide});
11378             }else{
11379                 afterHide();
11380             } 
11381         }
11382     };
11383     
11384     var afterHide = function(){
11385         el.hide();
11386         if(removeCls){
11387             el.removeClass(removeCls);
11388             removeCls = null;
11389         }
11390     };
11391     
11392     return {
11393         /**
11394         * @cfg {Number} minWidth
11395         * The minimum width of the quick tip (defaults to 40)
11396         */
11397        minWidth : 40,
11398         /**
11399         * @cfg {Number} maxWidth
11400         * The maximum width of the quick tip (defaults to 300)
11401         */
11402        maxWidth : 300,
11403         /**
11404         * @cfg {Boolean} interceptTitles
11405         * True to automatically use the element's DOM title value if available (defaults to false)
11406         */
11407        interceptTitles : false,
11408         /**
11409         * @cfg {Boolean} trackMouse
11410         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11411         */
11412        trackMouse : false,
11413         /**
11414         * @cfg {Boolean} hideOnClick
11415         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11416         */
11417        hideOnClick : true,
11418         /**
11419         * @cfg {Number} showDelay
11420         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11421         */
11422        showDelay : 500,
11423         /**
11424         * @cfg {Number} hideDelay
11425         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11426         */
11427        hideDelay : 200,
11428         /**
11429         * @cfg {Boolean} autoHide
11430         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11431         * Used in conjunction with hideDelay.
11432         */
11433        autoHide : true,
11434         /**
11435         * @cfg {Boolean}
11436         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11437         * (defaults to true).  Used in conjunction with autoDismissDelay.
11438         */
11439        autoDismiss : true,
11440         /**
11441         * @cfg {Number}
11442         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11443         */
11444        autoDismissDelay : 5000,
11445        /**
11446         * @cfg {Boolean} animate
11447         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11448         */
11449        animate : false,
11450
11451        /**
11452         * @cfg {String} title
11453         * Title text to display (defaults to '').  This can be any valid HTML markup.
11454         */
11455         title: '',
11456        /**
11457         * @cfg {String} text
11458         * Body text to display (defaults to '').  This can be any valid HTML markup.
11459         */
11460         text : '',
11461        /**
11462         * @cfg {String} cls
11463         * A CSS class to apply to the base quick tip element (defaults to '').
11464         */
11465         cls : '',
11466        /**
11467         * @cfg {Number} width
11468         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11469         * minWidth or maxWidth.
11470         */
11471         width : null,
11472
11473     /**
11474      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11475      * or display QuickTips in a page.
11476      */
11477        init : function(){
11478           tm = Roo.QuickTips;
11479           cfg = tm.tagConfig;
11480           if(!inited){
11481               if(!Roo.isReady){ // allow calling of init() before onReady
11482                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11483                   return;
11484               }
11485               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11486               el.fxDefaults = {stopFx: true};
11487               // maximum custom styling
11488               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
11489               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
11490               tipTitle = el.child('h3');
11491               tipTitle.enableDisplayMode("block");
11492               tipBody = el.child('div.x-tip-bd');
11493               tipBodyText = el.child('div.x-tip-bd-inner');
11494               //bdLeft = el.child('div.x-tip-bd-left');
11495               //bdRight = el.child('div.x-tip-bd-right');
11496               close = el.child('div.x-tip-close');
11497               close.enableDisplayMode("block");
11498               close.on("click", hide);
11499               var d = Roo.get(document);
11500               d.on("mousedown", onDown);
11501               d.on("mouseover", onOver);
11502               d.on("mouseout", onOut);
11503               d.on("mousemove", onMove);
11504               esc = d.addKeyListener(27, hide);
11505               esc.disable();
11506               if(Roo.dd.DD){
11507                   dd = el.initDD("default", null, {
11508                       onDrag : function(){
11509                           el.sync();  
11510                       }
11511                   });
11512                   dd.setHandleElId(tipTitle.id);
11513                   dd.lock();
11514               }
11515               inited = true;
11516           }
11517           this.enable(); 
11518        },
11519
11520     /**
11521      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11522      * are supported:
11523      * <pre>
11524 Property    Type                   Description
11525 ----------  ---------------------  ------------------------------------------------------------------------
11526 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11527      * </ul>
11528      * @param {Object} config The config object
11529      */
11530        register : function(config){
11531            var cs = config instanceof Array ? config : arguments;
11532            for(var i = 0, len = cs.length; i < len; i++) {
11533                var c = cs[i];
11534                var target = c.target;
11535                if(target){
11536                    if(target instanceof Array){
11537                        for(var j = 0, jlen = target.length; j < jlen; j++){
11538                            tagEls[target[j]] = c;
11539                        }
11540                    }else{
11541                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11542                    }
11543                }
11544            }
11545        },
11546
11547     /**
11548      * Removes this quick tip from its element and destroys it.
11549      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11550      */
11551        unregister : function(el){
11552            delete tagEls[Roo.id(el)];
11553        },
11554
11555     /**
11556      * Enable this quick tip.
11557      */
11558        enable : function(){
11559            if(inited && disabled){
11560                locks.pop();
11561                if(locks.length < 1){
11562                    disabled = false;
11563                }
11564            }
11565        },
11566
11567     /**
11568      * Disable this quick tip.
11569      */
11570        disable : function(){
11571           disabled = true;
11572           clearTimeout(showProc);
11573           clearTimeout(hideProc);
11574           clearTimeout(dismissProc);
11575           if(ce){
11576               hide(true);
11577           }
11578           locks.push(1);
11579        },
11580
11581     /**
11582      * Returns true if the quick tip is enabled, else false.
11583      */
11584        isEnabled : function(){
11585             return !disabled;
11586        },
11587
11588         // private
11589        tagConfig : {
11590            namespace : "roo", // was ext?? this may break..
11591            alt_namespace : "ext",
11592            attribute : "qtip",
11593            width : "width",
11594            target : "target",
11595            title : "qtitle",
11596            hide : "hide",
11597            cls : "qclass"
11598        }
11599    };
11600 }();
11601
11602 // backwards compat
11603 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11604  * Based on:
11605  * Ext JS Library 1.1.1
11606  * Copyright(c) 2006-2007, Ext JS, LLC.
11607  *
11608  * Originally Released Under LGPL - original licence link has changed is not relivant.
11609  *
11610  * Fork - LGPL
11611  * <script type="text/javascript">
11612  */
11613  
11614
11615 /**
11616  * @class Roo.tree.TreePanel
11617  * @extends Roo.data.Tree
11618
11619  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11620  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11621  * @cfg {Boolean} enableDD true to enable drag and drop
11622  * @cfg {Boolean} enableDrag true to enable just drag
11623  * @cfg {Boolean} enableDrop true to enable just drop
11624  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11625  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11626  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11627  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11628  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11629  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11630  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11631  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11632  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11633  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11634  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11635  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11636  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11637  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11638  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11639  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11640  * 
11641  * @constructor
11642  * @param {String/HTMLElement/Element} el The container element
11643  * @param {Object} config
11644  */
11645 Roo.tree.TreePanel = function(el, config){
11646     var root = false;
11647     var loader = false;
11648     if (config.root) {
11649         root = config.root;
11650         delete config.root;
11651     }
11652     if (config.loader) {
11653         loader = config.loader;
11654         delete config.loader;
11655     }
11656     
11657     Roo.apply(this, config);
11658     Roo.tree.TreePanel.superclass.constructor.call(this);
11659     this.el = Roo.get(el);
11660     this.el.addClass('x-tree');
11661     //console.log(root);
11662     if (root) {
11663         this.setRootNode( Roo.factory(root, Roo.tree));
11664     }
11665     if (loader) {
11666         this.loader = Roo.factory(loader, Roo.tree);
11667     }
11668    /**
11669     * Read-only. The id of the container element becomes this TreePanel's id.
11670     */
11671     this.id = this.el.id;
11672     this.addEvents({
11673         /**
11674         * @event beforeload
11675         * Fires before a node is loaded, return false to cancel
11676         * @param {Node} node The node being loaded
11677         */
11678         "beforeload" : true,
11679         /**
11680         * @event load
11681         * Fires when a node is loaded
11682         * @param {Node} node The node that was loaded
11683         */
11684         "load" : true,
11685         /**
11686         * @event textchange
11687         * Fires when the text for a node is changed
11688         * @param {Node} node The node
11689         * @param {String} text The new text
11690         * @param {String} oldText The old text
11691         */
11692         "textchange" : true,
11693         /**
11694         * @event beforeexpand
11695         * Fires before a node is expanded, return false to cancel.
11696         * @param {Node} node The node
11697         * @param {Boolean} deep
11698         * @param {Boolean} anim
11699         */
11700         "beforeexpand" : true,
11701         /**
11702         * @event beforecollapse
11703         * Fires before a node is collapsed, return false to cancel.
11704         * @param {Node} node The node
11705         * @param {Boolean} deep
11706         * @param {Boolean} anim
11707         */
11708         "beforecollapse" : true,
11709         /**
11710         * @event expand
11711         * Fires when a node is expanded
11712         * @param {Node} node The node
11713         */
11714         "expand" : true,
11715         /**
11716         * @event disabledchange
11717         * Fires when the disabled status of a node changes
11718         * @param {Node} node The node
11719         * @param {Boolean} disabled
11720         */
11721         "disabledchange" : true,
11722         /**
11723         * @event collapse
11724         * Fires when a node is collapsed
11725         * @param {Node} node The node
11726         */
11727         "collapse" : true,
11728         /**
11729         * @event beforeclick
11730         * Fires before click processing on a node. Return false to cancel the default action.
11731         * @param {Node} node The node
11732         * @param {Roo.EventObject} e The event object
11733         */
11734         "beforeclick":true,
11735         /**
11736         * @event checkchange
11737         * Fires when a node with a checkbox's checked property changes
11738         * @param {Node} this This node
11739         * @param {Boolean} checked
11740         */
11741         "checkchange":true,
11742         /**
11743         * @event click
11744         * Fires when a node is clicked
11745         * @param {Node} node The node
11746         * @param {Roo.EventObject} e The event object
11747         */
11748         "click":true,
11749         /**
11750         * @event dblclick
11751         * Fires when a node is double clicked
11752         * @param {Node} node The node
11753         * @param {Roo.EventObject} e The event object
11754         */
11755         "dblclick":true,
11756         /**
11757         * @event contextmenu
11758         * Fires when a node is right clicked
11759         * @param {Node} node The node
11760         * @param {Roo.EventObject} e The event object
11761         */
11762         "contextmenu":true,
11763         /**
11764         * @event beforechildrenrendered
11765         * Fires right before the child nodes for a node are rendered
11766         * @param {Node} node The node
11767         */
11768         "beforechildrenrendered":true,
11769         /**
11770         * @event startdrag
11771         * Fires when a node starts being dragged
11772         * @param {Roo.tree.TreePanel} this
11773         * @param {Roo.tree.TreeNode} node
11774         * @param {event} e The raw browser event
11775         */ 
11776        "startdrag" : true,
11777        /**
11778         * @event enddrag
11779         * Fires when a drag operation is complete
11780         * @param {Roo.tree.TreePanel} this
11781         * @param {Roo.tree.TreeNode} node
11782         * @param {event} e The raw browser event
11783         */
11784        "enddrag" : true,
11785        /**
11786         * @event dragdrop
11787         * Fires when a dragged node is dropped on a valid DD target
11788         * @param {Roo.tree.TreePanel} this
11789         * @param {Roo.tree.TreeNode} node
11790         * @param {DD} dd The dd it was dropped on
11791         * @param {event} e The raw browser event
11792         */
11793        "dragdrop" : true,
11794        /**
11795         * @event beforenodedrop
11796         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11797         * passed to handlers has the following properties:<br />
11798         * <ul style="padding:5px;padding-left:16px;">
11799         * <li>tree - The TreePanel</li>
11800         * <li>target - The node being targeted for the drop</li>
11801         * <li>data - The drag data from the drag source</li>
11802         * <li>point - The point of the drop - append, above or below</li>
11803         * <li>source - The drag source</li>
11804         * <li>rawEvent - Raw mouse event</li>
11805         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11806         * to be inserted by setting them on this object.</li>
11807         * <li>cancel - Set this to true to cancel the drop.</li>
11808         * </ul>
11809         * @param {Object} dropEvent
11810         */
11811        "beforenodedrop" : true,
11812        /**
11813         * @event nodedrop
11814         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11815         * passed to handlers has the following properties:<br />
11816         * <ul style="padding:5px;padding-left:16px;">
11817         * <li>tree - The TreePanel</li>
11818         * <li>target - The node being targeted for the drop</li>
11819         * <li>data - The drag data from the drag source</li>
11820         * <li>point - The point of the drop - append, above or below</li>
11821         * <li>source - The drag source</li>
11822         * <li>rawEvent - Raw mouse event</li>
11823         * <li>dropNode - Dropped node(s).</li>
11824         * </ul>
11825         * @param {Object} dropEvent
11826         */
11827        "nodedrop" : true,
11828         /**
11829         * @event nodedragover
11830         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11831         * passed to handlers has the following properties:<br />
11832         * <ul style="padding:5px;padding-left:16px;">
11833         * <li>tree - The TreePanel</li>
11834         * <li>target - The node being targeted for the drop</li>
11835         * <li>data - The drag data from the drag source</li>
11836         * <li>point - The point of the drop - append, above or below</li>
11837         * <li>source - The drag source</li>
11838         * <li>rawEvent - Raw mouse event</li>
11839         * <li>dropNode - Drop node(s) provided by the source.</li>
11840         * <li>cancel - Set this to true to signal drop not allowed.</li>
11841         * </ul>
11842         * @param {Object} dragOverEvent
11843         */
11844        "nodedragover" : true,
11845        /**
11846         * @event appendnode
11847         * Fires when append node to the tree
11848         * @param {Roo.tree.TreePanel} this
11849         * @param {Roo.tree.TreeNode} node
11850         * @param {Number} index The index of the newly appended node
11851         */
11852        "appendnode" : true
11853         
11854     });
11855     if(this.singleExpand){
11856        this.on("beforeexpand", this.restrictExpand, this);
11857     }
11858     if (this.editor) {
11859         this.editor.tree = this;
11860         this.editor = Roo.factory(this.editor, Roo.tree);
11861     }
11862     
11863     if (this.selModel) {
11864         this.selModel = Roo.factory(this.selModel, Roo.tree);
11865     }
11866    
11867 };
11868 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11869     rootVisible : true,
11870     animate: Roo.enableFx,
11871     lines : true,
11872     enableDD : false,
11873     hlDrop : Roo.enableFx,
11874   
11875     renderer: false,
11876     
11877     rendererTip: false,
11878     // private
11879     restrictExpand : function(node){
11880         var p = node.parentNode;
11881         if(p){
11882             if(p.expandedChild && p.expandedChild.parentNode == p){
11883                 p.expandedChild.collapse();
11884             }
11885             p.expandedChild = node;
11886         }
11887     },
11888
11889     // private override
11890     setRootNode : function(node){
11891         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11892         if(!this.rootVisible){
11893             node.ui = new Roo.tree.RootTreeNodeUI(node);
11894         }
11895         return node;
11896     },
11897
11898     /**
11899      * Returns the container element for this TreePanel
11900      */
11901     getEl : function(){
11902         return this.el;
11903     },
11904
11905     /**
11906      * Returns the default TreeLoader for this TreePanel
11907      */
11908     getLoader : function(){
11909         return this.loader;
11910     },
11911
11912     /**
11913      * Expand all nodes
11914      */
11915     expandAll : function(){
11916         this.root.expand(true);
11917     },
11918
11919     /**
11920      * Collapse all nodes
11921      */
11922     collapseAll : function(){
11923         this.root.collapse(true);
11924     },
11925
11926     /**
11927      * Returns the selection model used by this TreePanel
11928      */
11929     getSelectionModel : function(){
11930         if(!this.selModel){
11931             this.selModel = new Roo.tree.DefaultSelectionModel();
11932         }
11933         return this.selModel;
11934     },
11935
11936     /**
11937      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11938      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11939      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11940      * @return {Array}
11941      */
11942     getChecked : function(a, startNode){
11943         startNode = startNode || this.root;
11944         var r = [];
11945         var f = function(){
11946             if(this.attributes.checked){
11947                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11948             }
11949         }
11950         startNode.cascade(f);
11951         return r;
11952     },
11953
11954     /**
11955      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11956      * @param {String} path
11957      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11958      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11959      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11960      */
11961     expandPath : function(path, attr, callback){
11962         attr = attr || "id";
11963         var keys = path.split(this.pathSeparator);
11964         var curNode = this.root;
11965         if(curNode.attributes[attr] != keys[1]){ // invalid root
11966             if(callback){
11967                 callback(false, null);
11968             }
11969             return;
11970         }
11971         var index = 1;
11972         var f = function(){
11973             if(++index == keys.length){
11974                 if(callback){
11975                     callback(true, curNode);
11976                 }
11977                 return;
11978             }
11979             var c = curNode.findChild(attr, keys[index]);
11980             if(!c){
11981                 if(callback){
11982                     callback(false, curNode);
11983                 }
11984                 return;
11985             }
11986             curNode = c;
11987             c.expand(false, false, f);
11988         };
11989         curNode.expand(false, false, f);
11990     },
11991
11992     /**
11993      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11994      * @param {String} path
11995      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11996      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11997      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11998      */
11999     selectPath : function(path, attr, callback){
12000         attr = attr || "id";
12001         var keys = path.split(this.pathSeparator);
12002         var v = keys.pop();
12003         if(keys.length > 0){
12004             var f = function(success, node){
12005                 if(success && node){
12006                     var n = node.findChild(attr, v);
12007                     if(n){
12008                         n.select();
12009                         if(callback){
12010                             callback(true, n);
12011                         }
12012                     }else if(callback){
12013                         callback(false, n);
12014                     }
12015                 }else{
12016                     if(callback){
12017                         callback(false, n);
12018                     }
12019                 }
12020             };
12021             this.expandPath(keys.join(this.pathSeparator), attr, f);
12022         }else{
12023             this.root.select();
12024             if(callback){
12025                 callback(true, this.root);
12026             }
12027         }
12028     },
12029
12030     getTreeEl : function(){
12031         return this.el;
12032     },
12033
12034     /**
12035      * Trigger rendering of this TreePanel
12036      */
12037     render : function(){
12038         if (this.innerCt) {
12039             return this; // stop it rendering more than once!!
12040         }
12041         
12042         this.innerCt = this.el.createChild({tag:"ul",
12043                cls:"x-tree-root-ct " +
12044                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12045
12046         if(this.containerScroll){
12047             Roo.dd.ScrollManager.register(this.el);
12048         }
12049         if((this.enableDD || this.enableDrop) && !this.dropZone){
12050            /**
12051             * The dropZone used by this tree if drop is enabled
12052             * @type Roo.tree.TreeDropZone
12053             */
12054              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12055                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12056            });
12057         }
12058         if((this.enableDD || this.enableDrag) && !this.dragZone){
12059            /**
12060             * The dragZone used by this tree if drag is enabled
12061             * @type Roo.tree.TreeDragZone
12062             */
12063             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12064                ddGroup: this.ddGroup || "TreeDD",
12065                scroll: this.ddScroll
12066            });
12067         }
12068         this.getSelectionModel().init(this);
12069         if (!this.root) {
12070             Roo.log("ROOT not set in tree");
12071             return this;
12072         }
12073         this.root.render();
12074         if(!this.rootVisible){
12075             this.root.renderChildren();
12076         }
12077         return this;
12078     }
12079 });/*
12080  * Based on:
12081  * Ext JS Library 1.1.1
12082  * Copyright(c) 2006-2007, Ext JS, LLC.
12083  *
12084  * Originally Released Under LGPL - original licence link has changed is not relivant.
12085  *
12086  * Fork - LGPL
12087  * <script type="text/javascript">
12088  */
12089  
12090
12091 /**
12092  * @class Roo.tree.DefaultSelectionModel
12093  * @extends Roo.util.Observable
12094  * The default single selection for a TreePanel.
12095  * @param {Object} cfg Configuration
12096  */
12097 Roo.tree.DefaultSelectionModel = function(cfg){
12098    this.selNode = null;
12099    
12100    
12101    
12102    this.addEvents({
12103        /**
12104         * @event selectionchange
12105         * Fires when the selected node changes
12106         * @param {DefaultSelectionModel} this
12107         * @param {TreeNode} node the new selection
12108         */
12109        "selectionchange" : true,
12110
12111        /**
12112         * @event beforeselect
12113         * Fires before the selected node changes, return false to cancel the change
12114         * @param {DefaultSelectionModel} this
12115         * @param {TreeNode} node the new selection
12116         * @param {TreeNode} node the old selection
12117         */
12118        "beforeselect" : true
12119    });
12120    
12121     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12122 };
12123
12124 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12125     init : function(tree){
12126         this.tree = tree;
12127         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12128         tree.on("click", this.onNodeClick, this);
12129     },
12130     
12131     onNodeClick : function(node, e){
12132         if (e.ctrlKey && this.selNode == node)  {
12133             this.unselect(node);
12134             return;
12135         }
12136         this.select(node);
12137     },
12138     
12139     /**
12140      * Select a node.
12141      * @param {TreeNode} node The node to select
12142      * @return {TreeNode} The selected node
12143      */
12144     select : function(node){
12145         var last = this.selNode;
12146         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12147             if(last){
12148                 last.ui.onSelectedChange(false);
12149             }
12150             this.selNode = node;
12151             node.ui.onSelectedChange(true);
12152             this.fireEvent("selectionchange", this, node, last);
12153         }
12154         return node;
12155     },
12156     
12157     /**
12158      * Deselect a node.
12159      * @param {TreeNode} node The node to unselect
12160      */
12161     unselect : function(node){
12162         if(this.selNode == node){
12163             this.clearSelections();
12164         }    
12165     },
12166     
12167     /**
12168      * Clear all selections
12169      */
12170     clearSelections : function(){
12171         var n = this.selNode;
12172         if(n){
12173             n.ui.onSelectedChange(false);
12174             this.selNode = null;
12175             this.fireEvent("selectionchange", this, null);
12176         }
12177         return n;
12178     },
12179     
12180     /**
12181      * Get the selected node
12182      * @return {TreeNode} The selected node
12183      */
12184     getSelectedNode : function(){
12185         return this.selNode;    
12186     },
12187     
12188     /**
12189      * Returns true if the node is selected
12190      * @param {TreeNode} node The node to check
12191      * @return {Boolean}
12192      */
12193     isSelected : function(node){
12194         return this.selNode == node;  
12195     },
12196
12197     /**
12198      * Selects the node above the selected node in the tree, intelligently walking the nodes
12199      * @return TreeNode The new selection
12200      */
12201     selectPrevious : function(){
12202         var s = this.selNode || this.lastSelNode;
12203         if(!s){
12204             return null;
12205         }
12206         var ps = s.previousSibling;
12207         if(ps){
12208             if(!ps.isExpanded() || ps.childNodes.length < 1){
12209                 return this.select(ps);
12210             } else{
12211                 var lc = ps.lastChild;
12212                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12213                     lc = lc.lastChild;
12214                 }
12215                 return this.select(lc);
12216             }
12217         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12218             return this.select(s.parentNode);
12219         }
12220         return null;
12221     },
12222
12223     /**
12224      * Selects the node above the selected node in the tree, intelligently walking the nodes
12225      * @return TreeNode The new selection
12226      */
12227     selectNext : function(){
12228         var s = this.selNode || this.lastSelNode;
12229         if(!s){
12230             return null;
12231         }
12232         if(s.firstChild && s.isExpanded()){
12233              return this.select(s.firstChild);
12234          }else if(s.nextSibling){
12235              return this.select(s.nextSibling);
12236          }else if(s.parentNode){
12237             var newS = null;
12238             s.parentNode.bubble(function(){
12239                 if(this.nextSibling){
12240                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12241                     return false;
12242                 }
12243             });
12244             return newS;
12245          }
12246         return null;
12247     },
12248
12249     onKeyDown : function(e){
12250         var s = this.selNode || this.lastSelNode;
12251         // undesirable, but required
12252         var sm = this;
12253         if(!s){
12254             return;
12255         }
12256         var k = e.getKey();
12257         switch(k){
12258              case e.DOWN:
12259                  e.stopEvent();
12260                  this.selectNext();
12261              break;
12262              case e.UP:
12263                  e.stopEvent();
12264                  this.selectPrevious();
12265              break;
12266              case e.RIGHT:
12267                  e.preventDefault();
12268                  if(s.hasChildNodes()){
12269                      if(!s.isExpanded()){
12270                          s.expand();
12271                      }else if(s.firstChild){
12272                          this.select(s.firstChild, e);
12273                      }
12274                  }
12275              break;
12276              case e.LEFT:
12277                  e.preventDefault();
12278                  if(s.hasChildNodes() && s.isExpanded()){
12279                      s.collapse();
12280                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12281                      this.select(s.parentNode, e);
12282                  }
12283              break;
12284         };
12285     }
12286 });
12287
12288 /**
12289  * @class Roo.tree.MultiSelectionModel
12290  * @extends Roo.util.Observable
12291  * Multi selection for a TreePanel.
12292  * @param {Object} cfg Configuration
12293  */
12294 Roo.tree.MultiSelectionModel = function(){
12295    this.selNodes = [];
12296    this.selMap = {};
12297    this.addEvents({
12298        /**
12299         * @event selectionchange
12300         * Fires when the selected nodes change
12301         * @param {MultiSelectionModel} this
12302         * @param {Array} nodes Array of the selected nodes
12303         */
12304        "selectionchange" : true
12305    });
12306    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12307    
12308 };
12309
12310 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12311     init : function(tree){
12312         this.tree = tree;
12313         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12314         tree.on("click", this.onNodeClick, this);
12315     },
12316     
12317     onNodeClick : function(node, e){
12318         this.select(node, e, e.ctrlKey);
12319     },
12320     
12321     /**
12322      * Select a node.
12323      * @param {TreeNode} node The node to select
12324      * @param {EventObject} e (optional) An event associated with the selection
12325      * @param {Boolean} keepExisting True to retain existing selections
12326      * @return {TreeNode} The selected node
12327      */
12328     select : function(node, e, keepExisting){
12329         if(keepExisting !== true){
12330             this.clearSelections(true);
12331         }
12332         if(this.isSelected(node)){
12333             this.lastSelNode = node;
12334             return node;
12335         }
12336         this.selNodes.push(node);
12337         this.selMap[node.id] = node;
12338         this.lastSelNode = node;
12339         node.ui.onSelectedChange(true);
12340         this.fireEvent("selectionchange", this, this.selNodes);
12341         return node;
12342     },
12343     
12344     /**
12345      * Deselect a node.
12346      * @param {TreeNode} node The node to unselect
12347      */
12348     unselect : function(node){
12349         if(this.selMap[node.id]){
12350             node.ui.onSelectedChange(false);
12351             var sn = this.selNodes;
12352             var index = -1;
12353             if(sn.indexOf){
12354                 index = sn.indexOf(node);
12355             }else{
12356                 for(var i = 0, len = sn.length; i < len; i++){
12357                     if(sn[i] == node){
12358                         index = i;
12359                         break;
12360                     }
12361                 }
12362             }
12363             if(index != -1){
12364                 this.selNodes.splice(index, 1);
12365             }
12366             delete this.selMap[node.id];
12367             this.fireEvent("selectionchange", this, this.selNodes);
12368         }
12369     },
12370     
12371     /**
12372      * Clear all selections
12373      */
12374     clearSelections : function(suppressEvent){
12375         var sn = this.selNodes;
12376         if(sn.length > 0){
12377             for(var i = 0, len = sn.length; i < len; i++){
12378                 sn[i].ui.onSelectedChange(false);
12379             }
12380             this.selNodes = [];
12381             this.selMap = {};
12382             if(suppressEvent !== true){
12383                 this.fireEvent("selectionchange", this, this.selNodes);
12384             }
12385         }
12386     },
12387     
12388     /**
12389      * Returns true if the node is selected
12390      * @param {TreeNode} node The node to check
12391      * @return {Boolean}
12392      */
12393     isSelected : function(node){
12394         return this.selMap[node.id] ? true : false;  
12395     },
12396     
12397     /**
12398      * Returns an array of the selected nodes
12399      * @return {Array}
12400      */
12401     getSelectedNodes : function(){
12402         return this.selNodes;    
12403     },
12404
12405     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12406
12407     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12408
12409     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12410 });/*
12411  * Based on:
12412  * Ext JS Library 1.1.1
12413  * Copyright(c) 2006-2007, Ext JS, LLC.
12414  *
12415  * Originally Released Under LGPL - original licence link has changed is not relivant.
12416  *
12417  * Fork - LGPL
12418  * <script type="text/javascript">
12419  */
12420  
12421 /**
12422  * @class Roo.tree.TreeNode
12423  * @extends Roo.data.Node
12424  * @cfg {String} text The text for this node
12425  * @cfg {Boolean} expanded true to start the node expanded
12426  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12427  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12428  * @cfg {Boolean} disabled true to start the node disabled
12429  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12430  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12431  * @cfg {String} cls A css class to be added to the node
12432  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12433  * @cfg {String} href URL of the link used for the node (defaults to #)
12434  * @cfg {String} hrefTarget target frame for the link
12435  * @cfg {String} qtip An Ext QuickTip for the node
12436  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12437  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12438  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12439  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12440  * (defaults to undefined with no checkbox rendered)
12441  * @constructor
12442  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12443  */
12444 Roo.tree.TreeNode = function(attributes){
12445     attributes = attributes || {};
12446     if(typeof attributes == "string"){
12447         attributes = {text: attributes};
12448     }
12449     this.childrenRendered = false;
12450     this.rendered = false;
12451     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12452     this.expanded = attributes.expanded === true;
12453     this.isTarget = attributes.isTarget !== false;
12454     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12455     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12456
12457     /**
12458      * Read-only. The text for this node. To change it use setText().
12459      * @type String
12460      */
12461     this.text = attributes.text;
12462     /**
12463      * True if this node is disabled.
12464      * @type Boolean
12465      */
12466     this.disabled = attributes.disabled === true;
12467
12468     this.addEvents({
12469         /**
12470         * @event textchange
12471         * Fires when the text for this node is changed
12472         * @param {Node} this This node
12473         * @param {String} text The new text
12474         * @param {String} oldText The old text
12475         */
12476         "textchange" : true,
12477         /**
12478         * @event beforeexpand
12479         * Fires before this node is expanded, return false to cancel.
12480         * @param {Node} this This node
12481         * @param {Boolean} deep
12482         * @param {Boolean} anim
12483         */
12484         "beforeexpand" : true,
12485         /**
12486         * @event beforecollapse
12487         * Fires before this node is collapsed, return false to cancel.
12488         * @param {Node} this This node
12489         * @param {Boolean} deep
12490         * @param {Boolean} anim
12491         */
12492         "beforecollapse" : true,
12493         /**
12494         * @event expand
12495         * Fires when this node is expanded
12496         * @param {Node} this This node
12497         */
12498         "expand" : true,
12499         /**
12500         * @event disabledchange
12501         * Fires when the disabled status of this node changes
12502         * @param {Node} this This node
12503         * @param {Boolean} disabled
12504         */
12505         "disabledchange" : true,
12506         /**
12507         * @event collapse
12508         * Fires when this node is collapsed
12509         * @param {Node} this This node
12510         */
12511         "collapse" : true,
12512         /**
12513         * @event beforeclick
12514         * Fires before click processing. Return false to cancel the default action.
12515         * @param {Node} this This node
12516         * @param {Roo.EventObject} e The event object
12517         */
12518         "beforeclick":true,
12519         /**
12520         * @event checkchange
12521         * Fires when a node with a checkbox's checked property changes
12522         * @param {Node} this This node
12523         * @param {Boolean} checked
12524         */
12525         "checkchange":true,
12526         /**
12527         * @event click
12528         * Fires when this node is clicked
12529         * @param {Node} this This node
12530         * @param {Roo.EventObject} e The event object
12531         */
12532         "click":true,
12533         /**
12534         * @event dblclick
12535         * Fires when this node is double clicked
12536         * @param {Node} this This node
12537         * @param {Roo.EventObject} e The event object
12538         */
12539         "dblclick":true,
12540         /**
12541         * @event contextmenu
12542         * Fires when this node is right clicked
12543         * @param {Node} this This node
12544         * @param {Roo.EventObject} e The event object
12545         */
12546         "contextmenu":true,
12547         /**
12548         * @event beforechildrenrendered
12549         * Fires right before the child nodes for this node are rendered
12550         * @param {Node} this This node
12551         */
12552         "beforechildrenrendered":true
12553     });
12554
12555     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12556
12557     /**
12558      * Read-only. The UI for this node
12559      * @type TreeNodeUI
12560      */
12561     this.ui = new uiClass(this);
12562     
12563     // finally support items[]
12564     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12565         return;
12566     }
12567     
12568     
12569     Roo.each(this.attributes.items, function(c) {
12570         this.appendChild(Roo.factory(c,Roo.Tree));
12571     }, this);
12572     delete this.attributes.items;
12573     
12574     
12575     
12576 };
12577 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12578     preventHScroll: true,
12579     /**
12580      * Returns true if this node is expanded
12581      * @return {Boolean}
12582      */
12583     isExpanded : function(){
12584         return this.expanded;
12585     },
12586
12587     /**
12588      * Returns the UI object for this node
12589      * @return {TreeNodeUI}
12590      */
12591     getUI : function(){
12592         return this.ui;
12593     },
12594
12595     // private override
12596     setFirstChild : function(node){
12597         var of = this.firstChild;
12598         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12599         if(this.childrenRendered && of && node != of){
12600             of.renderIndent(true, true);
12601         }
12602         if(this.rendered){
12603             this.renderIndent(true, true);
12604         }
12605     },
12606
12607     // private override
12608     setLastChild : function(node){
12609         var ol = this.lastChild;
12610         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12611         if(this.childrenRendered && ol && node != ol){
12612             ol.renderIndent(true, true);
12613         }
12614         if(this.rendered){
12615             this.renderIndent(true, true);
12616         }
12617     },
12618
12619     // these methods are overridden to provide lazy rendering support
12620     // private override
12621     appendChild : function()
12622     {
12623         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12624         if(node && this.childrenRendered){
12625             node.render();
12626         }
12627         this.ui.updateExpandIcon();
12628         return node;
12629     },
12630
12631     // private override
12632     removeChild : function(node){
12633         this.ownerTree.getSelectionModel().unselect(node);
12634         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12635         // if it's been rendered remove dom node
12636         if(this.childrenRendered){
12637             node.ui.remove();
12638         }
12639         if(this.childNodes.length < 1){
12640             this.collapse(false, false);
12641         }else{
12642             this.ui.updateExpandIcon();
12643         }
12644         if(!this.firstChild) {
12645             this.childrenRendered = false;
12646         }
12647         return node;
12648     },
12649
12650     // private override
12651     insertBefore : function(node, refNode){
12652         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12653         if(newNode && refNode && this.childrenRendered){
12654             node.render();
12655         }
12656         this.ui.updateExpandIcon();
12657         return newNode;
12658     },
12659
12660     /**
12661      * Sets the text for this node
12662      * @param {String} text
12663      */
12664     setText : function(text){
12665         var oldText = this.text;
12666         this.text = text;
12667         this.attributes.text = text;
12668         if(this.rendered){ // event without subscribing
12669             this.ui.onTextChange(this, text, oldText);
12670         }
12671         this.fireEvent("textchange", this, text, oldText);
12672     },
12673
12674     /**
12675      * Triggers selection of this node
12676      */
12677     select : function(){
12678         this.getOwnerTree().getSelectionModel().select(this);
12679     },
12680
12681     /**
12682      * Triggers deselection of this node
12683      */
12684     unselect : function(){
12685         this.getOwnerTree().getSelectionModel().unselect(this);
12686     },
12687
12688     /**
12689      * Returns true if this node is selected
12690      * @return {Boolean}
12691      */
12692     isSelected : function(){
12693         return this.getOwnerTree().getSelectionModel().isSelected(this);
12694     },
12695
12696     /**
12697      * Expand this node.
12698      * @param {Boolean} deep (optional) True to expand all children as well
12699      * @param {Boolean} anim (optional) false to cancel the default animation
12700      * @param {Function} callback (optional) A callback to be called when
12701      * expanding this node completes (does not wait for deep expand to complete).
12702      * Called with 1 parameter, this node.
12703      */
12704     expand : function(deep, anim, callback){
12705         if(!this.expanded){
12706             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12707                 return;
12708             }
12709             if(!this.childrenRendered){
12710                 this.renderChildren();
12711             }
12712             this.expanded = true;
12713             
12714             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12715                 this.ui.animExpand(function(){
12716                     this.fireEvent("expand", this);
12717                     if(typeof callback == "function"){
12718                         callback(this);
12719                     }
12720                     if(deep === true){
12721                         this.expandChildNodes(true);
12722                     }
12723                 }.createDelegate(this));
12724                 return;
12725             }else{
12726                 this.ui.expand();
12727                 this.fireEvent("expand", this);
12728                 if(typeof callback == "function"){
12729                     callback(this);
12730                 }
12731             }
12732         }else{
12733            if(typeof callback == "function"){
12734                callback(this);
12735            }
12736         }
12737         if(deep === true){
12738             this.expandChildNodes(true);
12739         }
12740     },
12741
12742     isHiddenRoot : function(){
12743         return this.isRoot && !this.getOwnerTree().rootVisible;
12744     },
12745
12746     /**
12747      * Collapse this node.
12748      * @param {Boolean} deep (optional) True to collapse all children as well
12749      * @param {Boolean} anim (optional) false to cancel the default animation
12750      */
12751     collapse : function(deep, anim){
12752         if(this.expanded && !this.isHiddenRoot()){
12753             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12754                 return;
12755             }
12756             this.expanded = false;
12757             if((this.getOwnerTree().animate && anim !== false) || anim){
12758                 this.ui.animCollapse(function(){
12759                     this.fireEvent("collapse", this);
12760                     if(deep === true){
12761                         this.collapseChildNodes(true);
12762                     }
12763                 }.createDelegate(this));
12764                 return;
12765             }else{
12766                 this.ui.collapse();
12767                 this.fireEvent("collapse", this);
12768             }
12769         }
12770         if(deep === true){
12771             var cs = this.childNodes;
12772             for(var i = 0, len = cs.length; i < len; i++) {
12773                 cs[i].collapse(true, false);
12774             }
12775         }
12776     },
12777
12778     // private
12779     delayedExpand : function(delay){
12780         if(!this.expandProcId){
12781             this.expandProcId = this.expand.defer(delay, this);
12782         }
12783     },
12784
12785     // private
12786     cancelExpand : function(){
12787         if(this.expandProcId){
12788             clearTimeout(this.expandProcId);
12789         }
12790         this.expandProcId = false;
12791     },
12792
12793     /**
12794      * Toggles expanded/collapsed state of the node
12795      */
12796     toggle : function(){
12797         if(this.expanded){
12798             this.collapse();
12799         }else{
12800             this.expand();
12801         }
12802     },
12803
12804     /**
12805      * Ensures all parent nodes are expanded
12806      */
12807     ensureVisible : function(callback){
12808         var tree = this.getOwnerTree();
12809         tree.expandPath(this.parentNode.getPath(), false, function(){
12810             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12811             Roo.callback(callback);
12812         }.createDelegate(this));
12813     },
12814
12815     /**
12816      * Expand all child nodes
12817      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12818      */
12819     expandChildNodes : function(deep){
12820         var cs = this.childNodes;
12821         for(var i = 0, len = cs.length; i < len; i++) {
12822                 cs[i].expand(deep);
12823         }
12824     },
12825
12826     /**
12827      * Collapse all child nodes
12828      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12829      */
12830     collapseChildNodes : function(deep){
12831         var cs = this.childNodes;
12832         for(var i = 0, len = cs.length; i < len; i++) {
12833                 cs[i].collapse(deep);
12834         }
12835     },
12836
12837     /**
12838      * Disables this node
12839      */
12840     disable : function(){
12841         this.disabled = true;
12842         this.unselect();
12843         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12844             this.ui.onDisableChange(this, true);
12845         }
12846         this.fireEvent("disabledchange", this, true);
12847     },
12848
12849     /**
12850      * Enables this node
12851      */
12852     enable : function(){
12853         this.disabled = false;
12854         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12855             this.ui.onDisableChange(this, false);
12856         }
12857         this.fireEvent("disabledchange", this, false);
12858     },
12859
12860     // private
12861     renderChildren : function(suppressEvent){
12862         if(suppressEvent !== false){
12863             this.fireEvent("beforechildrenrendered", this);
12864         }
12865         var cs = this.childNodes;
12866         for(var i = 0, len = cs.length; i < len; i++){
12867             cs[i].render(true);
12868         }
12869         this.childrenRendered = true;
12870     },
12871
12872     // private
12873     sort : function(fn, scope){
12874         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12875         if(this.childrenRendered){
12876             var cs = this.childNodes;
12877             for(var i = 0, len = cs.length; i < len; i++){
12878                 cs[i].render(true);
12879             }
12880         }
12881     },
12882
12883     // private
12884     render : function(bulkRender){
12885         this.ui.render(bulkRender);
12886         if(!this.rendered){
12887             this.rendered = true;
12888             if(this.expanded){
12889                 this.expanded = false;
12890                 this.expand(false, false);
12891             }
12892         }
12893     },
12894
12895     // private
12896     renderIndent : function(deep, refresh){
12897         if(refresh){
12898             this.ui.childIndent = null;
12899         }
12900         this.ui.renderIndent();
12901         if(deep === true && this.childrenRendered){
12902             var cs = this.childNodes;
12903             for(var i = 0, len = cs.length; i < len; i++){
12904                 cs[i].renderIndent(true, refresh);
12905             }
12906         }
12907     }
12908 });/*
12909  * Based on:
12910  * Ext JS Library 1.1.1
12911  * Copyright(c) 2006-2007, Ext JS, LLC.
12912  *
12913  * Originally Released Under LGPL - original licence link has changed is not relivant.
12914  *
12915  * Fork - LGPL
12916  * <script type="text/javascript">
12917  */
12918  
12919 /**
12920  * @class Roo.tree.AsyncTreeNode
12921  * @extends Roo.tree.TreeNode
12922  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12923  * @constructor
12924  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12925  */
12926  Roo.tree.AsyncTreeNode = function(config){
12927     this.loaded = false;
12928     this.loading = false;
12929     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12930     /**
12931     * @event beforeload
12932     * Fires before this node is loaded, return false to cancel
12933     * @param {Node} this This node
12934     */
12935     this.addEvents({'beforeload':true, 'load': true});
12936     /**
12937     * @event load
12938     * Fires when this node is loaded
12939     * @param {Node} this This node
12940     */
12941     /**
12942      * The loader used by this node (defaults to using the tree's defined loader)
12943      * @type TreeLoader
12944      * @property loader
12945      */
12946 };
12947 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12948     expand : function(deep, anim, callback){
12949         if(this.loading){ // if an async load is already running, waiting til it's done
12950             var timer;
12951             var f = function(){
12952                 if(!this.loading){ // done loading
12953                     clearInterval(timer);
12954                     this.expand(deep, anim, callback);
12955                 }
12956             }.createDelegate(this);
12957             timer = setInterval(f, 200);
12958             return;
12959         }
12960         if(!this.loaded){
12961             if(this.fireEvent("beforeload", this) === false){
12962                 return;
12963             }
12964             this.loading = true;
12965             this.ui.beforeLoad(this);
12966             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12967             if(loader){
12968                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12969                 return;
12970             }
12971         }
12972         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12973     },
12974     
12975     /**
12976      * Returns true if this node is currently loading
12977      * @return {Boolean}
12978      */
12979     isLoading : function(){
12980         return this.loading;  
12981     },
12982     
12983     loadComplete : function(deep, anim, callback){
12984         this.loading = false;
12985         this.loaded = true;
12986         this.ui.afterLoad(this);
12987         this.fireEvent("load", this);
12988         this.expand(deep, anim, callback);
12989     },
12990     
12991     /**
12992      * Returns true if this node has been loaded
12993      * @return {Boolean}
12994      */
12995     isLoaded : function(){
12996         return this.loaded;
12997     },
12998     
12999     hasChildNodes : function(){
13000         if(!this.isLeaf() && !this.loaded){
13001             return true;
13002         }else{
13003             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
13004         }
13005     },
13006
13007     /**
13008      * Trigger a reload for this node
13009      * @param {Function} callback
13010      */
13011     reload : function(callback){
13012         this.collapse(false, false);
13013         while(this.firstChild){
13014             this.removeChild(this.firstChild);
13015         }
13016         this.childrenRendered = false;
13017         this.loaded = false;
13018         if(this.isHiddenRoot()){
13019             this.expanded = false;
13020         }
13021         this.expand(false, false, callback);
13022     }
13023 });/*
13024  * Based on:
13025  * Ext JS Library 1.1.1
13026  * Copyright(c) 2006-2007, Ext JS, LLC.
13027  *
13028  * Originally Released Under LGPL - original licence link has changed is not relivant.
13029  *
13030  * Fork - LGPL
13031  * <script type="text/javascript">
13032  */
13033  
13034 /**
13035  * @class Roo.tree.TreeNodeUI
13036  * @constructor
13037  * @param {Object} node The node to render
13038  * The TreeNode UI implementation is separate from the
13039  * tree implementation. Unless you are customizing the tree UI,
13040  * you should never have to use this directly.
13041  */
13042 Roo.tree.TreeNodeUI = function(node){
13043     this.node = node;
13044     this.rendered = false;
13045     this.animating = false;
13046     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13047 };
13048
13049 Roo.tree.TreeNodeUI.prototype = {
13050     removeChild : function(node){
13051         if(this.rendered){
13052             this.ctNode.removeChild(node.ui.getEl());
13053         }
13054     },
13055
13056     beforeLoad : function(){
13057          this.addClass("x-tree-node-loading");
13058     },
13059
13060     afterLoad : function(){
13061          this.removeClass("x-tree-node-loading");
13062     },
13063
13064     onTextChange : function(node, text, oldText){
13065         if(this.rendered){
13066             this.textNode.innerHTML = text;
13067         }
13068     },
13069
13070     onDisableChange : function(node, state){
13071         this.disabled = state;
13072         if(state){
13073             this.addClass("x-tree-node-disabled");
13074         }else{
13075             this.removeClass("x-tree-node-disabled");
13076         }
13077     },
13078
13079     onSelectedChange : function(state){
13080         if(state){
13081             this.focus();
13082             this.addClass("x-tree-selected");
13083         }else{
13084             //this.blur();
13085             this.removeClass("x-tree-selected");
13086         }
13087     },
13088
13089     onMove : function(tree, node, oldParent, newParent, index, refNode){
13090         this.childIndent = null;
13091         if(this.rendered){
13092             var targetNode = newParent.ui.getContainer();
13093             if(!targetNode){//target not rendered
13094                 this.holder = document.createElement("div");
13095                 this.holder.appendChild(this.wrap);
13096                 return;
13097             }
13098             var insertBefore = refNode ? refNode.ui.getEl() : null;
13099             if(insertBefore){
13100                 targetNode.insertBefore(this.wrap, insertBefore);
13101             }else{
13102                 targetNode.appendChild(this.wrap);
13103             }
13104             this.node.renderIndent(true);
13105         }
13106     },
13107
13108     addClass : function(cls){
13109         if(this.elNode){
13110             Roo.fly(this.elNode).addClass(cls);
13111         }
13112     },
13113
13114     removeClass : function(cls){
13115         if(this.elNode){
13116             Roo.fly(this.elNode).removeClass(cls);
13117         }
13118     },
13119
13120     remove : function(){
13121         if(this.rendered){
13122             this.holder = document.createElement("div");
13123             this.holder.appendChild(this.wrap);
13124         }
13125     },
13126
13127     fireEvent : function(){
13128         return this.node.fireEvent.apply(this.node, arguments);
13129     },
13130
13131     initEvents : function(){
13132         this.node.on("move", this.onMove, this);
13133         var E = Roo.EventManager;
13134         var a = this.anchor;
13135
13136         var el = Roo.fly(a, '_treeui');
13137
13138         if(Roo.isOpera){ // opera render bug ignores the CSS
13139             el.setStyle("text-decoration", "none");
13140         }
13141
13142         el.on("click", this.onClick, this);
13143         el.on("dblclick", this.onDblClick, this);
13144
13145         if(this.checkbox){
13146             Roo.EventManager.on(this.checkbox,
13147                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13148         }
13149
13150         el.on("contextmenu", this.onContextMenu, this);
13151
13152         var icon = Roo.fly(this.iconNode);
13153         icon.on("click", this.onClick, this);
13154         icon.on("dblclick", this.onDblClick, this);
13155         icon.on("contextmenu", this.onContextMenu, this);
13156         E.on(this.ecNode, "click", this.ecClick, this, true);
13157
13158         if(this.node.disabled){
13159             this.addClass("x-tree-node-disabled");
13160         }
13161         if(this.node.hidden){
13162             this.addClass("x-tree-node-disabled");
13163         }
13164         var ot = this.node.getOwnerTree();
13165         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13166         if(dd && (!this.node.isRoot || ot.rootVisible)){
13167             Roo.dd.Registry.register(this.elNode, {
13168                 node: this.node,
13169                 handles: this.getDDHandles(),
13170                 isHandle: false
13171             });
13172         }
13173     },
13174
13175     getDDHandles : function(){
13176         return [this.iconNode, this.textNode];
13177     },
13178
13179     hide : function(){
13180         if(this.rendered){
13181             this.wrap.style.display = "none";
13182         }
13183     },
13184
13185     show : function(){
13186         if(this.rendered){
13187             this.wrap.style.display = "";
13188         }
13189     },
13190
13191     onContextMenu : function(e){
13192         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13193             e.preventDefault();
13194             this.focus();
13195             this.fireEvent("contextmenu", this.node, e);
13196         }
13197     },
13198
13199     onClick : function(e){
13200         if(this.dropping){
13201             e.stopEvent();
13202             return;
13203         }
13204         if(this.fireEvent("beforeclick", this.node, e) !== false){
13205             if(!this.disabled && this.node.attributes.href){
13206                 this.fireEvent("click", this.node, e);
13207                 return;
13208             }
13209             e.preventDefault();
13210             if(this.disabled){
13211                 return;
13212             }
13213
13214             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13215                 this.node.toggle();
13216             }
13217
13218             this.fireEvent("click", this.node, e);
13219         }else{
13220             e.stopEvent();
13221         }
13222     },
13223
13224     onDblClick : function(e){
13225         e.preventDefault();
13226         if(this.disabled){
13227             return;
13228         }
13229         if(this.checkbox){
13230             this.toggleCheck();
13231         }
13232         if(!this.animating && this.node.hasChildNodes()){
13233             this.node.toggle();
13234         }
13235         this.fireEvent("dblclick", this.node, e);
13236     },
13237
13238     onCheckChange : function(){
13239         var checked = this.checkbox.checked;
13240         this.node.attributes.checked = checked;
13241         this.fireEvent('checkchange', this.node, checked);
13242     },
13243
13244     ecClick : function(e){
13245         if(!this.animating && this.node.hasChildNodes()){
13246             this.node.toggle();
13247         }
13248     },
13249
13250     startDrop : function(){
13251         this.dropping = true;
13252     },
13253
13254     // delayed drop so the click event doesn't get fired on a drop
13255     endDrop : function(){
13256        setTimeout(function(){
13257            this.dropping = false;
13258        }.createDelegate(this), 50);
13259     },
13260
13261     expand : function(){
13262         this.updateExpandIcon();
13263         this.ctNode.style.display = "";
13264     },
13265
13266     focus : function(){
13267         if(!this.node.preventHScroll){
13268             try{this.anchor.focus();
13269             }catch(e){}
13270         }else if(!Roo.isIE){
13271             try{
13272                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13273                 var l = noscroll.scrollLeft;
13274                 this.anchor.focus();
13275                 noscroll.scrollLeft = l;
13276             }catch(e){}
13277         }
13278     },
13279
13280     toggleCheck : function(value){
13281         var cb = this.checkbox;
13282         if(cb){
13283             cb.checked = (value === undefined ? !cb.checked : value);
13284         }
13285     },
13286
13287     blur : function(){
13288         try{
13289             this.anchor.blur();
13290         }catch(e){}
13291     },
13292
13293     animExpand : function(callback){
13294         var ct = Roo.get(this.ctNode);
13295         ct.stopFx();
13296         if(!this.node.hasChildNodes()){
13297             this.updateExpandIcon();
13298             this.ctNode.style.display = "";
13299             Roo.callback(callback);
13300             return;
13301         }
13302         this.animating = true;
13303         this.updateExpandIcon();
13304
13305         ct.slideIn('t', {
13306            callback : function(){
13307                this.animating = false;
13308                Roo.callback(callback);
13309             },
13310             scope: this,
13311             duration: this.node.ownerTree.duration || .25
13312         });
13313     },
13314
13315     highlight : function(){
13316         var tree = this.node.getOwnerTree();
13317         Roo.fly(this.wrap).highlight(
13318             tree.hlColor || "C3DAF9",
13319             {endColor: tree.hlBaseColor}
13320         );
13321     },
13322
13323     collapse : function(){
13324         this.updateExpandIcon();
13325         this.ctNode.style.display = "none";
13326     },
13327
13328     animCollapse : function(callback){
13329         var ct = Roo.get(this.ctNode);
13330         ct.enableDisplayMode('block');
13331         ct.stopFx();
13332
13333         this.animating = true;
13334         this.updateExpandIcon();
13335
13336         ct.slideOut('t', {
13337             callback : function(){
13338                this.animating = false;
13339                Roo.callback(callback);
13340             },
13341             scope: this,
13342             duration: this.node.ownerTree.duration || .25
13343         });
13344     },
13345
13346     getContainer : function(){
13347         return this.ctNode;
13348     },
13349
13350     getEl : function(){
13351         return this.wrap;
13352     },
13353
13354     appendDDGhost : function(ghostNode){
13355         ghostNode.appendChild(this.elNode.cloneNode(true));
13356     },
13357
13358     getDDRepairXY : function(){
13359         return Roo.lib.Dom.getXY(this.iconNode);
13360     },
13361
13362     onRender : function(){
13363         this.render();
13364     },
13365
13366     render : function(bulkRender){
13367         var n = this.node, a = n.attributes;
13368         var targetNode = n.parentNode ?
13369               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13370
13371         if(!this.rendered){
13372             this.rendered = true;
13373
13374             this.renderElements(n, a, targetNode, bulkRender);
13375
13376             if(a.qtip){
13377                if(this.textNode.setAttributeNS){
13378                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13379                    if(a.qtipTitle){
13380                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13381                    }
13382                }else{
13383                    this.textNode.setAttribute("ext:qtip", a.qtip);
13384                    if(a.qtipTitle){
13385                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13386                    }
13387                }
13388             }else if(a.qtipCfg){
13389                 a.qtipCfg.target = Roo.id(this.textNode);
13390                 Roo.QuickTips.register(a.qtipCfg);
13391             }
13392             this.initEvents();
13393             if(!this.node.expanded){
13394                 this.updateExpandIcon();
13395             }
13396         }else{
13397             if(bulkRender === true) {
13398                 targetNode.appendChild(this.wrap);
13399             }
13400         }
13401     },
13402
13403     renderElements : function(n, a, targetNode, bulkRender)
13404     {
13405         // add some indent caching, this helps performance when rendering a large tree
13406         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13407         var t = n.getOwnerTree();
13408         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13409         if (typeof(n.attributes.html) != 'undefined') {
13410             txt = n.attributes.html;
13411         }
13412         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13413         var cb = typeof a.checked == 'boolean';
13414         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13415         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13416             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13417             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13418             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13419             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13420             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13421              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13422                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13423             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13424             "</li>"];
13425
13426         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13427             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13428                                 n.nextSibling.ui.getEl(), buf.join(""));
13429         }else{
13430             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13431         }
13432
13433         this.elNode = this.wrap.childNodes[0];
13434         this.ctNode = this.wrap.childNodes[1];
13435         var cs = this.elNode.childNodes;
13436         this.indentNode = cs[0];
13437         this.ecNode = cs[1];
13438         this.iconNode = cs[2];
13439         var index = 3;
13440         if(cb){
13441             this.checkbox = cs[3];
13442             index++;
13443         }
13444         this.anchor = cs[index];
13445         this.textNode = cs[index].firstChild;
13446     },
13447
13448     getAnchor : function(){
13449         return this.anchor;
13450     },
13451
13452     getTextEl : function(){
13453         return this.textNode;
13454     },
13455
13456     getIconEl : function(){
13457         return this.iconNode;
13458     },
13459
13460     isChecked : function(){
13461         return this.checkbox ? this.checkbox.checked : false;
13462     },
13463
13464     updateExpandIcon : function(){
13465         if(this.rendered){
13466             var n = this.node, c1, c2;
13467             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13468             var hasChild = n.hasChildNodes();
13469             if(hasChild){
13470                 if(n.expanded){
13471                     cls += "-minus";
13472                     c1 = "x-tree-node-collapsed";
13473                     c2 = "x-tree-node-expanded";
13474                 }else{
13475                     cls += "-plus";
13476                     c1 = "x-tree-node-expanded";
13477                     c2 = "x-tree-node-collapsed";
13478                 }
13479                 if(this.wasLeaf){
13480                     this.removeClass("x-tree-node-leaf");
13481                     this.wasLeaf = false;
13482                 }
13483                 if(this.c1 != c1 || this.c2 != c2){
13484                     Roo.fly(this.elNode).replaceClass(c1, c2);
13485                     this.c1 = c1; this.c2 = c2;
13486                 }
13487             }else{
13488                 // this changes non-leafs into leafs if they have no children.
13489                 // it's not very rational behaviour..
13490                 
13491                 if(!this.wasLeaf && this.node.leaf){
13492                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13493                     delete this.c1;
13494                     delete this.c2;
13495                     this.wasLeaf = true;
13496                 }
13497             }
13498             var ecc = "x-tree-ec-icon "+cls;
13499             if(this.ecc != ecc){
13500                 this.ecNode.className = ecc;
13501                 this.ecc = ecc;
13502             }
13503         }
13504     },
13505
13506     getChildIndent : function(){
13507         if(!this.childIndent){
13508             var buf = [];
13509             var p = this.node;
13510             while(p){
13511                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13512                     if(!p.isLast()) {
13513                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13514                     } else {
13515                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13516                     }
13517                 }
13518                 p = p.parentNode;
13519             }
13520             this.childIndent = buf.join("");
13521         }
13522         return this.childIndent;
13523     },
13524
13525     renderIndent : function(){
13526         if(this.rendered){
13527             var indent = "";
13528             var p = this.node.parentNode;
13529             if(p){
13530                 indent = p.ui.getChildIndent();
13531             }
13532             if(this.indentMarkup != indent){ // don't rerender if not required
13533                 this.indentNode.innerHTML = indent;
13534                 this.indentMarkup = indent;
13535             }
13536             this.updateExpandIcon();
13537         }
13538     }
13539 };
13540
13541 Roo.tree.RootTreeNodeUI = function(){
13542     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13543 };
13544 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13545     render : function(){
13546         if(!this.rendered){
13547             var targetNode = this.node.ownerTree.innerCt.dom;
13548             this.node.expanded = true;
13549             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13550             this.wrap = this.ctNode = targetNode.firstChild;
13551         }
13552     },
13553     collapse : function(){
13554     },
13555     expand : function(){
13556     }
13557 });/*
13558  * Based on:
13559  * Ext JS Library 1.1.1
13560  * Copyright(c) 2006-2007, Ext JS, LLC.
13561  *
13562  * Originally Released Under LGPL - original licence link has changed is not relivant.
13563  *
13564  * Fork - LGPL
13565  * <script type="text/javascript">
13566  */
13567 /**
13568  * @class Roo.tree.TreeLoader
13569  * @extends Roo.util.Observable
13570  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13571  * nodes from a specified URL. The response must be a javascript Array definition
13572  * who's elements are node definition objects. eg:
13573  * <pre><code>
13574 {  success : true,
13575    data :      [
13576    
13577     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13578     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13579     ]
13580 }
13581
13582
13583 </code></pre>
13584  * <br><br>
13585  * The old style respose with just an array is still supported, but not recommended.
13586  * <br><br>
13587  *
13588  * A server request is sent, and child nodes are loaded only when a node is expanded.
13589  * The loading node's id is passed to the server under the parameter name "node" to
13590  * enable the server to produce the correct child nodes.
13591  * <br><br>
13592  * To pass extra parameters, an event handler may be attached to the "beforeload"
13593  * event, and the parameters specified in the TreeLoader's baseParams property:
13594  * <pre><code>
13595     myTreeLoader.on("beforeload", function(treeLoader, node) {
13596         this.baseParams.category = node.attributes.category;
13597     }, this);
13598     
13599 </code></pre>
13600  *
13601  * This would pass an HTTP parameter called "category" to the server containing
13602  * the value of the Node's "category" attribute.
13603  * @constructor
13604  * Creates a new Treeloader.
13605  * @param {Object} config A config object containing config properties.
13606  */
13607 Roo.tree.TreeLoader = function(config){
13608     this.baseParams = {};
13609     this.requestMethod = "POST";
13610     Roo.apply(this, config);
13611
13612     this.addEvents({
13613     
13614         /**
13615          * @event beforeload
13616          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13617          * @param {Object} This TreeLoader object.
13618          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13619          * @param {Object} callback The callback function specified in the {@link #load} call.
13620          */
13621         beforeload : true,
13622         /**
13623          * @event load
13624          * Fires when the node has been successfuly loaded.
13625          * @param {Object} This TreeLoader object.
13626          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13627          * @param {Object} response The response object containing the data from the server.
13628          */
13629         load : true,
13630         /**
13631          * @event loadexception
13632          * Fires if the network request failed.
13633          * @param {Object} This TreeLoader object.
13634          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13635          * @param {Object} response The response object containing the data from the server.
13636          */
13637         loadexception : true,
13638         /**
13639          * @event create
13640          * Fires before a node is created, enabling you to return custom Node types 
13641          * @param {Object} This TreeLoader object.
13642          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13643          */
13644         create : true
13645     });
13646
13647     Roo.tree.TreeLoader.superclass.constructor.call(this);
13648 };
13649
13650 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13651     /**
13652     * @cfg {String} dataUrl The URL from which to request a Json string which
13653     * specifies an array of node definition object representing the child nodes
13654     * to be loaded.
13655     */
13656     /**
13657     * @cfg {String} requestMethod either GET or POST
13658     * defaults to POST (due to BC)
13659     * to be loaded.
13660     */
13661     /**
13662     * @cfg {Object} baseParams (optional) An object containing properties which
13663     * specify HTTP parameters to be passed to each request for child nodes.
13664     */
13665     /**
13666     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13667     * created by this loader. If the attributes sent by the server have an attribute in this object,
13668     * they take priority.
13669     */
13670     /**
13671     * @cfg {Object} uiProviders (optional) An object containing properties which
13672     * 
13673     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13674     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13675     * <i>uiProvider</i> attribute of a returned child node is a string rather
13676     * than a reference to a TreeNodeUI implementation, this that string value
13677     * is used as a property name in the uiProviders object. You can define the provider named
13678     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13679     */
13680     uiProviders : {},
13681
13682     /**
13683     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13684     * child nodes before loading.
13685     */
13686     clearOnLoad : true,
13687
13688     /**
13689     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13690     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13691     * Grid query { data : [ .....] }
13692     */
13693     
13694     root : false,
13695      /**
13696     * @cfg {String} queryParam (optional) 
13697     * Name of the query as it will be passed on the querystring (defaults to 'node')
13698     * eg. the request will be ?node=[id]
13699     */
13700     
13701     
13702     queryParam: false,
13703     
13704     /**
13705      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13706      * This is called automatically when a node is expanded, but may be used to reload
13707      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13708      * @param {Roo.tree.TreeNode} node
13709      * @param {Function} callback
13710      */
13711     load : function(node, callback){
13712         if(this.clearOnLoad){
13713             while(node.firstChild){
13714                 node.removeChild(node.firstChild);
13715             }
13716         }
13717         if(node.attributes.children){ // preloaded json children
13718             var cs = node.attributes.children;
13719             for(var i = 0, len = cs.length; i < len; i++){
13720                 node.appendChild(this.createNode(cs[i]));
13721             }
13722             if(typeof callback == "function"){
13723                 callback();
13724             }
13725         }else if(this.dataUrl){
13726             this.requestData(node, callback);
13727         }
13728     },
13729
13730     getParams: function(node){
13731         var buf = [], bp = this.baseParams;
13732         for(var key in bp){
13733             if(typeof bp[key] != "function"){
13734                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13735             }
13736         }
13737         var n = this.queryParam === false ? 'node' : this.queryParam;
13738         buf.push(n + "=", encodeURIComponent(node.id));
13739         return buf.join("");
13740     },
13741
13742     requestData : function(node, callback){
13743         if(this.fireEvent("beforeload", this, node, callback) !== false){
13744             this.transId = Roo.Ajax.request({
13745                 method:this.requestMethod,
13746                 url: this.dataUrl||this.url,
13747                 success: this.handleResponse,
13748                 failure: this.handleFailure,
13749                 scope: this,
13750                 argument: {callback: callback, node: node},
13751                 params: this.getParams(node)
13752             });
13753         }else{
13754             // if the load is cancelled, make sure we notify
13755             // the node that we are done
13756             if(typeof callback == "function"){
13757                 callback();
13758             }
13759         }
13760     },
13761
13762     isLoading : function(){
13763         return this.transId ? true : false;
13764     },
13765
13766     abort : function(){
13767         if(this.isLoading()){
13768             Roo.Ajax.abort(this.transId);
13769         }
13770     },
13771
13772     // private
13773     createNode : function(attr)
13774     {
13775         // apply baseAttrs, nice idea Corey!
13776         if(this.baseAttrs){
13777             Roo.applyIf(attr, this.baseAttrs);
13778         }
13779         if(this.applyLoader !== false){
13780             attr.loader = this;
13781         }
13782         // uiProvider = depreciated..
13783         
13784         if(typeof(attr.uiProvider) == 'string'){
13785            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13786                 /**  eval:var:attr */ eval(attr.uiProvider);
13787         }
13788         if(typeof(this.uiProviders['default']) != 'undefined') {
13789             attr.uiProvider = this.uiProviders['default'];
13790         }
13791         
13792         this.fireEvent('create', this, attr);
13793         
13794         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13795         return(attr.leaf ?
13796                         new Roo.tree.TreeNode(attr) :
13797                         new Roo.tree.AsyncTreeNode(attr));
13798     },
13799
13800     processResponse : function(response, node, callback)
13801     {
13802         var json = response.responseText;
13803         try {
13804             
13805             var o = Roo.decode(json);
13806             
13807             if (this.root === false && typeof(o.success) != undefined) {
13808                 this.root = 'data'; // the default behaviour for list like data..
13809                 }
13810                 
13811             if (this.root !== false &&  !o.success) {
13812                 // it's a failure condition.
13813                 var a = response.argument;
13814                 this.fireEvent("loadexception", this, a.node, response);
13815                 Roo.log("Load failed - should have a handler really");
13816                 return;
13817             }
13818             
13819             
13820             
13821             if (this.root !== false) {
13822                  o = o[this.root];
13823             }
13824             
13825             for(var i = 0, len = o.length; i < len; i++){
13826                 var n = this.createNode(o[i]);
13827                 if(n){
13828                     node.appendChild(n);
13829                 }
13830             }
13831             if(typeof callback == "function"){
13832                 callback(this, node);
13833             }
13834         }catch(e){
13835             this.handleFailure(response);
13836         }
13837     },
13838
13839     handleResponse : function(response){
13840         this.transId = false;
13841         var a = response.argument;
13842         this.processResponse(response, a.node, a.callback);
13843         this.fireEvent("load", this, a.node, response);
13844     },
13845
13846     handleFailure : function(response)
13847     {
13848         // should handle failure better..
13849         this.transId = false;
13850         var a = response.argument;
13851         this.fireEvent("loadexception", this, a.node, response);
13852         if(typeof a.callback == "function"){
13853             a.callback(this, a.node);
13854         }
13855     }
13856 });/*
13857  * Based on:
13858  * Ext JS Library 1.1.1
13859  * Copyright(c) 2006-2007, Ext JS, LLC.
13860  *
13861  * Originally Released Under LGPL - original licence link has changed is not relivant.
13862  *
13863  * Fork - LGPL
13864  * <script type="text/javascript">
13865  */
13866
13867 /**
13868 * @class Roo.tree.TreeFilter
13869 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13870 * @param {TreePanel} tree
13871 * @param {Object} config (optional)
13872  */
13873 Roo.tree.TreeFilter = function(tree, config){
13874     this.tree = tree;
13875     this.filtered = {};
13876     Roo.apply(this, config);
13877 };
13878
13879 Roo.tree.TreeFilter.prototype = {
13880     clearBlank:false,
13881     reverse:false,
13882     autoClear:false,
13883     remove:false,
13884
13885      /**
13886      * Filter the data by a specific attribute.
13887      * @param {String/RegExp} value Either string that the attribute value
13888      * should start with or a RegExp to test against the attribute
13889      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13890      * @param {TreeNode} startNode (optional) The node to start the filter at.
13891      */
13892     filter : function(value, attr, startNode){
13893         attr = attr || "text";
13894         var f;
13895         if(typeof value == "string"){
13896             var vlen = value.length;
13897             // auto clear empty filter
13898             if(vlen == 0 && this.clearBlank){
13899                 this.clear();
13900                 return;
13901             }
13902             value = value.toLowerCase();
13903             f = function(n){
13904                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13905             };
13906         }else if(value.exec){ // regex?
13907             f = function(n){
13908                 return value.test(n.attributes[attr]);
13909             };
13910         }else{
13911             throw 'Illegal filter type, must be string or regex';
13912         }
13913         this.filterBy(f, null, startNode);
13914         },
13915
13916     /**
13917      * Filter by a function. The passed function will be called with each
13918      * node in the tree (or from the startNode). If the function returns true, the node is kept
13919      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13920      * @param {Function} fn The filter function
13921      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13922      */
13923     filterBy : function(fn, scope, startNode){
13924         startNode = startNode || this.tree.root;
13925         if(this.autoClear){
13926             this.clear();
13927         }
13928         var af = this.filtered, rv = this.reverse;
13929         var f = function(n){
13930             if(n == startNode){
13931                 return true;
13932             }
13933             if(af[n.id]){
13934                 return false;
13935             }
13936             var m = fn.call(scope || n, n);
13937             if(!m || rv){
13938                 af[n.id] = n;
13939                 n.ui.hide();
13940                 return false;
13941             }
13942             return true;
13943         };
13944         startNode.cascade(f);
13945         if(this.remove){
13946            for(var id in af){
13947                if(typeof id != "function"){
13948                    var n = af[id];
13949                    if(n && n.parentNode){
13950                        n.parentNode.removeChild(n);
13951                    }
13952                }
13953            }
13954         }
13955     },
13956
13957     /**
13958      * Clears the current filter. Note: with the "remove" option
13959      * set a filter cannot be cleared.
13960      */
13961     clear : function(){
13962         var t = this.tree;
13963         var af = this.filtered;
13964         for(var id in af){
13965             if(typeof id != "function"){
13966                 var n = af[id];
13967                 if(n){
13968                     n.ui.show();
13969                 }
13970             }
13971         }
13972         this.filtered = {};
13973     }
13974 };
13975 /*
13976  * Based on:
13977  * Ext JS Library 1.1.1
13978  * Copyright(c) 2006-2007, Ext JS, LLC.
13979  *
13980  * Originally Released Under LGPL - original licence link has changed is not relivant.
13981  *
13982  * Fork - LGPL
13983  * <script type="text/javascript">
13984  */
13985  
13986
13987 /**
13988  * @class Roo.tree.TreeSorter
13989  * Provides sorting of nodes in a TreePanel
13990  * 
13991  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13992  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13993  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13994  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13995  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13996  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13997  * @constructor
13998  * @param {TreePanel} tree
13999  * @param {Object} config
14000  */
14001 Roo.tree.TreeSorter = function(tree, config){
14002     Roo.apply(this, config);
14003     tree.on("beforechildrenrendered", this.doSort, this);
14004     tree.on("append", this.updateSort, this);
14005     tree.on("insert", this.updateSort, this);
14006     
14007     var dsc = this.dir && this.dir.toLowerCase() == "desc";
14008     var p = this.property || "text";
14009     var sortType = this.sortType;
14010     var fs = this.folderSort;
14011     var cs = this.caseSensitive === true;
14012     var leafAttr = this.leafAttr || 'leaf';
14013
14014     this.sortFn = function(n1, n2){
14015         if(fs){
14016             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14017                 return 1;
14018             }
14019             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14020                 return -1;
14021             }
14022         }
14023         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14024         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14025         if(v1 < v2){
14026                         return dsc ? +1 : -1;
14027                 }else if(v1 > v2){
14028                         return dsc ? -1 : +1;
14029         }else{
14030                 return 0;
14031         }
14032     };
14033 };
14034
14035 Roo.tree.TreeSorter.prototype = {
14036     doSort : function(node){
14037         node.sort(this.sortFn);
14038     },
14039     
14040     compareNodes : function(n1, n2){
14041         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14042     },
14043     
14044     updateSort : function(tree, node){
14045         if(node.childrenRendered){
14046             this.doSort.defer(1, this, [node]);
14047         }
14048     }
14049 };/*
14050  * Based on:
14051  * Ext JS Library 1.1.1
14052  * Copyright(c) 2006-2007, Ext JS, LLC.
14053  *
14054  * Originally Released Under LGPL - original licence link has changed is not relivant.
14055  *
14056  * Fork - LGPL
14057  * <script type="text/javascript">
14058  */
14059
14060 if(Roo.dd.DropZone){
14061     
14062 Roo.tree.TreeDropZone = function(tree, config){
14063     this.allowParentInsert = false;
14064     this.allowContainerDrop = false;
14065     this.appendOnly = false;
14066     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14067     this.tree = tree;
14068     this.lastInsertClass = "x-tree-no-status";
14069     this.dragOverData = {};
14070 };
14071
14072 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14073     ddGroup : "TreeDD",
14074     scroll:  true,
14075     
14076     expandDelay : 1000,
14077     
14078     expandNode : function(node){
14079         if(node.hasChildNodes() && !node.isExpanded()){
14080             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14081         }
14082     },
14083     
14084     queueExpand : function(node){
14085         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14086     },
14087     
14088     cancelExpand : function(){
14089         if(this.expandProcId){
14090             clearTimeout(this.expandProcId);
14091             this.expandProcId = false;
14092         }
14093     },
14094     
14095     isValidDropPoint : function(n, pt, dd, e, data){
14096         if(!n || !data){ return false; }
14097         var targetNode = n.node;
14098         var dropNode = data.node;
14099         // default drop rules
14100         if(!(targetNode && targetNode.isTarget && pt)){
14101             return false;
14102         }
14103         if(pt == "append" && targetNode.allowChildren === false){
14104             return false;
14105         }
14106         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14107             return false;
14108         }
14109         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14110             return false;
14111         }
14112         // reuse the object
14113         var overEvent = this.dragOverData;
14114         overEvent.tree = this.tree;
14115         overEvent.target = targetNode;
14116         overEvent.data = data;
14117         overEvent.point = pt;
14118         overEvent.source = dd;
14119         overEvent.rawEvent = e;
14120         overEvent.dropNode = dropNode;
14121         overEvent.cancel = false;  
14122         var result = this.tree.fireEvent("nodedragover", overEvent);
14123         return overEvent.cancel === false && result !== false;
14124     },
14125     
14126     getDropPoint : function(e, n, dd)
14127     {
14128         var tn = n.node;
14129         if(tn.isRoot){
14130             return tn.allowChildren !== false ? "append" : false; // always append for root
14131         }
14132         var dragEl = n.ddel;
14133         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14134         var y = Roo.lib.Event.getPageY(e);
14135         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14136         
14137         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14138         var noAppend = tn.allowChildren === false;
14139         if(this.appendOnly || tn.parentNode.allowChildren === false){
14140             return noAppend ? false : "append";
14141         }
14142         var noBelow = false;
14143         if(!this.allowParentInsert){
14144             noBelow = tn.hasChildNodes() && tn.isExpanded();
14145         }
14146         var q = (b - t) / (noAppend ? 2 : 3);
14147         if(y >= t && y < (t + q)){
14148             return "above";
14149         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14150             return "below";
14151         }else{
14152             return "append";
14153         }
14154     },
14155     
14156     onNodeEnter : function(n, dd, e, data)
14157     {
14158         this.cancelExpand();
14159     },
14160     
14161     onNodeOver : function(n, dd, e, data)
14162     {
14163        
14164         var pt = this.getDropPoint(e, n, dd);
14165         var node = n.node;
14166         
14167         // auto node expand check
14168         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14169             this.queueExpand(node);
14170         }else if(pt != "append"){
14171             this.cancelExpand();
14172         }
14173         
14174         // set the insert point style on the target node
14175         var returnCls = this.dropNotAllowed;
14176         if(this.isValidDropPoint(n, pt, dd, e, data)){
14177            if(pt){
14178                var el = n.ddel;
14179                var cls;
14180                if(pt == "above"){
14181                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14182                    cls = "x-tree-drag-insert-above";
14183                }else if(pt == "below"){
14184                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14185                    cls = "x-tree-drag-insert-below";
14186                }else{
14187                    returnCls = "x-tree-drop-ok-append";
14188                    cls = "x-tree-drag-append";
14189                }
14190                if(this.lastInsertClass != cls){
14191                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14192                    this.lastInsertClass = cls;
14193                }
14194            }
14195        }
14196        return returnCls;
14197     },
14198     
14199     onNodeOut : function(n, dd, e, data){
14200         
14201         this.cancelExpand();
14202         this.removeDropIndicators(n);
14203     },
14204     
14205     onNodeDrop : function(n, dd, e, data){
14206         var point = this.getDropPoint(e, n, dd);
14207         var targetNode = n.node;
14208         targetNode.ui.startDrop();
14209         if(!this.isValidDropPoint(n, point, dd, e, data)){
14210             targetNode.ui.endDrop();
14211             return false;
14212         }
14213         // first try to find the drop node
14214         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14215         var dropEvent = {
14216             tree : this.tree,
14217             target: targetNode,
14218             data: data,
14219             point: point,
14220             source: dd,
14221             rawEvent: e,
14222             dropNode: dropNode,
14223             cancel: !dropNode   
14224         };
14225         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14226         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14227             targetNode.ui.endDrop();
14228             return false;
14229         }
14230         // allow target changing
14231         targetNode = dropEvent.target;
14232         if(point == "append" && !targetNode.isExpanded()){
14233             targetNode.expand(false, null, function(){
14234                 this.completeDrop(dropEvent);
14235             }.createDelegate(this));
14236         }else{
14237             this.completeDrop(dropEvent);
14238         }
14239         return true;
14240     },
14241     
14242     completeDrop : function(de){
14243         var ns = de.dropNode, p = de.point, t = de.target;
14244         if(!(ns instanceof Array)){
14245             ns = [ns];
14246         }
14247         var n;
14248         for(var i = 0, len = ns.length; i < len; i++){
14249             n = ns[i];
14250             if(p == "above"){
14251                 t.parentNode.insertBefore(n, t);
14252             }else if(p == "below"){
14253                 t.parentNode.insertBefore(n, t.nextSibling);
14254             }else{
14255                 t.appendChild(n);
14256             }
14257         }
14258         n.ui.focus();
14259         if(this.tree.hlDrop){
14260             n.ui.highlight();
14261         }
14262         t.ui.endDrop();
14263         this.tree.fireEvent("nodedrop", de);
14264     },
14265     
14266     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14267         if(this.tree.hlDrop){
14268             dropNode.ui.focus();
14269             dropNode.ui.highlight();
14270         }
14271         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14272     },
14273     
14274     getTree : function(){
14275         return this.tree;
14276     },
14277     
14278     removeDropIndicators : function(n){
14279         if(n && n.ddel){
14280             var el = n.ddel;
14281             Roo.fly(el).removeClass([
14282                     "x-tree-drag-insert-above",
14283                     "x-tree-drag-insert-below",
14284                     "x-tree-drag-append"]);
14285             this.lastInsertClass = "_noclass";
14286         }
14287     },
14288     
14289     beforeDragDrop : function(target, e, id){
14290         this.cancelExpand();
14291         return true;
14292     },
14293     
14294     afterRepair : function(data){
14295         if(data && Roo.enableFx){
14296             data.node.ui.highlight();
14297         }
14298         this.hideProxy();
14299     } 
14300     
14301 });
14302
14303 }
14304 /*
14305  * Based on:
14306  * Ext JS Library 1.1.1
14307  * Copyright(c) 2006-2007, Ext JS, LLC.
14308  *
14309  * Originally Released Under LGPL - original licence link has changed is not relivant.
14310  *
14311  * Fork - LGPL
14312  * <script type="text/javascript">
14313  */
14314  
14315
14316 if(Roo.dd.DragZone){
14317 Roo.tree.TreeDragZone = function(tree, config){
14318     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14319     this.tree = tree;
14320 };
14321
14322 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14323     ddGroup : "TreeDD",
14324    
14325     onBeforeDrag : function(data, e){
14326         var n = data.node;
14327         return n && n.draggable && !n.disabled;
14328     },
14329      
14330     
14331     onInitDrag : function(e){
14332         var data = this.dragData;
14333         this.tree.getSelectionModel().select(data.node);
14334         this.proxy.update("");
14335         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14336         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14337     },
14338     
14339     getRepairXY : function(e, data){
14340         return data.node.ui.getDDRepairXY();
14341     },
14342     
14343     onEndDrag : function(data, e){
14344         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14345         
14346         
14347     },
14348     
14349     onValidDrop : function(dd, e, id){
14350         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14351         this.hideProxy();
14352     },
14353     
14354     beforeInvalidDrop : function(e, id){
14355         // this scrolls the original position back into view
14356         var sm = this.tree.getSelectionModel();
14357         sm.clearSelections();
14358         sm.select(this.dragData.node);
14359     }
14360 });
14361 }/*
14362  * Based on:
14363  * Ext JS Library 1.1.1
14364  * Copyright(c) 2006-2007, Ext JS, LLC.
14365  *
14366  * Originally Released Under LGPL - original licence link has changed is not relivant.
14367  *
14368  * Fork - LGPL
14369  * <script type="text/javascript">
14370  */
14371 /**
14372  * @class Roo.tree.TreeEditor
14373  * @extends Roo.Editor
14374  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14375  * as the editor field.
14376  * @constructor
14377  * @param {Object} config (used to be the tree panel.)
14378  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14379  * 
14380  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14381  * @cfg {Roo.form.TextField|Object} field The field configuration
14382  *
14383  * 
14384  */
14385 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14386     var tree = config;
14387     var field;
14388     if (oldconfig) { // old style..
14389         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14390     } else {
14391         // new style..
14392         tree = config.tree;
14393         config.field = config.field  || {};
14394         config.field.xtype = 'TextField';
14395         field = Roo.factory(config.field, Roo.form);
14396     }
14397     config = config || {};
14398     
14399     
14400     this.addEvents({
14401         /**
14402          * @event beforenodeedit
14403          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14404          * false from the handler of this event.
14405          * @param {Editor} this
14406          * @param {Roo.tree.Node} node 
14407          */
14408         "beforenodeedit" : true
14409     });
14410     
14411     //Roo.log(config);
14412     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14413
14414     this.tree = tree;
14415
14416     tree.on('beforeclick', this.beforeNodeClick, this);
14417     tree.getTreeEl().on('mousedown', this.hide, this);
14418     this.on('complete', this.updateNode, this);
14419     this.on('beforestartedit', this.fitToTree, this);
14420     this.on('startedit', this.bindScroll, this, {delay:10});
14421     this.on('specialkey', this.onSpecialKey, this);
14422 };
14423
14424 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14425     /**
14426      * @cfg {String} alignment
14427      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14428      */
14429     alignment: "l-l",
14430     // inherit
14431     autoSize: false,
14432     /**
14433      * @cfg {Boolean} hideEl
14434      * True to hide the bound element while the editor is displayed (defaults to false)
14435      */
14436     hideEl : false,
14437     /**
14438      * @cfg {String} cls
14439      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14440      */
14441     cls: "x-small-editor x-tree-editor",
14442     /**
14443      * @cfg {Boolean} shim
14444      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14445      */
14446     shim:false,
14447     // inherit
14448     shadow:"frame",
14449     /**
14450      * @cfg {Number} maxWidth
14451      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14452      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14453      * scroll and client offsets into account prior to each edit.
14454      */
14455     maxWidth: 250,
14456
14457     editDelay : 350,
14458
14459     // private
14460     fitToTree : function(ed, el){
14461         var td = this.tree.getTreeEl().dom, nd = el.dom;
14462         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14463             td.scrollLeft = nd.offsetLeft;
14464         }
14465         var w = Math.min(
14466                 this.maxWidth,
14467                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14468         this.setSize(w, '');
14469         
14470         return this.fireEvent('beforenodeedit', this, this.editNode);
14471         
14472     },
14473
14474     // private
14475     triggerEdit : function(node){
14476         this.completeEdit();
14477         this.editNode = node;
14478         this.startEdit(node.ui.textNode, node.text);
14479     },
14480
14481     // private
14482     bindScroll : function(){
14483         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14484     },
14485
14486     // private
14487     beforeNodeClick : function(node, e){
14488         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14489         this.lastClick = new Date();
14490         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14491             e.stopEvent();
14492             this.triggerEdit(node);
14493             return false;
14494         }
14495         return true;
14496     },
14497
14498     // private
14499     updateNode : function(ed, value){
14500         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14501         this.editNode.setText(value);
14502     },
14503
14504     // private
14505     onHide : function(){
14506         Roo.tree.TreeEditor.superclass.onHide.call(this);
14507         if(this.editNode){
14508             this.editNode.ui.focus();
14509         }
14510     },
14511
14512     // private
14513     onSpecialKey : function(field, e){
14514         var k = e.getKey();
14515         if(k == e.ESC){
14516             e.stopEvent();
14517             this.cancelEdit();
14518         }else if(k == e.ENTER && !e.hasModifier()){
14519             e.stopEvent();
14520             this.completeEdit();
14521         }
14522     }
14523 });//<Script type="text/javascript">
14524 /*
14525  * Based on:
14526  * Ext JS Library 1.1.1
14527  * Copyright(c) 2006-2007, Ext JS, LLC.
14528  *
14529  * Originally Released Under LGPL - original licence link has changed is not relivant.
14530  *
14531  * Fork - LGPL
14532  * <script type="text/javascript">
14533  */
14534  
14535 /**
14536  * Not documented??? - probably should be...
14537  */
14538
14539 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14540     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14541     
14542     renderElements : function(n, a, targetNode, bulkRender){
14543         //consel.log("renderElements?");
14544         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14545
14546         var t = n.getOwnerTree();
14547         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14548         
14549         var cols = t.columns;
14550         var bw = t.borderWidth;
14551         var c = cols[0];
14552         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14553          var cb = typeof a.checked == "boolean";
14554         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14555         var colcls = 'x-t-' + tid + '-c0';
14556         var buf = [
14557             '<li class="x-tree-node">',
14558             
14559                 
14560                 '<div class="x-tree-node-el ', a.cls,'">',
14561                     // extran...
14562                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14563                 
14564                 
14565                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14566                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14567                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14568                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14569                            (a.iconCls ? ' '+a.iconCls : ''),
14570                            '" unselectable="on" />',
14571                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14572                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14573                              
14574                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14575                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14576                             '<span unselectable="on" qtip="' + tx + '">',
14577                              tx,
14578                              '</span></a>' ,
14579                     '</div>',
14580                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14581                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14582                  ];
14583         for(var i = 1, len = cols.length; i < len; i++){
14584             c = cols[i];
14585             colcls = 'x-t-' + tid + '-c' +i;
14586             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14587             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14588                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14589                       "</div>");
14590          }
14591          
14592          buf.push(
14593             '</a>',
14594             '<div class="x-clear"></div></div>',
14595             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14596             "</li>");
14597         
14598         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14599             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14600                                 n.nextSibling.ui.getEl(), buf.join(""));
14601         }else{
14602             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14603         }
14604         var el = this.wrap.firstChild;
14605         this.elRow = el;
14606         this.elNode = el.firstChild;
14607         this.ranchor = el.childNodes[1];
14608         this.ctNode = this.wrap.childNodes[1];
14609         var cs = el.firstChild.childNodes;
14610         this.indentNode = cs[0];
14611         this.ecNode = cs[1];
14612         this.iconNode = cs[2];
14613         var index = 3;
14614         if(cb){
14615             this.checkbox = cs[3];
14616             index++;
14617         }
14618         this.anchor = cs[index];
14619         
14620         this.textNode = cs[index].firstChild;
14621         
14622         //el.on("click", this.onClick, this);
14623         //el.on("dblclick", this.onDblClick, this);
14624         
14625         
14626        // console.log(this);
14627     },
14628     initEvents : function(){
14629         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14630         
14631             
14632         var a = this.ranchor;
14633
14634         var el = Roo.get(a);
14635
14636         if(Roo.isOpera){ // opera render bug ignores the CSS
14637             el.setStyle("text-decoration", "none");
14638         }
14639
14640         el.on("click", this.onClick, this);
14641         el.on("dblclick", this.onDblClick, this);
14642         el.on("contextmenu", this.onContextMenu, this);
14643         
14644     },
14645     
14646     /*onSelectedChange : function(state){
14647         if(state){
14648             this.focus();
14649             this.addClass("x-tree-selected");
14650         }else{
14651             //this.blur();
14652             this.removeClass("x-tree-selected");
14653         }
14654     },*/
14655     addClass : function(cls){
14656         if(this.elRow){
14657             Roo.fly(this.elRow).addClass(cls);
14658         }
14659         
14660     },
14661     
14662     
14663     removeClass : function(cls){
14664         if(this.elRow){
14665             Roo.fly(this.elRow).removeClass(cls);
14666         }
14667     }
14668
14669     
14670     
14671 });//<Script type="text/javascript">
14672
14673 /*
14674  * Based on:
14675  * Ext JS Library 1.1.1
14676  * Copyright(c) 2006-2007, Ext JS, LLC.
14677  *
14678  * Originally Released Under LGPL - original licence link has changed is not relivant.
14679  *
14680  * Fork - LGPL
14681  * <script type="text/javascript">
14682  */
14683  
14684
14685 /**
14686  * @class Roo.tree.ColumnTree
14687  * @extends Roo.data.TreePanel
14688  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14689  * @cfg {int} borderWidth  compined right/left border allowance
14690  * @constructor
14691  * @param {String/HTMLElement/Element} el The container element
14692  * @param {Object} config
14693  */
14694 Roo.tree.ColumnTree =  function(el, config)
14695 {
14696    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14697    this.addEvents({
14698         /**
14699         * @event resize
14700         * Fire this event on a container when it resizes
14701         * @param {int} w Width
14702         * @param {int} h Height
14703         */
14704        "resize" : true
14705     });
14706     this.on('resize', this.onResize, this);
14707 };
14708
14709 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14710     //lines:false,
14711     
14712     
14713     borderWidth: Roo.isBorderBox ? 0 : 2, 
14714     headEls : false,
14715     
14716     render : function(){
14717         // add the header.....
14718        
14719         Roo.tree.ColumnTree.superclass.render.apply(this);
14720         
14721         this.el.addClass('x-column-tree');
14722         
14723         this.headers = this.el.createChild(
14724             {cls:'x-tree-headers'},this.innerCt.dom);
14725    
14726         var cols = this.columns, c;
14727         var totalWidth = 0;
14728         this.headEls = [];
14729         var  len = cols.length;
14730         for(var i = 0; i < len; i++){
14731              c = cols[i];
14732              totalWidth += c.width;
14733             this.headEls.push(this.headers.createChild({
14734                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14735                  cn: {
14736                      cls:'x-tree-hd-text',
14737                      html: c.header
14738                  },
14739                  style:'width:'+(c.width-this.borderWidth)+'px;'
14740              }));
14741         }
14742         this.headers.createChild({cls:'x-clear'});
14743         // prevent floats from wrapping when clipped
14744         this.headers.setWidth(totalWidth);
14745         //this.innerCt.setWidth(totalWidth);
14746         this.innerCt.setStyle({ overflow: 'auto' });
14747         this.onResize(this.width, this.height);
14748              
14749         
14750     },
14751     onResize : function(w,h)
14752     {
14753         this.height = h;
14754         this.width = w;
14755         // resize cols..
14756         this.innerCt.setWidth(this.width);
14757         this.innerCt.setHeight(this.height-20);
14758         
14759         // headers...
14760         var cols = this.columns, c;
14761         var totalWidth = 0;
14762         var expEl = false;
14763         var len = cols.length;
14764         for(var i = 0; i < len; i++){
14765             c = cols[i];
14766             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14767                 // it's the expander..
14768                 expEl  = this.headEls[i];
14769                 continue;
14770             }
14771             totalWidth += c.width;
14772             
14773         }
14774         if (expEl) {
14775             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14776         }
14777         this.headers.setWidth(w-20);
14778
14779         
14780         
14781         
14782     }
14783 });
14784 /*
14785  * Based on:
14786  * Ext JS Library 1.1.1
14787  * Copyright(c) 2006-2007, Ext JS, LLC.
14788  *
14789  * Originally Released Under LGPL - original licence link has changed is not relivant.
14790  *
14791  * Fork - LGPL
14792  * <script type="text/javascript">
14793  */
14794  
14795 /**
14796  * @class Roo.menu.Menu
14797  * @extends Roo.util.Observable
14798  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14799  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14800  * @constructor
14801  * Creates a new Menu
14802  * @param {Object} config Configuration options
14803  */
14804 Roo.menu.Menu = function(config){
14805     
14806     Roo.menu.Menu.superclass.constructor.call(this, config);
14807     
14808     this.id = this.id || Roo.id();
14809     this.addEvents({
14810         /**
14811          * @event beforeshow
14812          * Fires before this menu is displayed
14813          * @param {Roo.menu.Menu} this
14814          */
14815         beforeshow : true,
14816         /**
14817          * @event beforehide
14818          * Fires before this menu is hidden
14819          * @param {Roo.menu.Menu} this
14820          */
14821         beforehide : true,
14822         /**
14823          * @event show
14824          * Fires after this menu is displayed
14825          * @param {Roo.menu.Menu} this
14826          */
14827         show : true,
14828         /**
14829          * @event hide
14830          * Fires after this menu is hidden
14831          * @param {Roo.menu.Menu} this
14832          */
14833         hide : true,
14834         /**
14835          * @event click
14836          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14837          * @param {Roo.menu.Menu} this
14838          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14839          * @param {Roo.EventObject} e
14840          */
14841         click : true,
14842         /**
14843          * @event mouseover
14844          * Fires when the mouse is hovering over this menu
14845          * @param {Roo.menu.Menu} this
14846          * @param {Roo.EventObject} e
14847          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14848          */
14849         mouseover : true,
14850         /**
14851          * @event mouseout
14852          * Fires when the mouse exits this menu
14853          * @param {Roo.menu.Menu} this
14854          * @param {Roo.EventObject} e
14855          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14856          */
14857         mouseout : true,
14858         /**
14859          * @event itemclick
14860          * Fires when a menu item contained in this menu is clicked
14861          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14862          * @param {Roo.EventObject} e
14863          */
14864         itemclick: true
14865     });
14866     if (this.registerMenu) {
14867         Roo.menu.MenuMgr.register(this);
14868     }
14869     
14870     var mis = this.items;
14871     this.items = new Roo.util.MixedCollection();
14872     if(mis){
14873         this.add.apply(this, mis);
14874     }
14875 };
14876
14877 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14878     /**
14879      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14880      */
14881     minWidth : 120,
14882     /**
14883      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14884      * for bottom-right shadow (defaults to "sides")
14885      */
14886     shadow : "sides",
14887     /**
14888      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14889      * this menu (defaults to "tl-tr?")
14890      */
14891     subMenuAlign : "tl-tr?",
14892     /**
14893      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14894      * relative to its element of origin (defaults to "tl-bl?")
14895      */
14896     defaultAlign : "tl-bl?",
14897     /**
14898      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14899      */
14900     allowOtherMenus : false,
14901     /**
14902      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14903      */
14904     registerMenu : true,
14905
14906     hidden:true,
14907
14908     // private
14909     render : function(){
14910         if(this.el){
14911             return;
14912         }
14913         var el = this.el = new Roo.Layer({
14914             cls: "x-menu",
14915             shadow:this.shadow,
14916             constrain: false,
14917             parentEl: this.parentEl || document.body,
14918             zindex:15000
14919         });
14920
14921         this.keyNav = new Roo.menu.MenuNav(this);
14922
14923         if(this.plain){
14924             el.addClass("x-menu-plain");
14925         }
14926         if(this.cls){
14927             el.addClass(this.cls);
14928         }
14929         // generic focus element
14930         this.focusEl = el.createChild({
14931             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14932         });
14933         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14934         //disabling touch- as it's causing issues ..
14935         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14936         ul.on('click'   , this.onClick, this);
14937         
14938         
14939         ul.on("mouseover", this.onMouseOver, this);
14940         ul.on("mouseout", this.onMouseOut, this);
14941         this.items.each(function(item){
14942             if (item.hidden) {
14943                 return;
14944             }
14945             
14946             var li = document.createElement("li");
14947             li.className = "x-menu-list-item";
14948             ul.dom.appendChild(li);
14949             item.render(li, this);
14950         }, this);
14951         this.ul = ul;
14952         this.autoWidth();
14953     },
14954
14955     // private
14956     autoWidth : function(){
14957         var el = this.el, ul = this.ul;
14958         if(!el){
14959             return;
14960         }
14961         var w = this.width;
14962         if(w){
14963             el.setWidth(w);
14964         }else if(Roo.isIE){
14965             el.setWidth(this.minWidth);
14966             var t = el.dom.offsetWidth; // force recalc
14967             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14968         }
14969     },
14970
14971     // private
14972     delayAutoWidth : function(){
14973         if(this.rendered){
14974             if(!this.awTask){
14975                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14976             }
14977             this.awTask.delay(20);
14978         }
14979     },
14980
14981     // private
14982     findTargetItem : function(e){
14983         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14984         if(t && t.menuItemId){
14985             return this.items.get(t.menuItemId);
14986         }
14987     },
14988
14989     // private
14990     onClick : function(e){
14991         Roo.log("menu.onClick");
14992         var t = this.findTargetItem(e);
14993         if(!t){
14994             return;
14995         }
14996         Roo.log(e);
14997         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14998             if(t == this.activeItem && t.shouldDeactivate(e)){
14999                 this.activeItem.deactivate();
15000                 delete this.activeItem;
15001                 return;
15002             }
15003             if(t.canActivate){
15004                 this.setActiveItem(t, true);
15005             }
15006             return;
15007             
15008             
15009         }
15010         
15011         t.onClick(e);
15012         this.fireEvent("click", this, t, e);
15013     },
15014
15015     // private
15016     setActiveItem : function(item, autoExpand){
15017         if(item != this.activeItem){
15018             if(this.activeItem){
15019                 this.activeItem.deactivate();
15020             }
15021             this.activeItem = item;
15022             item.activate(autoExpand);
15023         }else if(autoExpand){
15024             item.expandMenu();
15025         }
15026     },
15027
15028     // private
15029     tryActivate : function(start, step){
15030         var items = this.items;
15031         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15032             var item = items.get(i);
15033             if(!item.disabled && item.canActivate){
15034                 this.setActiveItem(item, false);
15035                 return item;
15036             }
15037         }
15038         return false;
15039     },
15040
15041     // private
15042     onMouseOver : function(e){
15043         var t;
15044         if(t = this.findTargetItem(e)){
15045             if(t.canActivate && !t.disabled){
15046                 this.setActiveItem(t, true);
15047             }
15048         }
15049         this.fireEvent("mouseover", this, e, t);
15050     },
15051
15052     // private
15053     onMouseOut : function(e){
15054         var t;
15055         if(t = this.findTargetItem(e)){
15056             if(t == this.activeItem && t.shouldDeactivate(e)){
15057                 this.activeItem.deactivate();
15058                 delete this.activeItem;
15059             }
15060         }
15061         this.fireEvent("mouseout", this, e, t);
15062     },
15063
15064     /**
15065      * Read-only.  Returns true if the menu is currently displayed, else false.
15066      * @type Boolean
15067      */
15068     isVisible : function(){
15069         return this.el && !this.hidden;
15070     },
15071
15072     /**
15073      * Displays this menu relative to another element
15074      * @param {String/HTMLElement/Roo.Element} element The element to align to
15075      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15076      * the element (defaults to this.defaultAlign)
15077      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15078      */
15079     show : function(el, pos, parentMenu){
15080         this.parentMenu = parentMenu;
15081         if(!this.el){
15082             this.render();
15083         }
15084         this.fireEvent("beforeshow", this);
15085         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15086     },
15087
15088     /**
15089      * Displays this menu at a specific xy position
15090      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15091      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15092      */
15093     showAt : function(xy, parentMenu, /* private: */_e){
15094         this.parentMenu = parentMenu;
15095         if(!this.el){
15096             this.render();
15097         }
15098         if(_e !== false){
15099             this.fireEvent("beforeshow", this);
15100             xy = this.el.adjustForConstraints(xy);
15101         }
15102         this.el.setXY(xy);
15103         this.el.show();
15104         this.hidden = false;
15105         this.focus();
15106         this.fireEvent("show", this);
15107     },
15108
15109     focus : function(){
15110         if(!this.hidden){
15111             this.doFocus.defer(50, this);
15112         }
15113     },
15114
15115     doFocus : function(){
15116         if(!this.hidden){
15117             this.focusEl.focus();
15118         }
15119     },
15120
15121     /**
15122      * Hides this menu and optionally all parent menus
15123      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15124      */
15125     hide : function(deep){
15126         if(this.el && this.isVisible()){
15127             this.fireEvent("beforehide", this);
15128             if(this.activeItem){
15129                 this.activeItem.deactivate();
15130                 this.activeItem = null;
15131             }
15132             this.el.hide();
15133             this.hidden = true;
15134             this.fireEvent("hide", this);
15135         }
15136         if(deep === true && this.parentMenu){
15137             this.parentMenu.hide(true);
15138         }
15139     },
15140
15141     /**
15142      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15143      * Any of the following are valid:
15144      * <ul>
15145      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15146      * <li>An HTMLElement object which will be converted to a menu item</li>
15147      * <li>A menu item config object that will be created as a new menu item</li>
15148      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15149      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15150      * </ul>
15151      * Usage:
15152      * <pre><code>
15153 // Create the menu
15154 var menu = new Roo.menu.Menu();
15155
15156 // Create a menu item to add by reference
15157 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15158
15159 // Add a bunch of items at once using different methods.
15160 // Only the last item added will be returned.
15161 var item = menu.add(
15162     menuItem,                // add existing item by ref
15163     'Dynamic Item',          // new TextItem
15164     '-',                     // new separator
15165     { text: 'Config Item' }  // new item by config
15166 );
15167 </code></pre>
15168      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15169      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15170      */
15171     add : function(){
15172         var a = arguments, l = a.length, item;
15173         for(var i = 0; i < l; i++){
15174             var el = a[i];
15175             if ((typeof(el) == "object") && el.xtype && el.xns) {
15176                 el = Roo.factory(el, Roo.menu);
15177             }
15178             
15179             if(el.render){ // some kind of Item
15180                 item = this.addItem(el);
15181             }else if(typeof el == "string"){ // string
15182                 if(el == "separator" || el == "-"){
15183                     item = this.addSeparator();
15184                 }else{
15185                     item = this.addText(el);
15186                 }
15187             }else if(el.tagName || el.el){ // element
15188                 item = this.addElement(el);
15189             }else if(typeof el == "object"){ // must be menu item config?
15190                 item = this.addMenuItem(el);
15191             }
15192         }
15193         return item;
15194     },
15195
15196     /**
15197      * Returns this menu's underlying {@link Roo.Element} object
15198      * @return {Roo.Element} The element
15199      */
15200     getEl : function(){
15201         if(!this.el){
15202             this.render();
15203         }
15204         return this.el;
15205     },
15206
15207     /**
15208      * Adds a separator bar to the menu
15209      * @return {Roo.menu.Item} The menu item that was added
15210      */
15211     addSeparator : function(){
15212         return this.addItem(new Roo.menu.Separator());
15213     },
15214
15215     /**
15216      * Adds an {@link Roo.Element} object to the menu
15217      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15218      * @return {Roo.menu.Item} The menu item that was added
15219      */
15220     addElement : function(el){
15221         return this.addItem(new Roo.menu.BaseItem(el));
15222     },
15223
15224     /**
15225      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15226      * @param {Roo.menu.Item} item The menu item to add
15227      * @return {Roo.menu.Item} The menu item that was added
15228      */
15229     addItem : function(item){
15230         this.items.add(item);
15231         if(this.ul){
15232             var li = document.createElement("li");
15233             li.className = "x-menu-list-item";
15234             this.ul.dom.appendChild(li);
15235             item.render(li, this);
15236             this.delayAutoWidth();
15237         }
15238         return item;
15239     },
15240
15241     /**
15242      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15243      * @param {Object} config A MenuItem config object
15244      * @return {Roo.menu.Item} The menu item that was added
15245      */
15246     addMenuItem : function(config){
15247         if(!(config instanceof Roo.menu.Item)){
15248             if(typeof config.checked == "boolean"){ // must be check menu item config?
15249                 config = new Roo.menu.CheckItem(config);
15250             }else{
15251                 config = new Roo.menu.Item(config);
15252             }
15253         }
15254         return this.addItem(config);
15255     },
15256
15257     /**
15258      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15259      * @param {String} text The text to display in the menu item
15260      * @return {Roo.menu.Item} The menu item that was added
15261      */
15262     addText : function(text){
15263         return this.addItem(new Roo.menu.TextItem({ text : text }));
15264     },
15265
15266     /**
15267      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15268      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15269      * @param {Roo.menu.Item} item The menu item to add
15270      * @return {Roo.menu.Item} The menu item that was added
15271      */
15272     insert : function(index, item){
15273         this.items.insert(index, item);
15274         if(this.ul){
15275             var li = document.createElement("li");
15276             li.className = "x-menu-list-item";
15277             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15278             item.render(li, this);
15279             this.delayAutoWidth();
15280         }
15281         return item;
15282     },
15283
15284     /**
15285      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15286      * @param {Roo.menu.Item} item The menu item to remove
15287      */
15288     remove : function(item){
15289         this.items.removeKey(item.id);
15290         item.destroy();
15291     },
15292
15293     /**
15294      * Removes and destroys all items in the menu
15295      */
15296     removeAll : function(){
15297         var f;
15298         while(f = this.items.first()){
15299             this.remove(f);
15300         }
15301     }
15302 });
15303
15304 // MenuNav is a private utility class used internally by the Menu
15305 Roo.menu.MenuNav = function(menu){
15306     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15307     this.scope = this.menu = menu;
15308 };
15309
15310 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15311     doRelay : function(e, h){
15312         var k = e.getKey();
15313         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15314             this.menu.tryActivate(0, 1);
15315             return false;
15316         }
15317         return h.call(this.scope || this, e, this.menu);
15318     },
15319
15320     up : function(e, m){
15321         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15322             m.tryActivate(m.items.length-1, -1);
15323         }
15324     },
15325
15326     down : function(e, m){
15327         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15328             m.tryActivate(0, 1);
15329         }
15330     },
15331
15332     right : function(e, m){
15333         if(m.activeItem){
15334             m.activeItem.expandMenu(true);
15335         }
15336     },
15337
15338     left : function(e, m){
15339         m.hide();
15340         if(m.parentMenu && m.parentMenu.activeItem){
15341             m.parentMenu.activeItem.activate();
15342         }
15343     },
15344
15345     enter : function(e, m){
15346         if(m.activeItem){
15347             e.stopPropagation();
15348             m.activeItem.onClick(e);
15349             m.fireEvent("click", this, m.activeItem);
15350             return true;
15351         }
15352     }
15353 });/*
15354  * Based on:
15355  * Ext JS Library 1.1.1
15356  * Copyright(c) 2006-2007, Ext JS, LLC.
15357  *
15358  * Originally Released Under LGPL - original licence link has changed is not relivant.
15359  *
15360  * Fork - LGPL
15361  * <script type="text/javascript">
15362  */
15363  
15364 /**
15365  * @class Roo.menu.MenuMgr
15366  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15367  * @singleton
15368  */
15369 Roo.menu.MenuMgr = function(){
15370    var menus, active, groups = {}, attached = false, lastShow = new Date();
15371
15372    // private - called when first menu is created
15373    function init(){
15374        menus = {};
15375        active = new Roo.util.MixedCollection();
15376        Roo.get(document).addKeyListener(27, function(){
15377            if(active.length > 0){
15378                hideAll();
15379            }
15380        });
15381    }
15382
15383    // private
15384    function hideAll(){
15385        if(active && active.length > 0){
15386            var c = active.clone();
15387            c.each(function(m){
15388                m.hide();
15389            });
15390        }
15391    }
15392
15393    // private
15394    function onHide(m){
15395        active.remove(m);
15396        if(active.length < 1){
15397            Roo.get(document).un("mousedown", onMouseDown);
15398            attached = false;
15399        }
15400    }
15401
15402    // private
15403    function onShow(m){
15404        var last = active.last();
15405        lastShow = new Date();
15406        active.add(m);
15407        if(!attached){
15408            Roo.get(document).on("mousedown", onMouseDown);
15409            attached = true;
15410        }
15411        if(m.parentMenu){
15412           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15413           m.parentMenu.activeChild = m;
15414        }else if(last && last.isVisible()){
15415           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15416        }
15417    }
15418
15419    // private
15420    function onBeforeHide(m){
15421        if(m.activeChild){
15422            m.activeChild.hide();
15423        }
15424        if(m.autoHideTimer){
15425            clearTimeout(m.autoHideTimer);
15426            delete m.autoHideTimer;
15427        }
15428    }
15429
15430    // private
15431    function onBeforeShow(m){
15432        var pm = m.parentMenu;
15433        if(!pm && !m.allowOtherMenus){
15434            hideAll();
15435        }else if(pm && pm.activeChild && active != m){
15436            pm.activeChild.hide();
15437        }
15438    }
15439
15440    // private
15441    function onMouseDown(e){
15442        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15443            hideAll();
15444        }
15445    }
15446
15447    // private
15448    function onBeforeCheck(mi, state){
15449        if(state){
15450            var g = groups[mi.group];
15451            for(var i = 0, l = g.length; i < l; i++){
15452                if(g[i] != mi){
15453                    g[i].setChecked(false);
15454                }
15455            }
15456        }
15457    }
15458
15459    return {
15460
15461        /**
15462         * Hides all menus that are currently visible
15463         */
15464        hideAll : function(){
15465             hideAll();  
15466        },
15467
15468        // private
15469        register : function(menu){
15470            if(!menus){
15471                init();
15472            }
15473            menus[menu.id] = menu;
15474            menu.on("beforehide", onBeforeHide);
15475            menu.on("hide", onHide);
15476            menu.on("beforeshow", onBeforeShow);
15477            menu.on("show", onShow);
15478            var g = menu.group;
15479            if(g && menu.events["checkchange"]){
15480                if(!groups[g]){
15481                    groups[g] = [];
15482                }
15483                groups[g].push(menu);
15484                menu.on("checkchange", onCheck);
15485            }
15486        },
15487
15488         /**
15489          * Returns a {@link Roo.menu.Menu} object
15490          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15491          * be used to generate and return a new Menu instance.
15492          */
15493        get : function(menu){
15494            if(typeof menu == "string"){ // menu id
15495                return menus[menu];
15496            }else if(menu.events){  // menu instance
15497                return menu;
15498            }else if(typeof menu.length == 'number'){ // array of menu items?
15499                return new Roo.menu.Menu({items:menu});
15500            }else{ // otherwise, must be a config
15501                return new Roo.menu.Menu(menu);
15502            }
15503        },
15504
15505        // private
15506        unregister : function(menu){
15507            delete menus[menu.id];
15508            menu.un("beforehide", onBeforeHide);
15509            menu.un("hide", onHide);
15510            menu.un("beforeshow", onBeforeShow);
15511            menu.un("show", onShow);
15512            var g = menu.group;
15513            if(g && menu.events["checkchange"]){
15514                groups[g].remove(menu);
15515                menu.un("checkchange", onCheck);
15516            }
15517        },
15518
15519        // private
15520        registerCheckable : function(menuItem){
15521            var g = menuItem.group;
15522            if(g){
15523                if(!groups[g]){
15524                    groups[g] = [];
15525                }
15526                groups[g].push(menuItem);
15527                menuItem.on("beforecheckchange", onBeforeCheck);
15528            }
15529        },
15530
15531        // private
15532        unregisterCheckable : function(menuItem){
15533            var g = menuItem.group;
15534            if(g){
15535                groups[g].remove(menuItem);
15536                menuItem.un("beforecheckchange", onBeforeCheck);
15537            }
15538        }
15539    };
15540 }();/*
15541  * Based on:
15542  * Ext JS Library 1.1.1
15543  * Copyright(c) 2006-2007, Ext JS, LLC.
15544  *
15545  * Originally Released Under LGPL - original licence link has changed is not relivant.
15546  *
15547  * Fork - LGPL
15548  * <script type="text/javascript">
15549  */
15550  
15551
15552 /**
15553  * @class Roo.menu.BaseItem
15554  * @extends Roo.Component
15555  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15556  * management and base configuration options shared by all menu components.
15557  * @constructor
15558  * Creates a new BaseItem
15559  * @param {Object} config Configuration options
15560  */
15561 Roo.menu.BaseItem = function(config){
15562     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15563
15564     this.addEvents({
15565         /**
15566          * @event click
15567          * Fires when this item is clicked
15568          * @param {Roo.menu.BaseItem} this
15569          * @param {Roo.EventObject} e
15570          */
15571         click: true,
15572         /**
15573          * @event activate
15574          * Fires when this item is activated
15575          * @param {Roo.menu.BaseItem} this
15576          */
15577         activate : true,
15578         /**
15579          * @event deactivate
15580          * Fires when this item is deactivated
15581          * @param {Roo.menu.BaseItem} this
15582          */
15583         deactivate : true
15584     });
15585
15586     if(this.handler){
15587         this.on("click", this.handler, this.scope, true);
15588     }
15589 };
15590
15591 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15592     /**
15593      * @cfg {Function} handler
15594      * A function that will handle the click event of this menu item (defaults to undefined)
15595      */
15596     /**
15597      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15598      */
15599     canActivate : false,
15600     
15601      /**
15602      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15603      */
15604     hidden: false,
15605     
15606     /**
15607      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15608      */
15609     activeClass : "x-menu-item-active",
15610     /**
15611      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15612      */
15613     hideOnClick : true,
15614     /**
15615      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15616      */
15617     hideDelay : 100,
15618
15619     // private
15620     ctype: "Roo.menu.BaseItem",
15621
15622     // private
15623     actionMode : "container",
15624
15625     // private
15626     render : function(container, parentMenu){
15627         this.parentMenu = parentMenu;
15628         Roo.menu.BaseItem.superclass.render.call(this, container);
15629         this.container.menuItemId = this.id;
15630     },
15631
15632     // private
15633     onRender : function(container, position){
15634         this.el = Roo.get(this.el);
15635         container.dom.appendChild(this.el.dom);
15636     },
15637
15638     // private
15639     onClick : function(e){
15640         if(!this.disabled && this.fireEvent("click", this, e) !== false
15641                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15642             this.handleClick(e);
15643         }else{
15644             e.stopEvent();
15645         }
15646     },
15647
15648     // private
15649     activate : function(){
15650         if(this.disabled){
15651             return false;
15652         }
15653         var li = this.container;
15654         li.addClass(this.activeClass);
15655         this.region = li.getRegion().adjust(2, 2, -2, -2);
15656         this.fireEvent("activate", this);
15657         return true;
15658     },
15659
15660     // private
15661     deactivate : function(){
15662         this.container.removeClass(this.activeClass);
15663         this.fireEvent("deactivate", this);
15664     },
15665
15666     // private
15667     shouldDeactivate : function(e){
15668         return !this.region || !this.region.contains(e.getPoint());
15669     },
15670
15671     // private
15672     handleClick : function(e){
15673         if(this.hideOnClick){
15674             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15675         }
15676     },
15677
15678     // private
15679     expandMenu : function(autoActivate){
15680         // do nothing
15681     },
15682
15683     // private
15684     hideMenu : function(){
15685         // do nothing
15686     }
15687 });/*
15688  * Based on:
15689  * Ext JS Library 1.1.1
15690  * Copyright(c) 2006-2007, Ext JS, LLC.
15691  *
15692  * Originally Released Under LGPL - original licence link has changed is not relivant.
15693  *
15694  * Fork - LGPL
15695  * <script type="text/javascript">
15696  */
15697  
15698 /**
15699  * @class Roo.menu.Adapter
15700  * @extends Roo.menu.BaseItem
15701  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
15702  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15703  * @constructor
15704  * Creates a new Adapter
15705  * @param {Object} config Configuration options
15706  */
15707 Roo.menu.Adapter = function(component, config){
15708     Roo.menu.Adapter.superclass.constructor.call(this, config);
15709     this.component = component;
15710 };
15711 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15712     // private
15713     canActivate : true,
15714
15715     // private
15716     onRender : function(container, position){
15717         this.component.render(container);
15718         this.el = this.component.getEl();
15719     },
15720
15721     // private
15722     activate : function(){
15723         if(this.disabled){
15724             return false;
15725         }
15726         this.component.focus();
15727         this.fireEvent("activate", this);
15728         return true;
15729     },
15730
15731     // private
15732     deactivate : function(){
15733         this.fireEvent("deactivate", this);
15734     },
15735
15736     // private
15737     disable : function(){
15738         this.component.disable();
15739         Roo.menu.Adapter.superclass.disable.call(this);
15740     },
15741
15742     // private
15743     enable : function(){
15744         this.component.enable();
15745         Roo.menu.Adapter.superclass.enable.call(this);
15746     }
15747 });/*
15748  * Based on:
15749  * Ext JS Library 1.1.1
15750  * Copyright(c) 2006-2007, Ext JS, LLC.
15751  *
15752  * Originally Released Under LGPL - original licence link has changed is not relivant.
15753  *
15754  * Fork - LGPL
15755  * <script type="text/javascript">
15756  */
15757
15758 /**
15759  * @class Roo.menu.TextItem
15760  * @extends Roo.menu.BaseItem
15761  * Adds a static text string to a menu, usually used as either a heading or group separator.
15762  * Note: old style constructor with text is still supported.
15763  * 
15764  * @constructor
15765  * Creates a new TextItem
15766  * @param {Object} cfg Configuration
15767  */
15768 Roo.menu.TextItem = function(cfg){
15769     if (typeof(cfg) == 'string') {
15770         this.text = cfg;
15771     } else {
15772         Roo.apply(this,cfg);
15773     }
15774     
15775     Roo.menu.TextItem.superclass.constructor.call(this);
15776 };
15777
15778 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15779     /**
15780      * @cfg {Boolean} text Text to show on item.
15781      */
15782     text : '',
15783     
15784     /**
15785      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15786      */
15787     hideOnClick : false,
15788     /**
15789      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15790      */
15791     itemCls : "x-menu-text",
15792
15793     // private
15794     onRender : function(){
15795         var s = document.createElement("span");
15796         s.className = this.itemCls;
15797         s.innerHTML = this.text;
15798         this.el = s;
15799         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15800     }
15801 });/*
15802  * Based on:
15803  * Ext JS Library 1.1.1
15804  * Copyright(c) 2006-2007, Ext JS, LLC.
15805  *
15806  * Originally Released Under LGPL - original licence link has changed is not relivant.
15807  *
15808  * Fork - LGPL
15809  * <script type="text/javascript">
15810  */
15811
15812 /**
15813  * @class Roo.menu.Separator
15814  * @extends Roo.menu.BaseItem
15815  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15816  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15817  * @constructor
15818  * @param {Object} config Configuration options
15819  */
15820 Roo.menu.Separator = function(config){
15821     Roo.menu.Separator.superclass.constructor.call(this, config);
15822 };
15823
15824 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15825     /**
15826      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15827      */
15828     itemCls : "x-menu-sep",
15829     /**
15830      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15831      */
15832     hideOnClick : false,
15833
15834     // private
15835     onRender : function(li){
15836         var s = document.createElement("span");
15837         s.className = this.itemCls;
15838         s.innerHTML = "&#160;";
15839         this.el = s;
15840         li.addClass("x-menu-sep-li");
15841         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15842     }
15843 });/*
15844  * Based on:
15845  * Ext JS Library 1.1.1
15846  * Copyright(c) 2006-2007, Ext JS, LLC.
15847  *
15848  * Originally Released Under LGPL - original licence link has changed is not relivant.
15849  *
15850  * Fork - LGPL
15851  * <script type="text/javascript">
15852  */
15853 /**
15854  * @class Roo.menu.Item
15855  * @extends Roo.menu.BaseItem
15856  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15857  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15858  * activation and click handling.
15859  * @constructor
15860  * Creates a new Item
15861  * @param {Object} config Configuration options
15862  */
15863 Roo.menu.Item = function(config){
15864     Roo.menu.Item.superclass.constructor.call(this, config);
15865     if(this.menu){
15866         this.menu = Roo.menu.MenuMgr.get(this.menu);
15867     }
15868 };
15869 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15870     
15871     /**
15872      * @cfg {String} text
15873      * The text to show on the menu item.
15874      */
15875     text: '',
15876      /**
15877      * @cfg {String} HTML to render in menu
15878      * The text to show on the menu item (HTML version).
15879      */
15880     html: '',
15881     /**
15882      * @cfg {String} icon
15883      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15884      */
15885     icon: undefined,
15886     /**
15887      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15888      */
15889     itemCls : "x-menu-item",
15890     /**
15891      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15892      */
15893     canActivate : true,
15894     /**
15895      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15896      */
15897     showDelay: 200,
15898     // doc'd in BaseItem
15899     hideDelay: 200,
15900
15901     // private
15902     ctype: "Roo.menu.Item",
15903     
15904     // private
15905     onRender : function(container, position){
15906         var el = document.createElement("a");
15907         el.hideFocus = true;
15908         el.unselectable = "on";
15909         el.href = this.href || "#";
15910         if(this.hrefTarget){
15911             el.target = this.hrefTarget;
15912         }
15913         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15914         
15915         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15916         
15917         el.innerHTML = String.format(
15918                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15919                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15920         this.el = el;
15921         Roo.menu.Item.superclass.onRender.call(this, container, position);
15922     },
15923
15924     /**
15925      * Sets the text to display in this menu item
15926      * @param {String} text The text to display
15927      * @param {Boolean} isHTML true to indicate text is pure html.
15928      */
15929     setText : function(text, isHTML){
15930         if (isHTML) {
15931             this.html = text;
15932         } else {
15933             this.text = text;
15934             this.html = '';
15935         }
15936         if(this.rendered){
15937             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15938      
15939             this.el.update(String.format(
15940                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15941                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15942             this.parentMenu.autoWidth();
15943         }
15944     },
15945
15946     // private
15947     handleClick : function(e){
15948         if(!this.href){ // if no link defined, stop the event automatically
15949             e.stopEvent();
15950         }
15951         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15952     },
15953
15954     // private
15955     activate : function(autoExpand){
15956         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15957             this.focus();
15958             if(autoExpand){
15959                 this.expandMenu();
15960             }
15961         }
15962         return true;
15963     },
15964
15965     // private
15966     shouldDeactivate : function(e){
15967         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15968             if(this.menu && this.menu.isVisible()){
15969                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15970             }
15971             return true;
15972         }
15973         return false;
15974     },
15975
15976     // private
15977     deactivate : function(){
15978         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15979         this.hideMenu();
15980     },
15981
15982     // private
15983     expandMenu : function(autoActivate){
15984         if(!this.disabled && this.menu){
15985             clearTimeout(this.hideTimer);
15986             delete this.hideTimer;
15987             if(!this.menu.isVisible() && !this.showTimer){
15988                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15989             }else if (this.menu.isVisible() && autoActivate){
15990                 this.menu.tryActivate(0, 1);
15991             }
15992         }
15993     },
15994
15995     // private
15996     deferExpand : function(autoActivate){
15997         delete this.showTimer;
15998         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15999         if(autoActivate){
16000             this.menu.tryActivate(0, 1);
16001         }
16002     },
16003
16004     // private
16005     hideMenu : function(){
16006         clearTimeout(this.showTimer);
16007         delete this.showTimer;
16008         if(!this.hideTimer && this.menu && this.menu.isVisible()){
16009             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
16010         }
16011     },
16012
16013     // private
16014     deferHide : function(){
16015         delete this.hideTimer;
16016         this.menu.hide();
16017     }
16018 });/*
16019  * Based on:
16020  * Ext JS Library 1.1.1
16021  * Copyright(c) 2006-2007, Ext JS, LLC.
16022  *
16023  * Originally Released Under LGPL - original licence link has changed is not relivant.
16024  *
16025  * Fork - LGPL
16026  * <script type="text/javascript">
16027  */
16028  
16029 /**
16030  * @class Roo.menu.CheckItem
16031  * @extends Roo.menu.Item
16032  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16033  * @constructor
16034  * Creates a new CheckItem
16035  * @param {Object} config Configuration options
16036  */
16037 Roo.menu.CheckItem = function(config){
16038     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16039     this.addEvents({
16040         /**
16041          * @event beforecheckchange
16042          * Fires before the checked value is set, providing an opportunity to cancel if needed
16043          * @param {Roo.menu.CheckItem} this
16044          * @param {Boolean} checked The new checked value that will be set
16045          */
16046         "beforecheckchange" : true,
16047         /**
16048          * @event checkchange
16049          * Fires after the checked value has been set
16050          * @param {Roo.menu.CheckItem} this
16051          * @param {Boolean} checked The checked value that was set
16052          */
16053         "checkchange" : true
16054     });
16055     if(this.checkHandler){
16056         this.on('checkchange', this.checkHandler, this.scope);
16057     }
16058 };
16059 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16060     /**
16061      * @cfg {String} group
16062      * All check items with the same group name will automatically be grouped into a single-select
16063      * radio button group (defaults to '')
16064      */
16065     /**
16066      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16067      */
16068     itemCls : "x-menu-item x-menu-check-item",
16069     /**
16070      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16071      */
16072     groupClass : "x-menu-group-item",
16073
16074     /**
16075      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16076      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16077      * initialized with checked = true will be rendered as checked.
16078      */
16079     checked: false,
16080
16081     // private
16082     ctype: "Roo.menu.CheckItem",
16083
16084     // private
16085     onRender : function(c){
16086         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16087         if(this.group){
16088             this.el.addClass(this.groupClass);
16089         }
16090         Roo.menu.MenuMgr.registerCheckable(this);
16091         if(this.checked){
16092             this.checked = false;
16093             this.setChecked(true, true);
16094         }
16095     },
16096
16097     // private
16098     destroy : function(){
16099         if(this.rendered){
16100             Roo.menu.MenuMgr.unregisterCheckable(this);
16101         }
16102         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16103     },
16104
16105     /**
16106      * Set the checked state of this item
16107      * @param {Boolean} checked The new checked value
16108      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16109      */
16110     setChecked : function(state, suppressEvent){
16111         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16112             if(this.container){
16113                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16114             }
16115             this.checked = state;
16116             if(suppressEvent !== true){
16117                 this.fireEvent("checkchange", this, state);
16118             }
16119         }
16120     },
16121
16122     // private
16123     handleClick : function(e){
16124        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16125            this.setChecked(!this.checked);
16126        }
16127        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16128     }
16129 });/*
16130  * Based on:
16131  * Ext JS Library 1.1.1
16132  * Copyright(c) 2006-2007, Ext JS, LLC.
16133  *
16134  * Originally Released Under LGPL - original licence link has changed is not relivant.
16135  *
16136  * Fork - LGPL
16137  * <script type="text/javascript">
16138  */
16139  
16140 /**
16141  * @class Roo.menu.DateItem
16142  * @extends Roo.menu.Adapter
16143  * A menu item that wraps the {@link Roo.DatPicker} component.
16144  * @constructor
16145  * Creates a new DateItem
16146  * @param {Object} config Configuration options
16147  */
16148 Roo.menu.DateItem = function(config){
16149     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16150     /** The Roo.DatePicker object @type Roo.DatePicker */
16151     this.picker = this.component;
16152     this.addEvents({select: true});
16153     
16154     this.picker.on("render", function(picker){
16155         picker.getEl().swallowEvent("click");
16156         picker.container.addClass("x-menu-date-item");
16157     });
16158
16159     this.picker.on("select", this.onSelect, this);
16160 };
16161
16162 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16163     // private
16164     onSelect : function(picker, date){
16165         this.fireEvent("select", this, date, picker);
16166         Roo.menu.DateItem.superclass.handleClick.call(this);
16167     }
16168 });/*
16169  * Based on:
16170  * Ext JS Library 1.1.1
16171  * Copyright(c) 2006-2007, Ext JS, LLC.
16172  *
16173  * Originally Released Under LGPL - original licence link has changed is not relivant.
16174  *
16175  * Fork - LGPL
16176  * <script type="text/javascript">
16177  */
16178  
16179 /**
16180  * @class Roo.menu.ColorItem
16181  * @extends Roo.menu.Adapter
16182  * A menu item that wraps the {@link Roo.ColorPalette} component.
16183  * @constructor
16184  * Creates a new ColorItem
16185  * @param {Object} config Configuration options
16186  */
16187 Roo.menu.ColorItem = function(config){
16188     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16189     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16190     this.palette = this.component;
16191     this.relayEvents(this.palette, ["select"]);
16192     if(this.selectHandler){
16193         this.on('select', this.selectHandler, this.scope);
16194     }
16195 };
16196 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16197  * Based on:
16198  * Ext JS Library 1.1.1
16199  * Copyright(c) 2006-2007, Ext JS, LLC.
16200  *
16201  * Originally Released Under LGPL - original licence link has changed is not relivant.
16202  *
16203  * Fork - LGPL
16204  * <script type="text/javascript">
16205  */
16206  
16207
16208 /**
16209  * @class Roo.menu.DateMenu
16210  * @extends Roo.menu.Menu
16211  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16212  * @constructor
16213  * Creates a new DateMenu
16214  * @param {Object} config Configuration options
16215  */
16216 Roo.menu.DateMenu = function(config){
16217     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16218     this.plain = true;
16219     var di = new Roo.menu.DateItem(config);
16220     this.add(di);
16221     /**
16222      * The {@link Roo.DatePicker} instance for this DateMenu
16223      * @type DatePicker
16224      */
16225     this.picker = di.picker;
16226     /**
16227      * @event select
16228      * @param {DatePicker} picker
16229      * @param {Date} date
16230      */
16231     this.relayEvents(di, ["select"]);
16232     this.on('beforeshow', function(){
16233         if(this.picker){
16234             this.picker.hideMonthPicker(false);
16235         }
16236     }, this);
16237 };
16238 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16239     cls:'x-date-menu'
16240 });/*
16241  * Based on:
16242  * Ext JS Library 1.1.1
16243  * Copyright(c) 2006-2007, Ext JS, LLC.
16244  *
16245  * Originally Released Under LGPL - original licence link has changed is not relivant.
16246  *
16247  * Fork - LGPL
16248  * <script type="text/javascript">
16249  */
16250  
16251
16252 /**
16253  * @class Roo.menu.ColorMenu
16254  * @extends Roo.menu.Menu
16255  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16256  * @constructor
16257  * Creates a new ColorMenu
16258  * @param {Object} config Configuration options
16259  */
16260 Roo.menu.ColorMenu = function(config){
16261     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16262     this.plain = true;
16263     var ci = new Roo.menu.ColorItem(config);
16264     this.add(ci);
16265     /**
16266      * The {@link Roo.ColorPalette} instance for this ColorMenu
16267      * @type ColorPalette
16268      */
16269     this.palette = ci.palette;
16270     /**
16271      * @event select
16272      * @param {ColorPalette} palette
16273      * @param {String} color
16274      */
16275     this.relayEvents(ci, ["select"]);
16276 };
16277 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16278  * Based on:
16279  * Ext JS Library 1.1.1
16280  * Copyright(c) 2006-2007, Ext JS, LLC.
16281  *
16282  * Originally Released Under LGPL - original licence link has changed is not relivant.
16283  *
16284  * Fork - LGPL
16285  * <script type="text/javascript">
16286  */
16287  
16288 /**
16289  * @class Roo.form.TextItem
16290  * @extends Roo.BoxComponent
16291  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16292  * @constructor
16293  * Creates a new TextItem
16294  * @param {Object} config Configuration options
16295  */
16296 Roo.form.TextItem = function(config){
16297     Roo.form.TextItem.superclass.constructor.call(this, config);
16298 };
16299
16300 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16301     
16302     /**
16303      * @cfg {String} tag the tag for this item (default div)
16304      */
16305     tag : 'div',
16306     /**
16307      * @cfg {String} html the content for this item
16308      */
16309     html : '',
16310     
16311     getAutoCreate : function()
16312     {
16313         var cfg = {
16314             id: this.id,
16315             tag: this.tag,
16316             html: this.html,
16317             cls: 'x-form-item'
16318         };
16319         
16320         return cfg;
16321         
16322     },
16323     
16324     onRender : function(ct, position)
16325     {
16326         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16327         
16328         if(!this.el){
16329             var cfg = this.getAutoCreate();
16330             if(!cfg.name){
16331                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16332             }
16333             if (!cfg.name.length) {
16334                 delete cfg.name;
16335             }
16336             this.el = ct.createChild(cfg, position);
16337         }
16338     }
16339     
16340 });/*
16341  * Based on:
16342  * Ext JS Library 1.1.1
16343  * Copyright(c) 2006-2007, Ext JS, LLC.
16344  *
16345  * Originally Released Under LGPL - original licence link has changed is not relivant.
16346  *
16347  * Fork - LGPL
16348  * <script type="text/javascript">
16349  */
16350  
16351 /**
16352  * @class Roo.form.Field
16353  * @extends Roo.BoxComponent
16354  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16355  * @constructor
16356  * Creates a new Field
16357  * @param {Object} config Configuration options
16358  */
16359 Roo.form.Field = function(config){
16360     Roo.form.Field.superclass.constructor.call(this, config);
16361 };
16362
16363 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16364     /**
16365      * @cfg {String} fieldLabel Label to use when rendering a form.
16366      */
16367        /**
16368      * @cfg {String} qtip Mouse over tip
16369      */
16370      
16371     /**
16372      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16373      */
16374     invalidClass : "x-form-invalid",
16375     /**
16376      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
16377      */
16378     invalidText : "The value in this field is invalid",
16379     /**
16380      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16381      */
16382     focusClass : "x-form-focus",
16383     /**
16384      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16385       automatic validation (defaults to "keyup").
16386      */
16387     validationEvent : "keyup",
16388     /**
16389      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16390      */
16391     validateOnBlur : true,
16392     /**
16393      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16394      */
16395     validationDelay : 250,
16396     /**
16397      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16398      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16399      */
16400     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16401     /**
16402      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16403      */
16404     fieldClass : "x-form-field",
16405     /**
16406      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16407      *<pre>
16408 Value         Description
16409 -----------   ----------------------------------------------------------------------
16410 qtip          Display a quick tip when the user hovers over the field
16411 title         Display a default browser title attribute popup
16412 under         Add a block div beneath the field containing the error text
16413 side          Add an error icon to the right of the field with a popup on hover
16414 [element id]  Add the error text directly to the innerHTML of the specified element
16415 </pre>
16416      */
16417     msgTarget : 'qtip',
16418     /**
16419      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16420      */
16421     msgFx : 'normal',
16422
16423     /**
16424      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
16425      */
16426     readOnly : false,
16427
16428     /**
16429      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16430      */
16431     disabled : false,
16432
16433     /**
16434      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16435      */
16436     inputType : undefined,
16437     
16438     /**
16439      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
16440          */
16441         tabIndex : undefined,
16442         
16443     // private
16444     isFormField : true,
16445
16446     // private
16447     hasFocus : false,
16448     /**
16449      * @property {Roo.Element} fieldEl
16450      * Element Containing the rendered Field (with label etc.)
16451      */
16452     /**
16453      * @cfg {Mixed} value A value to initialize this field with.
16454      */
16455     value : undefined,
16456
16457     /**
16458      * @cfg {String} name The field's HTML name attribute.
16459      */
16460     /**
16461      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16462      */
16463     // private
16464     loadedValue : false,
16465      
16466      
16467         // private ??
16468         initComponent : function(){
16469         Roo.form.Field.superclass.initComponent.call(this);
16470         this.addEvents({
16471             /**
16472              * @event focus
16473              * Fires when this field receives input focus.
16474              * @param {Roo.form.Field} this
16475              */
16476             focus : true,
16477             /**
16478              * @event blur
16479              * Fires when this field loses input focus.
16480              * @param {Roo.form.Field} this
16481              */
16482             blur : true,
16483             /**
16484              * @event specialkey
16485              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16486              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16487              * @param {Roo.form.Field} this
16488              * @param {Roo.EventObject} e The event object
16489              */
16490             specialkey : true,
16491             /**
16492              * @event change
16493              * Fires just before the field blurs if the field value has changed.
16494              * @param {Roo.form.Field} this
16495              * @param {Mixed} newValue The new value
16496              * @param {Mixed} oldValue The original value
16497              */
16498             change : true,
16499             /**
16500              * @event invalid
16501              * Fires after the field has been marked as invalid.
16502              * @param {Roo.form.Field} this
16503              * @param {String} msg The validation message
16504              */
16505             invalid : true,
16506             /**
16507              * @event valid
16508              * Fires after the field has been validated with no errors.
16509              * @param {Roo.form.Field} this
16510              */
16511             valid : true,
16512              /**
16513              * @event keyup
16514              * Fires after the key up
16515              * @param {Roo.form.Field} this
16516              * @param {Roo.EventObject}  e The event Object
16517              */
16518             keyup : true
16519         });
16520     },
16521
16522     /**
16523      * Returns the name attribute of the field if available
16524      * @return {String} name The field name
16525      */
16526     getName: function(){
16527          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16528     },
16529
16530     // private
16531     onRender : function(ct, position){
16532         Roo.form.Field.superclass.onRender.call(this, ct, position);
16533         if(!this.el){
16534             var cfg = this.getAutoCreate();
16535             if(!cfg.name){
16536                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16537             }
16538             if (!cfg.name.length) {
16539                 delete cfg.name;
16540             }
16541             if(this.inputType){
16542                 cfg.type = this.inputType;
16543             }
16544             this.el = ct.createChild(cfg, position);
16545         }
16546         var type = this.el.dom.type;
16547         if(type){
16548             if(type == 'password'){
16549                 type = 'text';
16550             }
16551             this.el.addClass('x-form-'+type);
16552         }
16553         if(this.readOnly){
16554             this.el.dom.readOnly = true;
16555         }
16556         if(this.tabIndex !== undefined){
16557             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16558         }
16559
16560         this.el.addClass([this.fieldClass, this.cls]);
16561         this.initValue();
16562     },
16563
16564     /**
16565      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16566      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16567      * @return {Roo.form.Field} this
16568      */
16569     applyTo : function(target){
16570         this.allowDomMove = false;
16571         this.el = Roo.get(target);
16572         this.render(this.el.dom.parentNode);
16573         return this;
16574     },
16575
16576     // private
16577     initValue : function(){
16578         if(this.value !== undefined){
16579             this.setValue(this.value);
16580         }else if(this.el.dom.value.length > 0){
16581             this.setValue(this.el.dom.value);
16582         }
16583     },
16584
16585     /**
16586      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16587      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16588      */
16589     isDirty : function() {
16590         if(this.disabled) {
16591             return false;
16592         }
16593         return String(this.getValue()) !== String(this.originalValue);
16594     },
16595
16596     /**
16597      * stores the current value in loadedValue
16598      */
16599     resetHasChanged : function()
16600     {
16601         this.loadedValue = String(this.getValue());
16602     },
16603     /**
16604      * checks the current value against the 'loaded' value.
16605      * Note - will return false if 'resetHasChanged' has not been called first.
16606      */
16607     hasChanged : function()
16608     {
16609         if(this.disabled || this.readOnly) {
16610             return false;
16611         }
16612         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16613     },
16614     
16615     
16616     
16617     // private
16618     afterRender : function(){
16619         Roo.form.Field.superclass.afterRender.call(this);
16620         this.initEvents();
16621     },
16622
16623     // private
16624     fireKey : function(e){
16625         //Roo.log('field ' + e.getKey());
16626         if(e.isNavKeyPress()){
16627             this.fireEvent("specialkey", this, e);
16628         }
16629     },
16630
16631     /**
16632      * Resets the current field value to the originally loaded value and clears any validation messages
16633      */
16634     reset : function(){
16635         this.setValue(this.resetValue);
16636         this.originalValue = this.getValue();
16637         this.clearInvalid();
16638     },
16639
16640     // private
16641     initEvents : function(){
16642         // safari killled keypress - so keydown is now used..
16643         this.el.on("keydown" , this.fireKey,  this);
16644         this.el.on("focus", this.onFocus,  this);
16645         this.el.on("blur", this.onBlur,  this);
16646         this.el.relayEvent('keyup', this);
16647
16648         // reference to original value for reset
16649         this.originalValue = this.getValue();
16650         this.resetValue =  this.getValue();
16651     },
16652
16653     // private
16654     onFocus : function(){
16655         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16656             this.el.addClass(this.focusClass);
16657         }
16658         if(!this.hasFocus){
16659             this.hasFocus = true;
16660             this.startValue = this.getValue();
16661             this.fireEvent("focus", this);
16662         }
16663     },
16664
16665     beforeBlur : Roo.emptyFn,
16666
16667     // private
16668     onBlur : function(){
16669         this.beforeBlur();
16670         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16671             this.el.removeClass(this.focusClass);
16672         }
16673         this.hasFocus = false;
16674         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16675             this.validate();
16676         }
16677         var v = this.getValue();
16678         if(String(v) !== String(this.startValue)){
16679             this.fireEvent('change', this, v, this.startValue);
16680         }
16681         this.fireEvent("blur", this);
16682     },
16683
16684     /**
16685      * Returns whether or not the field value is currently valid
16686      * @param {Boolean} preventMark True to disable marking the field invalid
16687      * @return {Boolean} True if the value is valid, else false
16688      */
16689     isValid : function(preventMark){
16690         if(this.disabled){
16691             return true;
16692         }
16693         var restore = this.preventMark;
16694         this.preventMark = preventMark === true;
16695         var v = this.validateValue(this.processValue(this.getRawValue()));
16696         this.preventMark = restore;
16697         return v;
16698     },
16699
16700     /**
16701      * Validates the field value
16702      * @return {Boolean} True if the value is valid, else false
16703      */
16704     validate : function(){
16705         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16706             this.clearInvalid();
16707             return true;
16708         }
16709         return false;
16710     },
16711
16712     processValue : function(value){
16713         return value;
16714     },
16715
16716     // private
16717     // Subclasses should provide the validation implementation by overriding this
16718     validateValue : function(value){
16719         return true;
16720     },
16721
16722     /**
16723      * Mark this field as invalid
16724      * @param {String} msg The validation message
16725      */
16726     markInvalid : function(msg){
16727         if(!this.rendered || this.preventMark){ // not rendered
16728             return;
16729         }
16730         
16731         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16732         
16733         obj.el.addClass(this.invalidClass);
16734         msg = msg || this.invalidText;
16735         switch(this.msgTarget){
16736             case 'qtip':
16737                 obj.el.dom.qtip = msg;
16738                 obj.el.dom.qclass = 'x-form-invalid-tip';
16739                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16740                     Roo.QuickTips.enable();
16741                 }
16742                 break;
16743             case 'title':
16744                 this.el.dom.title = msg;
16745                 break;
16746             case 'under':
16747                 if(!this.errorEl){
16748                     var elp = this.el.findParent('.x-form-element', 5, true);
16749                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16750                     this.errorEl.setWidth(elp.getWidth(true)-20);
16751                 }
16752                 this.errorEl.update(msg);
16753                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16754                 break;
16755             case 'side':
16756                 if(!this.errorIcon){
16757                     var elp = this.el.findParent('.x-form-element', 5, true);
16758                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16759                 }
16760                 this.alignErrorIcon();
16761                 this.errorIcon.dom.qtip = msg;
16762                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16763                 this.errorIcon.show();
16764                 this.on('resize', this.alignErrorIcon, this);
16765                 break;
16766             default:
16767                 var t = Roo.getDom(this.msgTarget);
16768                 t.innerHTML = msg;
16769                 t.style.display = this.msgDisplay;
16770                 break;
16771         }
16772         this.fireEvent('invalid', this, msg);
16773     },
16774
16775     // private
16776     alignErrorIcon : function(){
16777         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16778     },
16779
16780     /**
16781      * Clear any invalid styles/messages for this field
16782      */
16783     clearInvalid : function(){
16784         if(!this.rendered || this.preventMark){ // not rendered
16785             return;
16786         }
16787         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16788         
16789         obj.el.removeClass(this.invalidClass);
16790         switch(this.msgTarget){
16791             case 'qtip':
16792                 obj.el.dom.qtip = '';
16793                 break;
16794             case 'title':
16795                 this.el.dom.title = '';
16796                 break;
16797             case 'under':
16798                 if(this.errorEl){
16799                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16800                 }
16801                 break;
16802             case 'side':
16803                 if(this.errorIcon){
16804                     this.errorIcon.dom.qtip = '';
16805                     this.errorIcon.hide();
16806                     this.un('resize', this.alignErrorIcon, this);
16807                 }
16808                 break;
16809             default:
16810                 var t = Roo.getDom(this.msgTarget);
16811                 t.innerHTML = '';
16812                 t.style.display = 'none';
16813                 break;
16814         }
16815         this.fireEvent('valid', this);
16816     },
16817
16818     /**
16819      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16820      * @return {Mixed} value The field value
16821      */
16822     getRawValue : function(){
16823         var v = this.el.getValue();
16824         
16825         return v;
16826     },
16827
16828     /**
16829      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16830      * @return {Mixed} value The field value
16831      */
16832     getValue : function(){
16833         var v = this.el.getValue();
16834          
16835         return v;
16836     },
16837
16838     /**
16839      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16840      * @param {Mixed} value The value to set
16841      */
16842     setRawValue : function(v){
16843         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16844     },
16845
16846     /**
16847      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16848      * @param {Mixed} value The value to set
16849      */
16850     setValue : function(v){
16851         this.value = v;
16852         if(this.rendered){
16853             this.el.dom.value = (v === null || v === undefined ? '' : v);
16854              this.validate();
16855         }
16856     },
16857
16858     adjustSize : function(w, h){
16859         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16860         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16861         return s;
16862     },
16863
16864     adjustWidth : function(tag, w){
16865         tag = tag.toLowerCase();
16866         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16867             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16868                 if(tag == 'input'){
16869                     return w + 2;
16870                 }
16871                 if(tag == 'textarea'){
16872                     return w-2;
16873                 }
16874             }else if(Roo.isOpera){
16875                 if(tag == 'input'){
16876                     return w + 2;
16877                 }
16878                 if(tag == 'textarea'){
16879                     return w-2;
16880                 }
16881             }
16882         }
16883         return w;
16884     }
16885 });
16886
16887
16888 // anything other than normal should be considered experimental
16889 Roo.form.Field.msgFx = {
16890     normal : {
16891         show: function(msgEl, f){
16892             msgEl.setDisplayed('block');
16893         },
16894
16895         hide : function(msgEl, f){
16896             msgEl.setDisplayed(false).update('');
16897         }
16898     },
16899
16900     slide : {
16901         show: function(msgEl, f){
16902             msgEl.slideIn('t', {stopFx:true});
16903         },
16904
16905         hide : function(msgEl, f){
16906             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16907         }
16908     },
16909
16910     slideRight : {
16911         show: function(msgEl, f){
16912             msgEl.fixDisplay();
16913             msgEl.alignTo(f.el, 'tl-tr');
16914             msgEl.slideIn('l', {stopFx:true});
16915         },
16916
16917         hide : function(msgEl, f){
16918             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16919         }
16920     }
16921 };/*
16922  * Based on:
16923  * Ext JS Library 1.1.1
16924  * Copyright(c) 2006-2007, Ext JS, LLC.
16925  *
16926  * Originally Released Under LGPL - original licence link has changed is not relivant.
16927  *
16928  * Fork - LGPL
16929  * <script type="text/javascript">
16930  */
16931  
16932
16933 /**
16934  * @class Roo.form.TextField
16935  * @extends Roo.form.Field
16936  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16937  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16938  * @constructor
16939  * Creates a new TextField
16940  * @param {Object} config Configuration options
16941  */
16942 Roo.form.TextField = function(config){
16943     Roo.form.TextField.superclass.constructor.call(this, config);
16944     this.addEvents({
16945         /**
16946          * @event autosize
16947          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16948          * according to the default logic, but this event provides a hook for the developer to apply additional
16949          * logic at runtime to resize the field if needed.
16950              * @param {Roo.form.Field} this This text field
16951              * @param {Number} width The new field width
16952              */
16953         autosize : true
16954     });
16955 };
16956
16957 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16958     /**
16959      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16960      */
16961     grow : false,
16962     /**
16963      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16964      */
16965     growMin : 30,
16966     /**
16967      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16968      */
16969     growMax : 800,
16970     /**
16971      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16972      */
16973     vtype : null,
16974     /**
16975      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16976      */
16977     maskRe : null,
16978     /**
16979      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16980      */
16981     disableKeyFilter : false,
16982     /**
16983      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16984      */
16985     allowBlank : true,
16986     /**
16987      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16988      */
16989     minLength : 0,
16990     /**
16991      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16992      */
16993     maxLength : Number.MAX_VALUE,
16994     /**
16995      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16996      */
16997     minLengthText : "The minimum length for this field is {0}",
16998     /**
16999      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
17000      */
17001     maxLengthText : "The maximum length for this field is {0}",
17002     /**
17003      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
17004      */
17005     selectOnFocus : false,
17006     /**
17007      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
17008      */    
17009     allowLeadingSpace : false,
17010     /**
17011      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
17012      */
17013     blankText : "This field is required",
17014     /**
17015      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17016      * If available, this function will be called only after the basic validators all return true, and will be passed the
17017      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17018      */
17019     validator : null,
17020     /**
17021      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17022      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17023      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
17024      */
17025     regex : null,
17026     /**
17027      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17028      */
17029     regexText : "",
17030     /**
17031      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17032      */
17033     emptyText : null,
17034    
17035
17036     // private
17037     initEvents : function()
17038     {
17039         if (this.emptyText) {
17040             this.el.attr('placeholder', this.emptyText);
17041         }
17042         
17043         Roo.form.TextField.superclass.initEvents.call(this);
17044         if(this.validationEvent == 'keyup'){
17045             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17046             this.el.on('keyup', this.filterValidation, this);
17047         }
17048         else if(this.validationEvent !== false){
17049             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17050         }
17051         
17052         if(this.selectOnFocus){
17053             this.on("focus", this.preFocus, this);
17054         }
17055         if (!this.allowLeadingSpace) {
17056             this.on('blur', this.cleanLeadingSpace, this);
17057         }
17058         
17059         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17060             this.el.on("keypress", this.filterKeys, this);
17061         }
17062         if(this.grow){
17063             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17064             this.el.on("click", this.autoSize,  this);
17065         }
17066         if(this.el.is('input[type=password]') && Roo.isSafari){
17067             this.el.on('keydown', this.SafariOnKeyDown, this);
17068         }
17069     },
17070
17071     processValue : function(value){
17072         if(this.stripCharsRe){
17073             var newValue = value.replace(this.stripCharsRe, '');
17074             if(newValue !== value){
17075                 this.setRawValue(newValue);
17076                 return newValue;
17077             }
17078         }
17079         return value;
17080     },
17081
17082     filterValidation : function(e){
17083         if(!e.isNavKeyPress()){
17084             this.validationTask.delay(this.validationDelay);
17085         }
17086     },
17087
17088     // private
17089     onKeyUp : function(e){
17090         if(!e.isNavKeyPress()){
17091             this.autoSize();
17092         }
17093     },
17094     // private - clean the leading white space
17095     cleanLeadingSpace : function(e)
17096     {
17097         if ( this.inputType == 'file') {
17098             return;
17099         }
17100         
17101         this.setValue((this.getValue() + '').replace(/^\s+/,''));
17102     },
17103     /**
17104      * Resets the current field value to the originally-loaded value and clears any validation messages.
17105      *  
17106      */
17107     reset : function(){
17108         Roo.form.TextField.superclass.reset.call(this);
17109        
17110     }, 
17111     // private
17112     preFocus : function(){
17113         
17114         if(this.selectOnFocus){
17115             this.el.dom.select();
17116         }
17117     },
17118
17119     
17120     // private
17121     filterKeys : function(e){
17122         var k = e.getKey();
17123         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17124             return;
17125         }
17126         var c = e.getCharCode(), cc = String.fromCharCode(c);
17127         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17128             return;
17129         }
17130         if(!this.maskRe.test(cc)){
17131             e.stopEvent();
17132         }
17133     },
17134
17135     setValue : function(v){
17136         
17137         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17138         
17139         this.autoSize();
17140     },
17141
17142     /**
17143      * Validates a value according to the field's validation rules and marks the field as invalid
17144      * if the validation fails
17145      * @param {Mixed} value The value to validate
17146      * @return {Boolean} True if the value is valid, else false
17147      */
17148     validateValue : function(value){
17149         if(value.length < 1)  { // if it's blank
17150              if(this.allowBlank){
17151                 this.clearInvalid();
17152                 return true;
17153              }else{
17154                 this.markInvalid(this.blankText);
17155                 return false;
17156              }
17157         }
17158         if(value.length < this.minLength){
17159             this.markInvalid(String.format(this.minLengthText, this.minLength));
17160             return false;
17161         }
17162         if(value.length > this.maxLength){
17163             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17164             return false;
17165         }
17166         if(this.vtype){
17167             var vt = Roo.form.VTypes;
17168             if(!vt[this.vtype](value, this)){
17169                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17170                 return false;
17171             }
17172         }
17173         if(typeof this.validator == "function"){
17174             var msg = this.validator(value);
17175             if(msg !== true){
17176                 this.markInvalid(msg);
17177                 return false;
17178             }
17179         }
17180         if(this.regex && !this.regex.test(value)){
17181             this.markInvalid(this.regexText);
17182             return false;
17183         }
17184         return true;
17185     },
17186
17187     /**
17188      * Selects text in this field
17189      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17190      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17191      */
17192     selectText : function(start, end){
17193         var v = this.getRawValue();
17194         if(v.length > 0){
17195             start = start === undefined ? 0 : start;
17196             end = end === undefined ? v.length : end;
17197             var d = this.el.dom;
17198             if(d.setSelectionRange){
17199                 d.setSelectionRange(start, end);
17200             }else if(d.createTextRange){
17201                 var range = d.createTextRange();
17202                 range.moveStart("character", start);
17203                 range.moveEnd("character", v.length-end);
17204                 range.select();
17205             }
17206         }
17207     },
17208
17209     /**
17210      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17211      * This only takes effect if grow = true, and fires the autosize event.
17212      */
17213     autoSize : function(){
17214         if(!this.grow || !this.rendered){
17215             return;
17216         }
17217         if(!this.metrics){
17218             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17219         }
17220         var el = this.el;
17221         var v = el.dom.value;
17222         var d = document.createElement('div');
17223         d.appendChild(document.createTextNode(v));
17224         v = d.innerHTML;
17225         d = null;
17226         v += "&#160;";
17227         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17228         this.el.setWidth(w);
17229         this.fireEvent("autosize", this, w);
17230     },
17231     
17232     // private
17233     SafariOnKeyDown : function(event)
17234     {
17235         // this is a workaround for a password hang bug on chrome/ webkit.
17236         
17237         var isSelectAll = false;
17238         
17239         if(this.el.dom.selectionEnd > 0){
17240             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17241         }
17242         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17243             event.preventDefault();
17244             this.setValue('');
17245             return;
17246         }
17247         
17248         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17249             
17250             event.preventDefault();
17251             // this is very hacky as keydown always get's upper case.
17252             
17253             var cc = String.fromCharCode(event.getCharCode());
17254             
17255             
17256             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17257             
17258         }
17259         
17260         
17261     }
17262 });/*
17263  * Based on:
17264  * Ext JS Library 1.1.1
17265  * Copyright(c) 2006-2007, Ext JS, LLC.
17266  *
17267  * Originally Released Under LGPL - original licence link has changed is not relivant.
17268  *
17269  * Fork - LGPL
17270  * <script type="text/javascript">
17271  */
17272  
17273 /**
17274  * @class Roo.form.Hidden
17275  * @extends Roo.form.TextField
17276  * Simple Hidden element used on forms 
17277  * 
17278  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17279  * 
17280  * @constructor
17281  * Creates a new Hidden form element.
17282  * @param {Object} config Configuration options
17283  */
17284
17285
17286
17287 // easy hidden field...
17288 Roo.form.Hidden = function(config){
17289     Roo.form.Hidden.superclass.constructor.call(this, config);
17290 };
17291   
17292 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17293     fieldLabel:      '',
17294     inputType:      'hidden',
17295     width:          50,
17296     allowBlank:     true,
17297     labelSeparator: '',
17298     hidden:         true,
17299     itemCls :       'x-form-item-display-none'
17300
17301
17302 });
17303
17304
17305 /*
17306  * Based on:
17307  * Ext JS Library 1.1.1
17308  * Copyright(c) 2006-2007, Ext JS, LLC.
17309  *
17310  * Originally Released Under LGPL - original licence link has changed is not relivant.
17311  *
17312  * Fork - LGPL
17313  * <script type="text/javascript">
17314  */
17315  
17316 /**
17317  * @class Roo.form.TriggerField
17318  * @extends Roo.form.TextField
17319  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17320  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17321  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17322  * for which you can provide a custom implementation.  For example:
17323  * <pre><code>
17324 var trigger = new Roo.form.TriggerField();
17325 trigger.onTriggerClick = myTriggerFn;
17326 trigger.applyTo('my-field');
17327 </code></pre>
17328  *
17329  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17330  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17331  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17332  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17333  * @constructor
17334  * Create a new TriggerField.
17335  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17336  * to the base TextField)
17337  */
17338 Roo.form.TriggerField = function(config){
17339     this.mimicing = false;
17340     Roo.form.TriggerField.superclass.constructor.call(this, config);
17341 };
17342
17343 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17344     /**
17345      * @cfg {String} triggerClass A CSS class to apply to the trigger
17346      */
17347     /**
17348      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17349      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17350      */
17351     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17352     /**
17353      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17354      */
17355     hideTrigger:false,
17356
17357     /** @cfg {Boolean} grow @hide */
17358     /** @cfg {Number} growMin @hide */
17359     /** @cfg {Number} growMax @hide */
17360
17361     /**
17362      * @hide 
17363      * @method
17364      */
17365     autoSize: Roo.emptyFn,
17366     // private
17367     monitorTab : true,
17368     // private
17369     deferHeight : true,
17370
17371     
17372     actionMode : 'wrap',
17373     // private
17374     onResize : function(w, h){
17375         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17376         if(typeof w == 'number'){
17377             var x = w - this.trigger.getWidth();
17378             this.el.setWidth(this.adjustWidth('input', x));
17379             this.trigger.setStyle('left', x+'px');
17380         }
17381     },
17382
17383     // private
17384     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17385
17386     // private
17387     getResizeEl : function(){
17388         return this.wrap;
17389     },
17390
17391     // private
17392     getPositionEl : function(){
17393         return this.wrap;
17394     },
17395
17396     // private
17397     alignErrorIcon : function(){
17398         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17399     },
17400
17401     // private
17402     onRender : function(ct, position){
17403         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17404         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17405         this.trigger = this.wrap.createChild(this.triggerConfig ||
17406                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17407         if(this.hideTrigger){
17408             this.trigger.setDisplayed(false);
17409         }
17410         this.initTrigger();
17411         if(!this.width){
17412             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17413         }
17414     },
17415
17416     // private
17417     initTrigger : function(){
17418         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17419         this.trigger.addClassOnOver('x-form-trigger-over');
17420         this.trigger.addClassOnClick('x-form-trigger-click');
17421     },
17422
17423     // private
17424     onDestroy : function(){
17425         if(this.trigger){
17426             this.trigger.removeAllListeners();
17427             this.trigger.remove();
17428         }
17429         if(this.wrap){
17430             this.wrap.remove();
17431         }
17432         Roo.form.TriggerField.superclass.onDestroy.call(this);
17433     },
17434
17435     // private
17436     onFocus : function(){
17437         Roo.form.TriggerField.superclass.onFocus.call(this);
17438         if(!this.mimicing){
17439             this.wrap.addClass('x-trigger-wrap-focus');
17440             this.mimicing = true;
17441             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17442             if(this.monitorTab){
17443                 this.el.on("keydown", this.checkTab, this);
17444             }
17445         }
17446     },
17447
17448     // private
17449     checkTab : function(e){
17450         if(e.getKey() == e.TAB){
17451             this.triggerBlur();
17452         }
17453     },
17454
17455     // private
17456     onBlur : function(){
17457         // do nothing
17458     },
17459
17460     // private
17461     mimicBlur : function(e, t){
17462         if(!this.wrap.contains(t) && this.validateBlur()){
17463             this.triggerBlur();
17464         }
17465     },
17466
17467     // private
17468     triggerBlur : function(){
17469         this.mimicing = false;
17470         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17471         if(this.monitorTab){
17472             this.el.un("keydown", this.checkTab, this);
17473         }
17474         this.wrap.removeClass('x-trigger-wrap-focus');
17475         Roo.form.TriggerField.superclass.onBlur.call(this);
17476     },
17477
17478     // private
17479     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17480     validateBlur : function(e, t){
17481         return true;
17482     },
17483
17484     // private
17485     onDisable : function(){
17486         Roo.form.TriggerField.superclass.onDisable.call(this);
17487         if(this.wrap){
17488             this.wrap.addClass('x-item-disabled');
17489         }
17490     },
17491
17492     // private
17493     onEnable : function(){
17494         Roo.form.TriggerField.superclass.onEnable.call(this);
17495         if(this.wrap){
17496             this.wrap.removeClass('x-item-disabled');
17497         }
17498     },
17499
17500     // private
17501     onShow : function(){
17502         var ae = this.getActionEl();
17503         
17504         if(ae){
17505             ae.dom.style.display = '';
17506             ae.dom.style.visibility = 'visible';
17507         }
17508     },
17509
17510     // private
17511     
17512     onHide : function(){
17513         var ae = this.getActionEl();
17514         ae.dom.style.display = 'none';
17515     },
17516
17517     /**
17518      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17519      * by an implementing function.
17520      * @method
17521      * @param {EventObject} e
17522      */
17523     onTriggerClick : Roo.emptyFn
17524 });
17525
17526 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17527 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17528 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17529 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17530     initComponent : function(){
17531         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17532
17533         this.triggerConfig = {
17534             tag:'span', cls:'x-form-twin-triggers', cn:[
17535             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17536             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17537         ]};
17538     },
17539
17540     getTrigger : function(index){
17541         return this.triggers[index];
17542     },
17543
17544     initTrigger : function(){
17545         var ts = this.trigger.select('.x-form-trigger', true);
17546         this.wrap.setStyle('overflow', 'hidden');
17547         var triggerField = this;
17548         ts.each(function(t, all, index){
17549             t.hide = function(){
17550                 var w = triggerField.wrap.getWidth();
17551                 this.dom.style.display = 'none';
17552                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17553             };
17554             t.show = function(){
17555                 var w = triggerField.wrap.getWidth();
17556                 this.dom.style.display = '';
17557                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17558             };
17559             var triggerIndex = 'Trigger'+(index+1);
17560
17561             if(this['hide'+triggerIndex]){
17562                 t.dom.style.display = 'none';
17563             }
17564             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17565             t.addClassOnOver('x-form-trigger-over');
17566             t.addClassOnClick('x-form-trigger-click');
17567         }, this);
17568         this.triggers = ts.elements;
17569     },
17570
17571     onTrigger1Click : Roo.emptyFn,
17572     onTrigger2Click : Roo.emptyFn
17573 });/*
17574  * Based on:
17575  * Ext JS Library 1.1.1
17576  * Copyright(c) 2006-2007, Ext JS, LLC.
17577  *
17578  * Originally Released Under LGPL - original licence link has changed is not relivant.
17579  *
17580  * Fork - LGPL
17581  * <script type="text/javascript">
17582  */
17583  
17584 /**
17585  * @class Roo.form.TextArea
17586  * @extends Roo.form.TextField
17587  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17588  * support for auto-sizing.
17589  * @constructor
17590  * Creates a new TextArea
17591  * @param {Object} config Configuration options
17592  */
17593 Roo.form.TextArea = function(config){
17594     Roo.form.TextArea.superclass.constructor.call(this, config);
17595     // these are provided exchanges for backwards compat
17596     // minHeight/maxHeight were replaced by growMin/growMax to be
17597     // compatible with TextField growing config values
17598     if(this.minHeight !== undefined){
17599         this.growMin = this.minHeight;
17600     }
17601     if(this.maxHeight !== undefined){
17602         this.growMax = this.maxHeight;
17603     }
17604 };
17605
17606 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17607     /**
17608      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17609      */
17610     growMin : 60,
17611     /**
17612      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17613      */
17614     growMax: 1000,
17615     /**
17616      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17617      * in the field (equivalent to setting overflow: hidden, defaults to false)
17618      */
17619     preventScrollbars: false,
17620     /**
17621      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17622      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17623      */
17624
17625     // private
17626     onRender : function(ct, position){
17627         if(!this.el){
17628             this.defaultAutoCreate = {
17629                 tag: "textarea",
17630                 style:"width:300px;height:60px;",
17631                 autocomplete: "new-password"
17632             };
17633         }
17634         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17635         if(this.grow){
17636             this.textSizeEl = Roo.DomHelper.append(document.body, {
17637                 tag: "pre", cls: "x-form-grow-sizer"
17638             });
17639             if(this.preventScrollbars){
17640                 this.el.setStyle("overflow", "hidden");
17641             }
17642             this.el.setHeight(this.growMin);
17643         }
17644     },
17645
17646     onDestroy : function(){
17647         if(this.textSizeEl){
17648             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17649         }
17650         Roo.form.TextArea.superclass.onDestroy.call(this);
17651     },
17652
17653     // private
17654     onKeyUp : function(e){
17655         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17656             this.autoSize();
17657         }
17658     },
17659
17660     /**
17661      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17662      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17663      */
17664     autoSize : function(){
17665         if(!this.grow || !this.textSizeEl){
17666             return;
17667         }
17668         var el = this.el;
17669         var v = el.dom.value;
17670         var ts = this.textSizeEl;
17671
17672         ts.innerHTML = '';
17673         ts.appendChild(document.createTextNode(v));
17674         v = ts.innerHTML;
17675
17676         Roo.fly(ts).setWidth(this.el.getWidth());
17677         if(v.length < 1){
17678             v = "&#160;&#160;";
17679         }else{
17680             if(Roo.isIE){
17681                 v = v.replace(/\n/g, '<p>&#160;</p>');
17682             }
17683             v += "&#160;\n&#160;";
17684         }
17685         ts.innerHTML = v;
17686         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17687         if(h != this.lastHeight){
17688             this.lastHeight = h;
17689             this.el.setHeight(h);
17690             this.fireEvent("autosize", this, h);
17691         }
17692     }
17693 });/*
17694  * Based on:
17695  * Ext JS Library 1.1.1
17696  * Copyright(c) 2006-2007, Ext JS, LLC.
17697  *
17698  * Originally Released Under LGPL - original licence link has changed is not relivant.
17699  *
17700  * Fork - LGPL
17701  * <script type="text/javascript">
17702  */
17703  
17704
17705 /**
17706  * @class Roo.form.NumberField
17707  * @extends Roo.form.TextField
17708  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17709  * @constructor
17710  * Creates a new NumberField
17711  * @param {Object} config Configuration options
17712  */
17713 Roo.form.NumberField = function(config){
17714     Roo.form.NumberField.superclass.constructor.call(this, config);
17715 };
17716
17717 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17718     /**
17719      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17720      */
17721     fieldClass: "x-form-field x-form-num-field",
17722     /**
17723      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17724      */
17725     allowDecimals : true,
17726     /**
17727      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17728      */
17729     decimalSeparator : ".",
17730     /**
17731      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17732      */
17733     decimalPrecision : 2,
17734     /**
17735      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17736      */
17737     allowNegative : true,
17738     /**
17739      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17740      */
17741     minValue : Number.NEGATIVE_INFINITY,
17742     /**
17743      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17744      */
17745     maxValue : Number.MAX_VALUE,
17746     /**
17747      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17748      */
17749     minText : "The minimum value for this field is {0}",
17750     /**
17751      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17752      */
17753     maxText : "The maximum value for this field is {0}",
17754     /**
17755      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17756      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17757      */
17758     nanText : "{0} is not a valid number",
17759
17760     // private
17761     initEvents : function(){
17762         Roo.form.NumberField.superclass.initEvents.call(this);
17763         var allowed = "0123456789";
17764         if(this.allowDecimals){
17765             allowed += this.decimalSeparator;
17766         }
17767         if(this.allowNegative){
17768             allowed += "-";
17769         }
17770         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17771         var keyPress = function(e){
17772             var k = e.getKey();
17773             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17774                 return;
17775             }
17776             var c = e.getCharCode();
17777             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17778                 e.stopEvent();
17779             }
17780         };
17781         this.el.on("keypress", keyPress, this);
17782     },
17783
17784     // private
17785     validateValue : function(value){
17786         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17787             return false;
17788         }
17789         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17790              return true;
17791         }
17792         var num = this.parseValue(value);
17793         if(isNaN(num)){
17794             this.markInvalid(String.format(this.nanText, value));
17795             return false;
17796         }
17797         if(num < this.minValue){
17798             this.markInvalid(String.format(this.minText, this.minValue));
17799             return false;
17800         }
17801         if(num > this.maxValue){
17802             this.markInvalid(String.format(this.maxText, this.maxValue));
17803             return false;
17804         }
17805         return true;
17806     },
17807
17808     getValue : function(){
17809         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17810     },
17811
17812     // private
17813     parseValue : function(value){
17814         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17815         return isNaN(value) ? '' : value;
17816     },
17817
17818     // private
17819     fixPrecision : function(value){
17820         var nan = isNaN(value);
17821         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17822             return nan ? '' : value;
17823         }
17824         return parseFloat(value).toFixed(this.decimalPrecision);
17825     },
17826
17827     setValue : function(v){
17828         v = this.fixPrecision(v);
17829         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17830     },
17831
17832     // private
17833     decimalPrecisionFcn : function(v){
17834         return Math.floor(v);
17835     },
17836
17837     beforeBlur : function(){
17838         var v = this.parseValue(this.getRawValue());
17839         if(v){
17840             this.setValue(v);
17841         }
17842     }
17843 });/*
17844  * Based on:
17845  * Ext JS Library 1.1.1
17846  * Copyright(c) 2006-2007, Ext JS, LLC.
17847  *
17848  * Originally Released Under LGPL - original licence link has changed is not relivant.
17849  *
17850  * Fork - LGPL
17851  * <script type="text/javascript">
17852  */
17853  
17854 /**
17855  * @class Roo.form.DateField
17856  * @extends Roo.form.TriggerField
17857  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17858 * @constructor
17859 * Create a new DateField
17860 * @param {Object} config
17861  */
17862 Roo.form.DateField = function(config)
17863 {
17864     Roo.form.DateField.superclass.constructor.call(this, config);
17865     
17866       this.addEvents({
17867          
17868         /**
17869          * @event select
17870          * Fires when a date is selected
17871              * @param {Roo.form.DateField} combo This combo box
17872              * @param {Date} date The date selected
17873              */
17874         'select' : true
17875          
17876     });
17877     
17878     
17879     if(typeof this.minValue == "string") {
17880         this.minValue = this.parseDate(this.minValue);
17881     }
17882     if(typeof this.maxValue == "string") {
17883         this.maxValue = this.parseDate(this.maxValue);
17884     }
17885     this.ddMatch = null;
17886     if(this.disabledDates){
17887         var dd = this.disabledDates;
17888         var re = "(?:";
17889         for(var i = 0; i < dd.length; i++){
17890             re += dd[i];
17891             if(i != dd.length-1) {
17892                 re += "|";
17893             }
17894         }
17895         this.ddMatch = new RegExp(re + ")");
17896     }
17897 };
17898
17899 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17900     /**
17901      * @cfg {String} format
17902      * The default date format string which can be overriden for localization support.  The format must be
17903      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17904      */
17905     format : "m/d/y",
17906     /**
17907      * @cfg {String} altFormats
17908      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17909      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17910      */
17911     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17912     /**
17913      * @cfg {Array} disabledDays
17914      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17915      */
17916     disabledDays : null,
17917     /**
17918      * @cfg {String} disabledDaysText
17919      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17920      */
17921     disabledDaysText : "Disabled",
17922     /**
17923      * @cfg {Array} disabledDates
17924      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17925      * expression so they are very powerful. Some examples:
17926      * <ul>
17927      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17928      * <li>["03/08", "09/16"] would disable those days for every year</li>
17929      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17930      * <li>["03/../2006"] would disable every day in March 2006</li>
17931      * <li>["^03"] would disable every day in every March</li>
17932      * </ul>
17933      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17934      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17935      */
17936     disabledDates : null,
17937     /**
17938      * @cfg {String} disabledDatesText
17939      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17940      */
17941     disabledDatesText : "Disabled",
17942     /**
17943      * @cfg {Date/String} minValue
17944      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17945      * valid format (defaults to null).
17946      */
17947     minValue : null,
17948     /**
17949      * @cfg {Date/String} maxValue
17950      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17951      * valid format (defaults to null).
17952      */
17953     maxValue : null,
17954     /**
17955      * @cfg {String} minText
17956      * The error text to display when the date in the cell is before minValue (defaults to
17957      * 'The date in this field must be after {minValue}').
17958      */
17959     minText : "The date in this field must be equal to or after {0}",
17960     /**
17961      * @cfg {String} maxText
17962      * The error text to display when the date in the cell is after maxValue (defaults to
17963      * 'The date in this field must be before {maxValue}').
17964      */
17965     maxText : "The date in this field must be equal to or before {0}",
17966     /**
17967      * @cfg {String} invalidText
17968      * The error text to display when the date in the field is invalid (defaults to
17969      * '{value} is not a valid date - it must be in the format {format}').
17970      */
17971     invalidText : "{0} is not a valid date - it must be in the format {1}",
17972     /**
17973      * @cfg {String} triggerClass
17974      * An additional CSS class used to style the trigger button.  The trigger will always get the
17975      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17976      * which displays a calendar icon).
17977      */
17978     triggerClass : 'x-form-date-trigger',
17979     
17980
17981     /**
17982      * @cfg {Boolean} useIso
17983      * if enabled, then the date field will use a hidden field to store the 
17984      * real value as iso formated date. default (false)
17985      */ 
17986     useIso : false,
17987     /**
17988      * @cfg {String/Object} autoCreate
17989      * A DomHelper element spec, or true for a default element spec (defaults to
17990      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17991      */ 
17992     // private
17993     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17994     
17995     // private
17996     hiddenField: false,
17997     
17998     onRender : function(ct, position)
17999     {
18000         Roo.form.DateField.superclass.onRender.call(this, ct, position);
18001         if (this.useIso) {
18002             //this.el.dom.removeAttribute('name'); 
18003             Roo.log("Changing name?");
18004             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
18005             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18006                     'before', true);
18007             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18008             // prevent input submission
18009             this.hiddenName = this.name;
18010         }
18011             
18012             
18013     },
18014     
18015     // private
18016     validateValue : function(value)
18017     {
18018         value = this.formatDate(value);
18019         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
18020             Roo.log('super failed');
18021             return false;
18022         }
18023         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18024              return true;
18025         }
18026         var svalue = value;
18027         value = this.parseDate(value);
18028         if(!value){
18029             Roo.log('parse date failed' + svalue);
18030             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18031             return false;
18032         }
18033         var time = value.getTime();
18034         if(this.minValue && time < this.minValue.getTime()){
18035             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18036             return false;
18037         }
18038         if(this.maxValue && time > this.maxValue.getTime()){
18039             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18040             return false;
18041         }
18042         if(this.disabledDays){
18043             var day = value.getDay();
18044             for(var i = 0; i < this.disabledDays.length; i++) {
18045                 if(day === this.disabledDays[i]){
18046                     this.markInvalid(this.disabledDaysText);
18047                     return false;
18048                 }
18049             }
18050         }
18051         var fvalue = this.formatDate(value);
18052         if(this.ddMatch && this.ddMatch.test(fvalue)){
18053             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18054             return false;
18055         }
18056         return true;
18057     },
18058
18059     // private
18060     // Provides logic to override the default TriggerField.validateBlur which just returns true
18061     validateBlur : function(){
18062         return !this.menu || !this.menu.isVisible();
18063     },
18064     
18065     getName: function()
18066     {
18067         // returns hidden if it's set..
18068         if (!this.rendered) {return ''};
18069         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18070         
18071     },
18072
18073     /**
18074      * Returns the current date value of the date field.
18075      * @return {Date} The date value
18076      */
18077     getValue : function(){
18078         
18079         return  this.hiddenField ?
18080                 this.hiddenField.value :
18081                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18082     },
18083
18084     /**
18085      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18086      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18087      * (the default format used is "m/d/y").
18088      * <br />Usage:
18089      * <pre><code>
18090 //All of these calls set the same date value (May 4, 2006)
18091
18092 //Pass a date object:
18093 var dt = new Date('5/4/06');
18094 dateField.setValue(dt);
18095
18096 //Pass a date string (default format):
18097 dateField.setValue('5/4/06');
18098
18099 //Pass a date string (custom format):
18100 dateField.format = 'Y-m-d';
18101 dateField.setValue('2006-5-4');
18102 </code></pre>
18103      * @param {String/Date} date The date or valid date string
18104      */
18105     setValue : function(date){
18106         if (this.hiddenField) {
18107             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18108         }
18109         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18110         // make sure the value field is always stored as a date..
18111         this.value = this.parseDate(date);
18112         
18113         
18114     },
18115
18116     // private
18117     parseDate : function(value){
18118         if(!value || value instanceof Date){
18119             return value;
18120         }
18121         var v = Date.parseDate(value, this.format);
18122          if (!v && this.useIso) {
18123             v = Date.parseDate(value, 'Y-m-d');
18124         }
18125         if(!v && this.altFormats){
18126             if(!this.altFormatsArray){
18127                 this.altFormatsArray = this.altFormats.split("|");
18128             }
18129             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18130                 v = Date.parseDate(value, this.altFormatsArray[i]);
18131             }
18132         }
18133         return v;
18134     },
18135
18136     // private
18137     formatDate : function(date, fmt){
18138         return (!date || !(date instanceof Date)) ?
18139                date : date.dateFormat(fmt || this.format);
18140     },
18141
18142     // private
18143     menuListeners : {
18144         select: function(m, d){
18145             
18146             this.setValue(d);
18147             this.fireEvent('select', this, d);
18148         },
18149         show : function(){ // retain focus styling
18150             this.onFocus();
18151         },
18152         hide : function(){
18153             this.focus.defer(10, this);
18154             var ml = this.menuListeners;
18155             this.menu.un("select", ml.select,  this);
18156             this.menu.un("show", ml.show,  this);
18157             this.menu.un("hide", ml.hide,  this);
18158         }
18159     },
18160
18161     // private
18162     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18163     onTriggerClick : function(){
18164         if(this.disabled){
18165             return;
18166         }
18167         if(this.menu == null){
18168             this.menu = new Roo.menu.DateMenu();
18169         }
18170         Roo.apply(this.menu.picker,  {
18171             showClear: this.allowBlank,
18172             minDate : this.minValue,
18173             maxDate : this.maxValue,
18174             disabledDatesRE : this.ddMatch,
18175             disabledDatesText : this.disabledDatesText,
18176             disabledDays : this.disabledDays,
18177             disabledDaysText : this.disabledDaysText,
18178             format : this.useIso ? 'Y-m-d' : this.format,
18179             minText : String.format(this.minText, this.formatDate(this.minValue)),
18180             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18181         });
18182         this.menu.on(Roo.apply({}, this.menuListeners, {
18183             scope:this
18184         }));
18185         this.menu.picker.setValue(this.getValue() || new Date());
18186         this.menu.show(this.el, "tl-bl?");
18187     },
18188
18189     beforeBlur : function(){
18190         var v = this.parseDate(this.getRawValue());
18191         if(v){
18192             this.setValue(v);
18193         }
18194     },
18195
18196     /*@
18197      * overide
18198      * 
18199      */
18200     isDirty : function() {
18201         if(this.disabled) {
18202             return false;
18203         }
18204         
18205         if(typeof(this.startValue) === 'undefined'){
18206             return false;
18207         }
18208         
18209         return String(this.getValue()) !== String(this.startValue);
18210         
18211     },
18212     // @overide
18213     cleanLeadingSpace : function(e)
18214     {
18215        return;
18216     }
18217     
18218 });/*
18219  * Based on:
18220  * Ext JS Library 1.1.1
18221  * Copyright(c) 2006-2007, Ext JS, LLC.
18222  *
18223  * Originally Released Under LGPL - original licence link has changed is not relivant.
18224  *
18225  * Fork - LGPL
18226  * <script type="text/javascript">
18227  */
18228  
18229 /**
18230  * @class Roo.form.MonthField
18231  * @extends Roo.form.TriggerField
18232  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18233 * @constructor
18234 * Create a new MonthField
18235 * @param {Object} config
18236  */
18237 Roo.form.MonthField = function(config){
18238     
18239     Roo.form.MonthField.superclass.constructor.call(this, config);
18240     
18241       this.addEvents({
18242          
18243         /**
18244          * @event select
18245          * Fires when a date is selected
18246              * @param {Roo.form.MonthFieeld} combo This combo box
18247              * @param {Date} date The date selected
18248              */
18249         'select' : true
18250          
18251     });
18252     
18253     
18254     if(typeof this.minValue == "string") {
18255         this.minValue = this.parseDate(this.minValue);
18256     }
18257     if(typeof this.maxValue == "string") {
18258         this.maxValue = this.parseDate(this.maxValue);
18259     }
18260     this.ddMatch = null;
18261     if(this.disabledDates){
18262         var dd = this.disabledDates;
18263         var re = "(?:";
18264         for(var i = 0; i < dd.length; i++){
18265             re += dd[i];
18266             if(i != dd.length-1) {
18267                 re += "|";
18268             }
18269         }
18270         this.ddMatch = new RegExp(re + ")");
18271     }
18272 };
18273
18274 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18275     /**
18276      * @cfg {String} format
18277      * The default date format string which can be overriden for localization support.  The format must be
18278      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18279      */
18280     format : "M Y",
18281     /**
18282      * @cfg {String} altFormats
18283      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18284      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18285      */
18286     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18287     /**
18288      * @cfg {Array} disabledDays
18289      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18290      */
18291     disabledDays : [0,1,2,3,4,5,6],
18292     /**
18293      * @cfg {String} disabledDaysText
18294      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18295      */
18296     disabledDaysText : "Disabled",
18297     /**
18298      * @cfg {Array} disabledDates
18299      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18300      * expression so they are very powerful. Some examples:
18301      * <ul>
18302      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18303      * <li>["03/08", "09/16"] would disable those days for every year</li>
18304      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18305      * <li>["03/../2006"] would disable every day in March 2006</li>
18306      * <li>["^03"] would disable every day in every March</li>
18307      * </ul>
18308      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18309      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18310      */
18311     disabledDates : null,
18312     /**
18313      * @cfg {String} disabledDatesText
18314      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18315      */
18316     disabledDatesText : "Disabled",
18317     /**
18318      * @cfg {Date/String} minValue
18319      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18320      * valid format (defaults to null).
18321      */
18322     minValue : null,
18323     /**
18324      * @cfg {Date/String} maxValue
18325      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18326      * valid format (defaults to null).
18327      */
18328     maxValue : null,
18329     /**
18330      * @cfg {String} minText
18331      * The error text to display when the date in the cell is before minValue (defaults to
18332      * 'The date in this field must be after {minValue}').
18333      */
18334     minText : "The date in this field must be equal to or after {0}",
18335     /**
18336      * @cfg {String} maxTextf
18337      * The error text to display when the date in the cell is after maxValue (defaults to
18338      * 'The date in this field must be before {maxValue}').
18339      */
18340     maxText : "The date in this field must be equal to or before {0}",
18341     /**
18342      * @cfg {String} invalidText
18343      * The error text to display when the date in the field is invalid (defaults to
18344      * '{value} is not a valid date - it must be in the format {format}').
18345      */
18346     invalidText : "{0} is not a valid date - it must be in the format {1}",
18347     /**
18348      * @cfg {String} triggerClass
18349      * An additional CSS class used to style the trigger button.  The trigger will always get the
18350      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18351      * which displays a calendar icon).
18352      */
18353     triggerClass : 'x-form-date-trigger',
18354     
18355
18356     /**
18357      * @cfg {Boolean} useIso
18358      * if enabled, then the date field will use a hidden field to store the 
18359      * real value as iso formated date. default (true)
18360      */ 
18361     useIso : true,
18362     /**
18363      * @cfg {String/Object} autoCreate
18364      * A DomHelper element spec, or true for a default element spec (defaults to
18365      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18366      */ 
18367     // private
18368     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18369     
18370     // private
18371     hiddenField: false,
18372     
18373     hideMonthPicker : false,
18374     
18375     onRender : function(ct, position)
18376     {
18377         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18378         if (this.useIso) {
18379             this.el.dom.removeAttribute('name'); 
18380             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18381                     'before', true);
18382             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18383             // prevent input submission
18384             this.hiddenName = this.name;
18385         }
18386             
18387             
18388     },
18389     
18390     // private
18391     validateValue : function(value)
18392     {
18393         value = this.formatDate(value);
18394         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18395             return false;
18396         }
18397         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18398              return true;
18399         }
18400         var svalue = value;
18401         value = this.parseDate(value);
18402         if(!value){
18403             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18404             return false;
18405         }
18406         var time = value.getTime();
18407         if(this.minValue && time < this.minValue.getTime()){
18408             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18409             return false;
18410         }
18411         if(this.maxValue && time > this.maxValue.getTime()){
18412             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18413             return false;
18414         }
18415         /*if(this.disabledDays){
18416             var day = value.getDay();
18417             for(var i = 0; i < this.disabledDays.length; i++) {
18418                 if(day === this.disabledDays[i]){
18419                     this.markInvalid(this.disabledDaysText);
18420                     return false;
18421                 }
18422             }
18423         }
18424         */
18425         var fvalue = this.formatDate(value);
18426         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18427             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18428             return false;
18429         }
18430         */
18431         return true;
18432     },
18433
18434     // private
18435     // Provides logic to override the default TriggerField.validateBlur which just returns true
18436     validateBlur : function(){
18437         return !this.menu || !this.menu.isVisible();
18438     },
18439
18440     /**
18441      * Returns the current date value of the date field.
18442      * @return {Date} The date value
18443      */
18444     getValue : function(){
18445         
18446         
18447         
18448         return  this.hiddenField ?
18449                 this.hiddenField.value :
18450                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18451     },
18452
18453     /**
18454      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18455      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18456      * (the default format used is "m/d/y").
18457      * <br />Usage:
18458      * <pre><code>
18459 //All of these calls set the same date value (May 4, 2006)
18460
18461 //Pass a date object:
18462 var dt = new Date('5/4/06');
18463 monthField.setValue(dt);
18464
18465 //Pass a date string (default format):
18466 monthField.setValue('5/4/06');
18467
18468 //Pass a date string (custom format):
18469 monthField.format = 'Y-m-d';
18470 monthField.setValue('2006-5-4');
18471 </code></pre>
18472      * @param {String/Date} date The date or valid date string
18473      */
18474     setValue : function(date){
18475         Roo.log('month setValue' + date);
18476         // can only be first of month..
18477         
18478         var val = this.parseDate(date);
18479         
18480         if (this.hiddenField) {
18481             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18482         }
18483         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18484         this.value = this.parseDate(date);
18485     },
18486
18487     // private
18488     parseDate : function(value){
18489         if(!value || value instanceof Date){
18490             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18491             return value;
18492         }
18493         var v = Date.parseDate(value, this.format);
18494         if (!v && this.useIso) {
18495             v = Date.parseDate(value, 'Y-m-d');
18496         }
18497         if (v) {
18498             // 
18499             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18500         }
18501         
18502         
18503         if(!v && this.altFormats){
18504             if(!this.altFormatsArray){
18505                 this.altFormatsArray = this.altFormats.split("|");
18506             }
18507             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18508                 v = Date.parseDate(value, this.altFormatsArray[i]);
18509             }
18510         }
18511         return v;
18512     },
18513
18514     // private
18515     formatDate : function(date, fmt){
18516         return (!date || !(date instanceof Date)) ?
18517                date : date.dateFormat(fmt || this.format);
18518     },
18519
18520     // private
18521     menuListeners : {
18522         select: function(m, d){
18523             this.setValue(d);
18524             this.fireEvent('select', this, d);
18525         },
18526         show : function(){ // retain focus styling
18527             this.onFocus();
18528         },
18529         hide : function(){
18530             this.focus.defer(10, this);
18531             var ml = this.menuListeners;
18532             this.menu.un("select", ml.select,  this);
18533             this.menu.un("show", ml.show,  this);
18534             this.menu.un("hide", ml.hide,  this);
18535         }
18536     },
18537     // private
18538     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18539     onTriggerClick : function(){
18540         if(this.disabled){
18541             return;
18542         }
18543         if(this.menu == null){
18544             this.menu = new Roo.menu.DateMenu();
18545            
18546         }
18547         
18548         Roo.apply(this.menu.picker,  {
18549             
18550             showClear: this.allowBlank,
18551             minDate : this.minValue,
18552             maxDate : this.maxValue,
18553             disabledDatesRE : this.ddMatch,
18554             disabledDatesText : this.disabledDatesText,
18555             
18556             format : this.useIso ? 'Y-m-d' : this.format,
18557             minText : String.format(this.minText, this.formatDate(this.minValue)),
18558             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18559             
18560         });
18561          this.menu.on(Roo.apply({}, this.menuListeners, {
18562             scope:this
18563         }));
18564        
18565         
18566         var m = this.menu;
18567         var p = m.picker;
18568         
18569         // hide month picker get's called when we called by 'before hide';
18570         
18571         var ignorehide = true;
18572         p.hideMonthPicker  = function(disableAnim){
18573             if (ignorehide) {
18574                 return;
18575             }
18576              if(this.monthPicker){
18577                 Roo.log("hideMonthPicker called");
18578                 if(disableAnim === true){
18579                     this.monthPicker.hide();
18580                 }else{
18581                     this.monthPicker.slideOut('t', {duration:.2});
18582                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18583                     p.fireEvent("select", this, this.value);
18584                     m.hide();
18585                 }
18586             }
18587         }
18588         
18589         Roo.log('picker set value');
18590         Roo.log(this.getValue());
18591         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18592         m.show(this.el, 'tl-bl?');
18593         ignorehide  = false;
18594         // this will trigger hideMonthPicker..
18595         
18596         
18597         // hidden the day picker
18598         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18599         
18600         
18601         
18602       
18603         
18604         p.showMonthPicker.defer(100, p);
18605     
18606         
18607        
18608     },
18609
18610     beforeBlur : function(){
18611         var v = this.parseDate(this.getRawValue());
18612         if(v){
18613             this.setValue(v);
18614         }
18615     }
18616
18617     /** @cfg {Boolean} grow @hide */
18618     /** @cfg {Number} growMin @hide */
18619     /** @cfg {Number} growMax @hide */
18620     /**
18621      * @hide
18622      * @method autoSize
18623      */
18624 });/*
18625  * Based on:
18626  * Ext JS Library 1.1.1
18627  * Copyright(c) 2006-2007, Ext JS, LLC.
18628  *
18629  * Originally Released Under LGPL - original licence link has changed is not relivant.
18630  *
18631  * Fork - LGPL
18632  * <script type="text/javascript">
18633  */
18634  
18635
18636 /**
18637  * @class Roo.form.ComboBox
18638  * @extends Roo.form.TriggerField
18639  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18640  * @constructor
18641  * Create a new ComboBox.
18642  * @param {Object} config Configuration options
18643  */
18644 Roo.form.ComboBox = function(config){
18645     Roo.form.ComboBox.superclass.constructor.call(this, config);
18646     this.addEvents({
18647         /**
18648          * @event expand
18649          * Fires when the dropdown list is expanded
18650              * @param {Roo.form.ComboBox} combo This combo box
18651              */
18652         'expand' : true,
18653         /**
18654          * @event collapse
18655          * Fires when the dropdown list is collapsed
18656              * @param {Roo.form.ComboBox} combo This combo box
18657              */
18658         'collapse' : true,
18659         /**
18660          * @event beforeselect
18661          * Fires before a list item is selected. Return false to cancel the selection.
18662              * @param {Roo.form.ComboBox} combo This combo box
18663              * @param {Roo.data.Record} record The data record returned from the underlying store
18664              * @param {Number} index The index of the selected item in the dropdown list
18665              */
18666         'beforeselect' : true,
18667         /**
18668          * @event select
18669          * Fires when a list item is selected
18670              * @param {Roo.form.ComboBox} combo This combo box
18671              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18672              * @param {Number} index The index of the selected item in the dropdown list
18673              */
18674         'select' : true,
18675         /**
18676          * @event beforequery
18677          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18678          * The event object passed has these properties:
18679              * @param {Roo.form.ComboBox} combo This combo box
18680              * @param {String} query The query
18681              * @param {Boolean} forceAll true to force "all" query
18682              * @param {Boolean} cancel true to cancel the query
18683              * @param {Object} e The query event object
18684              */
18685         'beforequery': true,
18686          /**
18687          * @event add
18688          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18689              * @param {Roo.form.ComboBox} combo This combo box
18690              */
18691         'add' : true,
18692         /**
18693          * @event edit
18694          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18695              * @param {Roo.form.ComboBox} combo This combo box
18696              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18697              */
18698         'edit' : true
18699         
18700         
18701     });
18702     if(this.transform){
18703         this.allowDomMove = false;
18704         var s = Roo.getDom(this.transform);
18705         if(!this.hiddenName){
18706             this.hiddenName = s.name;
18707         }
18708         if(!this.store){
18709             this.mode = 'local';
18710             var d = [], opts = s.options;
18711             for(var i = 0, len = opts.length;i < len; i++){
18712                 var o = opts[i];
18713                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18714                 if(o.selected) {
18715                     this.value = value;
18716                 }
18717                 d.push([value, o.text]);
18718             }
18719             this.store = new Roo.data.SimpleStore({
18720                 'id': 0,
18721                 fields: ['value', 'text'],
18722                 data : d
18723             });
18724             this.valueField = 'value';
18725             this.displayField = 'text';
18726         }
18727         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18728         if(!this.lazyRender){
18729             this.target = true;
18730             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18731             s.parentNode.removeChild(s); // remove it
18732             this.render(this.el.parentNode);
18733         }else{
18734             s.parentNode.removeChild(s); // remove it
18735         }
18736
18737     }
18738     if (this.store) {
18739         this.store = Roo.factory(this.store, Roo.data);
18740     }
18741     
18742     this.selectedIndex = -1;
18743     if(this.mode == 'local'){
18744         if(config.queryDelay === undefined){
18745             this.queryDelay = 10;
18746         }
18747         if(config.minChars === undefined){
18748             this.minChars = 0;
18749         }
18750     }
18751 };
18752
18753 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18754     /**
18755      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18756      */
18757     /**
18758      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18759      * rendering into an Roo.Editor, defaults to false)
18760      */
18761     /**
18762      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18763      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18764      */
18765     /**
18766      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18767      */
18768     /**
18769      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18770      * the dropdown list (defaults to undefined, with no header element)
18771      */
18772
18773      /**
18774      * @cfg {String/Roo.Template} tpl The template to use to render the output
18775      */
18776      
18777     // private
18778     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18779     /**
18780      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18781      */
18782     listWidth: undefined,
18783     /**
18784      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18785      * mode = 'remote' or 'text' if mode = 'local')
18786      */
18787     displayField: undefined,
18788     /**
18789      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18790      * mode = 'remote' or 'value' if mode = 'local'). 
18791      * Note: use of a valueField requires the user make a selection
18792      * in order for a value to be mapped.
18793      */
18794     valueField: undefined,
18795     
18796     
18797     /**
18798      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18799      * field's data value (defaults to the underlying DOM element's name)
18800      */
18801     hiddenName: undefined,
18802     /**
18803      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18804      */
18805     listClass: '',
18806     /**
18807      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18808      */
18809     selectedClass: 'x-combo-selected',
18810     /**
18811      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18812      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18813      * which displays a downward arrow icon).
18814      */
18815     triggerClass : 'x-form-arrow-trigger',
18816     /**
18817      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18818      */
18819     shadow:'sides',
18820     /**
18821      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18822      * anchor positions (defaults to 'tl-bl')
18823      */
18824     listAlign: 'tl-bl?',
18825     /**
18826      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18827      */
18828     maxHeight: 300,
18829     /**
18830      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18831      * query specified by the allQuery config option (defaults to 'query')
18832      */
18833     triggerAction: 'query',
18834     /**
18835      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18836      * (defaults to 4, does not apply if editable = false)
18837      */
18838     minChars : 4,
18839     /**
18840      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18841      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18842      */
18843     typeAhead: false,
18844     /**
18845      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18846      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18847      */
18848     queryDelay: 500,
18849     /**
18850      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18851      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18852      */
18853     pageSize: 0,
18854     /**
18855      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18856      * when editable = true (defaults to false)
18857      */
18858     selectOnFocus:false,
18859     /**
18860      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18861      */
18862     queryParam: 'query',
18863     /**
18864      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18865      * when mode = 'remote' (defaults to 'Loading...')
18866      */
18867     loadingText: 'Loading...',
18868     /**
18869      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18870      */
18871     resizable: false,
18872     /**
18873      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18874      */
18875     handleHeight : 8,
18876     /**
18877      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18878      * traditional select (defaults to true)
18879      */
18880     editable: true,
18881     /**
18882      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18883      */
18884     allQuery: '',
18885     /**
18886      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18887      */
18888     mode: 'remote',
18889     /**
18890      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18891      * listWidth has a higher value)
18892      */
18893     minListWidth : 70,
18894     /**
18895      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18896      * allow the user to set arbitrary text into the field (defaults to false)
18897      */
18898     forceSelection:false,
18899     /**
18900      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18901      * if typeAhead = true (defaults to 250)
18902      */
18903     typeAheadDelay : 250,
18904     /**
18905      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18906      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18907      */
18908     valueNotFoundText : undefined,
18909     /**
18910      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18911      */
18912     blockFocus : false,
18913     
18914     /**
18915      * @cfg {Boolean} disableClear Disable showing of clear button.
18916      */
18917     disableClear : false,
18918     /**
18919      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18920      */
18921     alwaysQuery : false,
18922     
18923     //private
18924     addicon : false,
18925     editicon: false,
18926     
18927     // element that contains real text value.. (when hidden is used..)
18928      
18929     // private
18930     onRender : function(ct, position)
18931     {
18932         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18933         
18934         if(this.hiddenName){
18935             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18936                     'before', true);
18937             this.hiddenField.value =
18938                 this.hiddenValue !== undefined ? this.hiddenValue :
18939                 this.value !== undefined ? this.value : '';
18940
18941             // prevent input submission
18942             this.el.dom.removeAttribute('name');
18943              
18944              
18945         }
18946         
18947         if(Roo.isGecko){
18948             this.el.dom.setAttribute('autocomplete', 'off');
18949         }
18950
18951         var cls = 'x-combo-list';
18952
18953         this.list = new Roo.Layer({
18954             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18955         });
18956
18957         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18958         this.list.setWidth(lw);
18959         this.list.swallowEvent('mousewheel');
18960         this.assetHeight = 0;
18961
18962         if(this.title){
18963             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18964             this.assetHeight += this.header.getHeight();
18965         }
18966
18967         this.innerList = this.list.createChild({cls:cls+'-inner'});
18968         this.innerList.on('mouseover', this.onViewOver, this);
18969         this.innerList.on('mousemove', this.onViewMove, this);
18970         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18971         
18972         if(this.allowBlank && !this.pageSize && !this.disableClear){
18973             this.footer = this.list.createChild({cls:cls+'-ft'});
18974             this.pageTb = new Roo.Toolbar(this.footer);
18975            
18976         }
18977         if(this.pageSize){
18978             this.footer = this.list.createChild({cls:cls+'-ft'});
18979             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18980                     {pageSize: this.pageSize});
18981             
18982         }
18983         
18984         if (this.pageTb && this.allowBlank && !this.disableClear) {
18985             var _this = this;
18986             this.pageTb.add(new Roo.Toolbar.Fill(), {
18987                 cls: 'x-btn-icon x-btn-clear',
18988                 text: '&#160;',
18989                 handler: function()
18990                 {
18991                     _this.collapse();
18992                     _this.clearValue();
18993                     _this.onSelect(false, -1);
18994                 }
18995             });
18996         }
18997         if (this.footer) {
18998             this.assetHeight += this.footer.getHeight();
18999         }
19000         
19001
19002         if(!this.tpl){
19003             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
19004         }
19005
19006         this.view = new Roo.View(this.innerList, this.tpl, {
19007             singleSelect:true,
19008             store: this.store,
19009             selectedClass: this.selectedClass
19010         });
19011
19012         this.view.on('click', this.onViewClick, this);
19013
19014         this.store.on('beforeload', this.onBeforeLoad, this);
19015         this.store.on('load', this.onLoad, this);
19016         this.store.on('loadexception', this.onLoadException, this);
19017
19018         if(this.resizable){
19019             this.resizer = new Roo.Resizable(this.list,  {
19020                pinned:true, handles:'se'
19021             });
19022             this.resizer.on('resize', function(r, w, h){
19023                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
19024                 this.listWidth = w;
19025                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
19026                 this.restrictHeight();
19027             }, this);
19028             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
19029         }
19030         if(!this.editable){
19031             this.editable = true;
19032             this.setEditable(false);
19033         }  
19034         
19035         
19036         if (typeof(this.events.add.listeners) != 'undefined') {
19037             
19038             this.addicon = this.wrap.createChild(
19039                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
19040        
19041             this.addicon.on('click', function(e) {
19042                 this.fireEvent('add', this);
19043             }, this);
19044         }
19045         if (typeof(this.events.edit.listeners) != 'undefined') {
19046             
19047             this.editicon = this.wrap.createChild(
19048                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
19049             if (this.addicon) {
19050                 this.editicon.setStyle('margin-left', '40px');
19051             }
19052             this.editicon.on('click', function(e) {
19053                 
19054                 // we fire even  if inothing is selected..
19055                 this.fireEvent('edit', this, this.lastData );
19056                 
19057             }, this);
19058         }
19059         
19060         
19061         
19062     },
19063
19064     // private
19065     initEvents : function(){
19066         Roo.form.ComboBox.superclass.initEvents.call(this);
19067
19068         this.keyNav = new Roo.KeyNav(this.el, {
19069             "up" : function(e){
19070                 this.inKeyMode = true;
19071                 this.selectPrev();
19072             },
19073
19074             "down" : function(e){
19075                 if(!this.isExpanded()){
19076                     this.onTriggerClick();
19077                 }else{
19078                     this.inKeyMode = true;
19079                     this.selectNext();
19080                 }
19081             },
19082
19083             "enter" : function(e){
19084                 this.onViewClick();
19085                 //return true;
19086             },
19087
19088             "esc" : function(e){
19089                 this.collapse();
19090             },
19091
19092             "tab" : function(e){
19093                 this.onViewClick(false);
19094                 this.fireEvent("specialkey", this, e);
19095                 return true;
19096             },
19097
19098             scope : this,
19099
19100             doRelay : function(foo, bar, hname){
19101                 if(hname == 'down' || this.scope.isExpanded()){
19102                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19103                 }
19104                 return true;
19105             },
19106
19107             forceKeyDown: true
19108         });
19109         this.queryDelay = Math.max(this.queryDelay || 10,
19110                 this.mode == 'local' ? 10 : 250);
19111         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19112         if(this.typeAhead){
19113             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19114         }
19115         if(this.editable !== false){
19116             this.el.on("keyup", this.onKeyUp, this);
19117         }
19118         if(this.forceSelection){
19119             this.on('blur', this.doForce, this);
19120         }
19121     },
19122
19123     onDestroy : function(){
19124         if(this.view){
19125             this.view.setStore(null);
19126             this.view.el.removeAllListeners();
19127             this.view.el.remove();
19128             this.view.purgeListeners();
19129         }
19130         if(this.list){
19131             this.list.destroy();
19132         }
19133         if(this.store){
19134             this.store.un('beforeload', this.onBeforeLoad, this);
19135             this.store.un('load', this.onLoad, this);
19136             this.store.un('loadexception', this.onLoadException, this);
19137         }
19138         Roo.form.ComboBox.superclass.onDestroy.call(this);
19139     },
19140
19141     // private
19142     fireKey : function(e){
19143         if(e.isNavKeyPress() && !this.list.isVisible()){
19144             this.fireEvent("specialkey", this, e);
19145         }
19146     },
19147
19148     // private
19149     onResize: function(w, h){
19150         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19151         
19152         if(typeof w != 'number'){
19153             // we do not handle it!?!?
19154             return;
19155         }
19156         var tw = this.trigger.getWidth();
19157         tw += this.addicon ? this.addicon.getWidth() : 0;
19158         tw += this.editicon ? this.editicon.getWidth() : 0;
19159         var x = w - tw;
19160         this.el.setWidth( this.adjustWidth('input', x));
19161             
19162         this.trigger.setStyle('left', x+'px');
19163         
19164         if(this.list && this.listWidth === undefined){
19165             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19166             this.list.setWidth(lw);
19167             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19168         }
19169         
19170     
19171         
19172     },
19173
19174     /**
19175      * Allow or prevent the user from directly editing the field text.  If false is passed,
19176      * the user will only be able to select from the items defined in the dropdown list.  This method
19177      * is the runtime equivalent of setting the 'editable' config option at config time.
19178      * @param {Boolean} value True to allow the user to directly edit the field text
19179      */
19180     setEditable : function(value){
19181         if(value == this.editable){
19182             return;
19183         }
19184         this.editable = value;
19185         if(!value){
19186             this.el.dom.setAttribute('readOnly', true);
19187             this.el.on('mousedown', this.onTriggerClick,  this);
19188             this.el.addClass('x-combo-noedit');
19189         }else{
19190             this.el.dom.setAttribute('readOnly', false);
19191             this.el.un('mousedown', this.onTriggerClick,  this);
19192             this.el.removeClass('x-combo-noedit');
19193         }
19194     },
19195
19196     // private
19197     onBeforeLoad : function(){
19198         if(!this.hasFocus){
19199             return;
19200         }
19201         this.innerList.update(this.loadingText ?
19202                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19203         this.restrictHeight();
19204         this.selectedIndex = -1;
19205     },
19206
19207     // private
19208     onLoad : function(){
19209         if(!this.hasFocus){
19210             return;
19211         }
19212         if(this.store.getCount() > 0){
19213             this.expand();
19214             this.restrictHeight();
19215             if(this.lastQuery == this.allQuery){
19216                 if(this.editable){
19217                     this.el.dom.select();
19218                 }
19219                 if(!this.selectByValue(this.value, true)){
19220                     this.select(0, true);
19221                 }
19222             }else{
19223                 this.selectNext();
19224                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19225                     this.taTask.delay(this.typeAheadDelay);
19226                 }
19227             }
19228         }else{
19229             this.onEmptyResults();
19230         }
19231         //this.el.focus();
19232     },
19233     // private
19234     onLoadException : function()
19235     {
19236         this.collapse();
19237         Roo.log(this.store.reader.jsonData);
19238         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19239             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19240         }
19241         
19242         
19243     },
19244     // private
19245     onTypeAhead : function(){
19246         if(this.store.getCount() > 0){
19247             var r = this.store.getAt(0);
19248             var newValue = r.data[this.displayField];
19249             var len = newValue.length;
19250             var selStart = this.getRawValue().length;
19251             if(selStart != len){
19252                 this.setRawValue(newValue);
19253                 this.selectText(selStart, newValue.length);
19254             }
19255         }
19256     },
19257
19258     // private
19259     onSelect : function(record, index){
19260         if(this.fireEvent('beforeselect', this, record, index) !== false){
19261             this.setFromData(index > -1 ? record.data : false);
19262             this.collapse();
19263             this.fireEvent('select', this, record, index);
19264         }
19265     },
19266
19267     /**
19268      * Returns the currently selected field value or empty string if no value is set.
19269      * @return {String} value The selected value
19270      */
19271     getValue : function(){
19272         if(this.valueField){
19273             return typeof this.value != 'undefined' ? this.value : '';
19274         }
19275         return Roo.form.ComboBox.superclass.getValue.call(this);
19276     },
19277
19278     /**
19279      * Clears any text/value currently set in the field
19280      */
19281     clearValue : function(){
19282         if(this.hiddenField){
19283             this.hiddenField.value = '';
19284         }
19285         this.value = '';
19286         this.setRawValue('');
19287         this.lastSelectionText = '';
19288         
19289     },
19290
19291     /**
19292      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19293      * will be displayed in the field.  If the value does not match the data value of an existing item,
19294      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19295      * Otherwise the field will be blank (although the value will still be set).
19296      * @param {String} value The value to match
19297      */
19298     setValue : function(v){
19299         var text = v;
19300         if(this.valueField){
19301             var r = this.findRecord(this.valueField, v);
19302             if(r){
19303                 text = r.data[this.displayField];
19304             }else if(this.valueNotFoundText !== undefined){
19305                 text = this.valueNotFoundText;
19306             }
19307         }
19308         this.lastSelectionText = text;
19309         if(this.hiddenField){
19310             this.hiddenField.value = v;
19311         }
19312         Roo.form.ComboBox.superclass.setValue.call(this, text);
19313         this.value = v;
19314     },
19315     /**
19316      * @property {Object} the last set data for the element
19317      */
19318     
19319     lastData : false,
19320     /**
19321      * Sets the value of the field based on a object which is related to the record format for the store.
19322      * @param {Object} value the value to set as. or false on reset?
19323      */
19324     setFromData : function(o){
19325         var dv = ''; // display value
19326         var vv = ''; // value value..
19327         this.lastData = o;
19328         if (this.displayField) {
19329             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19330         } else {
19331             // this is an error condition!!!
19332             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19333         }
19334         
19335         if(this.valueField){
19336             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19337         }
19338         if(this.hiddenField){
19339             this.hiddenField.value = vv;
19340             
19341             this.lastSelectionText = dv;
19342             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19343             this.value = vv;
19344             return;
19345         }
19346         // no hidden field.. - we store the value in 'value', but still display
19347         // display field!!!!
19348         this.lastSelectionText = dv;
19349         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19350         this.value = vv;
19351         
19352         
19353     },
19354     // private
19355     reset : function(){
19356         // overridden so that last data is reset..
19357         this.setValue(this.resetValue);
19358         this.originalValue = this.getValue();
19359         this.clearInvalid();
19360         this.lastData = false;
19361         if (this.view) {
19362             this.view.clearSelections();
19363         }
19364     },
19365     // private
19366     findRecord : function(prop, value){
19367         var record;
19368         if(this.store.getCount() > 0){
19369             this.store.each(function(r){
19370                 if(r.data[prop] == value){
19371                     record = r;
19372                     return false;
19373                 }
19374                 return true;
19375             });
19376         }
19377         return record;
19378     },
19379     
19380     getName: function()
19381     {
19382         // returns hidden if it's set..
19383         if (!this.rendered) {return ''};
19384         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19385         
19386     },
19387     // private
19388     onViewMove : function(e, t){
19389         this.inKeyMode = false;
19390     },
19391
19392     // private
19393     onViewOver : function(e, t){
19394         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19395             return;
19396         }
19397         var item = this.view.findItemFromChild(t);
19398         if(item){
19399             var index = this.view.indexOf(item);
19400             this.select(index, false);
19401         }
19402     },
19403
19404     // private
19405     onViewClick : function(doFocus)
19406     {
19407         var index = this.view.getSelectedIndexes()[0];
19408         var r = this.store.getAt(index);
19409         if(r){
19410             this.onSelect(r, index);
19411         }
19412         if(doFocus !== false && !this.blockFocus){
19413             this.el.focus();
19414         }
19415     },
19416
19417     // private
19418     restrictHeight : function(){
19419         this.innerList.dom.style.height = '';
19420         var inner = this.innerList.dom;
19421         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19422         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19423         this.list.beginUpdate();
19424         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19425         this.list.alignTo(this.el, this.listAlign);
19426         this.list.endUpdate();
19427     },
19428
19429     // private
19430     onEmptyResults : function(){
19431         this.collapse();
19432     },
19433
19434     /**
19435      * Returns true if the dropdown list is expanded, else false.
19436      */
19437     isExpanded : function(){
19438         return this.list.isVisible();
19439     },
19440
19441     /**
19442      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19443      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19444      * @param {String} value The data value of the item to select
19445      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19446      * selected item if it is not currently in view (defaults to true)
19447      * @return {Boolean} True if the value matched an item in the list, else false
19448      */
19449     selectByValue : function(v, scrollIntoView){
19450         if(v !== undefined && v !== null){
19451             var r = this.findRecord(this.valueField || this.displayField, v);
19452             if(r){
19453                 this.select(this.store.indexOf(r), scrollIntoView);
19454                 return true;
19455             }
19456         }
19457         return false;
19458     },
19459
19460     /**
19461      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19462      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19463      * @param {Number} index The zero-based index of the list item to select
19464      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19465      * selected item if it is not currently in view (defaults to true)
19466      */
19467     select : function(index, scrollIntoView){
19468         this.selectedIndex = index;
19469         this.view.select(index);
19470         if(scrollIntoView !== false){
19471             var el = this.view.getNode(index);
19472             if(el){
19473                 this.innerList.scrollChildIntoView(el, false);
19474             }
19475         }
19476     },
19477
19478     // private
19479     selectNext : function(){
19480         var ct = this.store.getCount();
19481         if(ct > 0){
19482             if(this.selectedIndex == -1){
19483                 this.select(0);
19484             }else if(this.selectedIndex < ct-1){
19485                 this.select(this.selectedIndex+1);
19486             }
19487         }
19488     },
19489
19490     // private
19491     selectPrev : function(){
19492         var ct = this.store.getCount();
19493         if(ct > 0){
19494             if(this.selectedIndex == -1){
19495                 this.select(0);
19496             }else if(this.selectedIndex != 0){
19497                 this.select(this.selectedIndex-1);
19498             }
19499         }
19500     },
19501
19502     // private
19503     onKeyUp : function(e){
19504         if(this.editable !== false && !e.isSpecialKey()){
19505             this.lastKey = e.getKey();
19506             this.dqTask.delay(this.queryDelay);
19507         }
19508     },
19509
19510     // private
19511     validateBlur : function(){
19512         return !this.list || !this.list.isVisible();   
19513     },
19514
19515     // private
19516     initQuery : function(){
19517         this.doQuery(this.getRawValue());
19518     },
19519
19520     // private
19521     doForce : function(){
19522         if(this.el.dom.value.length > 0){
19523             this.el.dom.value =
19524                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19525              
19526         }
19527     },
19528
19529     /**
19530      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19531      * query allowing the query action to be canceled if needed.
19532      * @param {String} query The SQL query to execute
19533      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19534      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19535      * saved in the current store (defaults to false)
19536      */
19537     doQuery : function(q, forceAll){
19538         if(q === undefined || q === null){
19539             q = '';
19540         }
19541         var qe = {
19542             query: q,
19543             forceAll: forceAll,
19544             combo: this,
19545             cancel:false
19546         };
19547         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19548             return false;
19549         }
19550         q = qe.query;
19551         forceAll = qe.forceAll;
19552         if(forceAll === true || (q.length >= this.minChars)){
19553             if(this.lastQuery != q || this.alwaysQuery){
19554                 this.lastQuery = q;
19555                 if(this.mode == 'local'){
19556                     this.selectedIndex = -1;
19557                     if(forceAll){
19558                         this.store.clearFilter();
19559                     }else{
19560                         this.store.filter(this.displayField, q);
19561                     }
19562                     this.onLoad();
19563                 }else{
19564                     this.store.baseParams[this.queryParam] = q;
19565                     this.store.load({
19566                         params: this.getParams(q)
19567                     });
19568                     this.expand();
19569                 }
19570             }else{
19571                 this.selectedIndex = -1;
19572                 this.onLoad();   
19573             }
19574         }
19575     },
19576
19577     // private
19578     getParams : function(q){
19579         var p = {};
19580         //p[this.queryParam] = q;
19581         if(this.pageSize){
19582             p.start = 0;
19583             p.limit = this.pageSize;
19584         }
19585         return p;
19586     },
19587
19588     /**
19589      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19590      */
19591     collapse : function(){
19592         if(!this.isExpanded()){
19593             return;
19594         }
19595         this.list.hide();
19596         Roo.get(document).un('mousedown', this.collapseIf, this);
19597         Roo.get(document).un('mousewheel', this.collapseIf, this);
19598         if (!this.editable) {
19599             Roo.get(document).un('keydown', this.listKeyPress, this);
19600         }
19601         this.fireEvent('collapse', this);
19602     },
19603
19604     // private
19605     collapseIf : function(e){
19606         if(!e.within(this.wrap) && !e.within(this.list)){
19607             this.collapse();
19608         }
19609     },
19610
19611     /**
19612      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19613      */
19614     expand : function(){
19615         if(this.isExpanded() || !this.hasFocus){
19616             return;
19617         }
19618         this.list.alignTo(this.el, this.listAlign);
19619         this.list.show();
19620         Roo.get(document).on('mousedown', this.collapseIf, this);
19621         Roo.get(document).on('mousewheel', this.collapseIf, this);
19622         if (!this.editable) {
19623             Roo.get(document).on('keydown', this.listKeyPress, this);
19624         }
19625         
19626         this.fireEvent('expand', this);
19627     },
19628
19629     // private
19630     // Implements the default empty TriggerField.onTriggerClick function
19631     onTriggerClick : function(){
19632         if(this.disabled){
19633             return;
19634         }
19635         if(this.isExpanded()){
19636             this.collapse();
19637             if (!this.blockFocus) {
19638                 this.el.focus();
19639             }
19640             
19641         }else {
19642             this.hasFocus = true;
19643             if(this.triggerAction == 'all') {
19644                 this.doQuery(this.allQuery, true);
19645             } else {
19646                 this.doQuery(this.getRawValue());
19647             }
19648             if (!this.blockFocus) {
19649                 this.el.focus();
19650             }
19651         }
19652     },
19653     listKeyPress : function(e)
19654     {
19655         //Roo.log('listkeypress');
19656         // scroll to first matching element based on key pres..
19657         if (e.isSpecialKey()) {
19658             return false;
19659         }
19660         var k = String.fromCharCode(e.getKey()).toUpperCase();
19661         //Roo.log(k);
19662         var match  = false;
19663         var csel = this.view.getSelectedNodes();
19664         var cselitem = false;
19665         if (csel.length) {
19666             var ix = this.view.indexOf(csel[0]);
19667             cselitem  = this.store.getAt(ix);
19668             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19669                 cselitem = false;
19670             }
19671             
19672         }
19673         
19674         this.store.each(function(v) { 
19675             if (cselitem) {
19676                 // start at existing selection.
19677                 if (cselitem.id == v.id) {
19678                     cselitem = false;
19679                 }
19680                 return;
19681             }
19682                 
19683             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19684                 match = this.store.indexOf(v);
19685                 return false;
19686             }
19687         }, this);
19688         
19689         if (match === false) {
19690             return true; // no more action?
19691         }
19692         // scroll to?
19693         this.view.select(match);
19694         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19695         sn.scrollIntoView(sn.dom.parentNode, false);
19696     } 
19697
19698     /** 
19699     * @cfg {Boolean} grow 
19700     * @hide 
19701     */
19702     /** 
19703     * @cfg {Number} growMin 
19704     * @hide 
19705     */
19706     /** 
19707     * @cfg {Number} growMax 
19708     * @hide 
19709     */
19710     /**
19711      * @hide
19712      * @method autoSize
19713      */
19714 });/*
19715  * Copyright(c) 2010-2012, Roo J Solutions Limited
19716  *
19717  * Licence LGPL
19718  *
19719  */
19720
19721 /**
19722  * @class Roo.form.ComboBoxArray
19723  * @extends Roo.form.TextField
19724  * A facebook style adder... for lists of email / people / countries  etc...
19725  * pick multiple items from a combo box, and shows each one.
19726  *
19727  *  Fred [x]  Brian [x]  [Pick another |v]
19728  *
19729  *
19730  *  For this to work: it needs various extra information
19731  *    - normal combo problay has
19732  *      name, hiddenName
19733  *    + displayField, valueField
19734  *
19735  *    For our purpose...
19736  *
19737  *
19738  *   If we change from 'extends' to wrapping...
19739  *   
19740  *  
19741  *
19742  
19743  
19744  * @constructor
19745  * Create a new ComboBoxArray.
19746  * @param {Object} config Configuration options
19747  */
19748  
19749
19750 Roo.form.ComboBoxArray = function(config)
19751 {
19752     this.addEvents({
19753         /**
19754          * @event beforeremove
19755          * Fires before remove the value from the list
19756              * @param {Roo.form.ComboBoxArray} _self This combo box array
19757              * @param {Roo.form.ComboBoxArray.Item} item removed item
19758              */
19759         'beforeremove' : true,
19760         /**
19761          * @event remove
19762          * Fires when remove the value from the list
19763              * @param {Roo.form.ComboBoxArray} _self This combo box array
19764              * @param {Roo.form.ComboBoxArray.Item} item removed item
19765              */
19766         'remove' : true
19767         
19768         
19769     });
19770     
19771     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19772     
19773     this.items = new Roo.util.MixedCollection(false);
19774     
19775     // construct the child combo...
19776     
19777     
19778     
19779     
19780    
19781     
19782 }
19783
19784  
19785 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19786
19787     /**
19788      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19789      */
19790     
19791     lastData : false,
19792     
19793     // behavies liek a hiddne field
19794     inputType:      'hidden',
19795     /**
19796      * @cfg {Number} width The width of the box that displays the selected element
19797      */ 
19798     width:          300,
19799
19800     
19801     
19802     /**
19803      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19804      */
19805     name : false,
19806     /**
19807      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19808      */
19809     hiddenName : false,
19810     
19811     
19812     // private the array of items that are displayed..
19813     items  : false,
19814     // private - the hidden field el.
19815     hiddenEl : false,
19816     // private - the filed el..
19817     el : false,
19818     
19819     //validateValue : function() { return true; }, // all values are ok!
19820     //onAddClick: function() { },
19821     
19822     onRender : function(ct, position) 
19823     {
19824         
19825         // create the standard hidden element
19826         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19827         
19828         
19829         // give fake names to child combo;
19830         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19831         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19832         
19833         this.combo = Roo.factory(this.combo, Roo.form);
19834         this.combo.onRender(ct, position);
19835         if (typeof(this.combo.width) != 'undefined') {
19836             this.combo.onResize(this.combo.width,0);
19837         }
19838         
19839         this.combo.initEvents();
19840         
19841         // assigned so form know we need to do this..
19842         this.store          = this.combo.store;
19843         this.valueField     = this.combo.valueField;
19844         this.displayField   = this.combo.displayField ;
19845         
19846         
19847         this.combo.wrap.addClass('x-cbarray-grp');
19848         
19849         var cbwrap = this.combo.wrap.createChild(
19850             {tag: 'div', cls: 'x-cbarray-cb'},
19851             this.combo.el.dom
19852         );
19853         
19854              
19855         this.hiddenEl = this.combo.wrap.createChild({
19856             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19857         });
19858         this.el = this.combo.wrap.createChild({
19859             tag: 'input',  type:'hidden' , name: this.name, value : ''
19860         });
19861          //   this.el.dom.removeAttribute("name");
19862         
19863         
19864         this.outerWrap = this.combo.wrap;
19865         this.wrap = cbwrap;
19866         
19867         this.outerWrap.setWidth(this.width);
19868         this.outerWrap.dom.removeChild(this.el.dom);
19869         
19870         this.wrap.dom.appendChild(this.el.dom);
19871         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19872         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19873         
19874         this.combo.trigger.setStyle('position','relative');
19875         this.combo.trigger.setStyle('left', '0px');
19876         this.combo.trigger.setStyle('top', '2px');
19877         
19878         this.combo.el.setStyle('vertical-align', 'text-bottom');
19879         
19880         //this.trigger.setStyle('vertical-align', 'top');
19881         
19882         // this should use the code from combo really... on('add' ....)
19883         if (this.adder) {
19884             
19885         
19886             this.adder = this.outerWrap.createChild(
19887                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19888             var _t = this;
19889             this.adder.on('click', function(e) {
19890                 _t.fireEvent('adderclick', this, e);
19891             }, _t);
19892         }
19893         //var _t = this;
19894         //this.adder.on('click', this.onAddClick, _t);
19895         
19896         
19897         this.combo.on('select', function(cb, rec, ix) {
19898             this.addItem(rec.data);
19899             
19900             cb.setValue('');
19901             cb.el.dom.value = '';
19902             //cb.lastData = rec.data;
19903             // add to list
19904             
19905         }, this);
19906         
19907         
19908     },
19909     
19910     
19911     getName: function()
19912     {
19913         // returns hidden if it's set..
19914         if (!this.rendered) {return ''};
19915         return  this.hiddenName ? this.hiddenName : this.name;
19916         
19917     },
19918     
19919     
19920     onResize: function(w, h){
19921         
19922         return;
19923         // not sure if this is needed..
19924         //this.combo.onResize(w,h);
19925         
19926         if(typeof w != 'number'){
19927             // we do not handle it!?!?
19928             return;
19929         }
19930         var tw = this.combo.trigger.getWidth();
19931         tw += this.addicon ? this.addicon.getWidth() : 0;
19932         tw += this.editicon ? this.editicon.getWidth() : 0;
19933         var x = w - tw;
19934         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19935             
19936         this.combo.trigger.setStyle('left', '0px');
19937         
19938         if(this.list && this.listWidth === undefined){
19939             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19940             this.list.setWidth(lw);
19941             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19942         }
19943         
19944     
19945         
19946     },
19947     
19948     addItem: function(rec)
19949     {
19950         var valueField = this.combo.valueField;
19951         var displayField = this.combo.displayField;
19952         
19953         if (this.items.indexOfKey(rec[valueField]) > -1) {
19954             //console.log("GOT " + rec.data.id);
19955             return;
19956         }
19957         
19958         var x = new Roo.form.ComboBoxArray.Item({
19959             //id : rec[this.idField],
19960             data : rec,
19961             displayField : displayField ,
19962             tipField : displayField ,
19963             cb : this
19964         });
19965         // use the 
19966         this.items.add(rec[valueField],x);
19967         // add it before the element..
19968         this.updateHiddenEl();
19969         x.render(this.outerWrap, this.wrap.dom);
19970         // add the image handler..
19971     },
19972     
19973     updateHiddenEl : function()
19974     {
19975         this.validate();
19976         if (!this.hiddenEl) {
19977             return;
19978         }
19979         var ar = [];
19980         var idField = this.combo.valueField;
19981         
19982         this.items.each(function(f) {
19983             ar.push(f.data[idField]);
19984         });
19985         this.hiddenEl.dom.value = ar.join(',');
19986         this.validate();
19987     },
19988     
19989     reset : function()
19990     {
19991         this.items.clear();
19992         
19993         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19994            el.remove();
19995         });
19996         
19997         this.el.dom.value = '';
19998         if (this.hiddenEl) {
19999             this.hiddenEl.dom.value = '';
20000         }
20001         
20002     },
20003     getValue: function()
20004     {
20005         return this.hiddenEl ? this.hiddenEl.dom.value : '';
20006     },
20007     setValue: function(v) // not a valid action - must use addItems..
20008     {
20009         
20010         this.reset();
20011          
20012         if (this.store.isLocal && (typeof(v) == 'string')) {
20013             // then we can use the store to find the values..
20014             // comma seperated at present.. this needs to allow JSON based encoding..
20015             this.hiddenEl.value  = v;
20016             var v_ar = [];
20017             Roo.each(v.split(','), function(k) {
20018                 Roo.log("CHECK " + this.valueField + ',' + k);
20019                 var li = this.store.query(this.valueField, k);
20020                 if (!li.length) {
20021                     return;
20022                 }
20023                 var add = {};
20024                 add[this.valueField] = k;
20025                 add[this.displayField] = li.item(0).data[this.displayField];
20026                 
20027                 this.addItem(add);
20028             }, this) 
20029              
20030         }
20031         if (typeof(v) == 'object' ) {
20032             // then let's assume it's an array of objects..
20033             Roo.each(v, function(l) {
20034                 this.addItem(l);
20035             }, this);
20036              
20037         }
20038         
20039         
20040     },
20041     setFromData: function(v)
20042     {
20043         // this recieves an object, if setValues is called.
20044         this.reset();
20045         this.el.dom.value = v[this.displayField];
20046         this.hiddenEl.dom.value = v[this.valueField];
20047         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20048             return;
20049         }
20050         var kv = v[this.valueField];
20051         var dv = v[this.displayField];
20052         kv = typeof(kv) != 'string' ? '' : kv;
20053         dv = typeof(dv) != 'string' ? '' : dv;
20054         
20055         
20056         var keys = kv.split(',');
20057         var display = dv.split(',');
20058         for (var i = 0 ; i < keys.length; i++) {
20059             
20060             add = {};
20061             add[this.valueField] = keys[i];
20062             add[this.displayField] = display[i];
20063             this.addItem(add);
20064         }
20065       
20066         
20067     },
20068     
20069     /**
20070      * Validates the combox array value
20071      * @return {Boolean} True if the value is valid, else false
20072      */
20073     validate : function(){
20074         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20075             this.clearInvalid();
20076             return true;
20077         }
20078         return false;
20079     },
20080     
20081     validateValue : function(value){
20082         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20083         
20084     },
20085     
20086     /*@
20087      * overide
20088      * 
20089      */
20090     isDirty : function() {
20091         if(this.disabled) {
20092             return false;
20093         }
20094         
20095         try {
20096             var d = Roo.decode(String(this.originalValue));
20097         } catch (e) {
20098             return String(this.getValue()) !== String(this.originalValue);
20099         }
20100         
20101         var originalValue = [];
20102         
20103         for (var i = 0; i < d.length; i++){
20104             originalValue.push(d[i][this.valueField]);
20105         }
20106         
20107         return String(this.getValue()) !== String(originalValue.join(','));
20108         
20109     }
20110     
20111 });
20112
20113
20114
20115 /**
20116  * @class Roo.form.ComboBoxArray.Item
20117  * @extends Roo.BoxComponent
20118  * A selected item in the list
20119  *  Fred [x]  Brian [x]  [Pick another |v]
20120  * 
20121  * @constructor
20122  * Create a new item.
20123  * @param {Object} config Configuration options
20124  */
20125  
20126 Roo.form.ComboBoxArray.Item = function(config) {
20127     config.id = Roo.id();
20128     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20129 }
20130
20131 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20132     data : {},
20133     cb: false,
20134     displayField : false,
20135     tipField : false,
20136     
20137     
20138     defaultAutoCreate : {
20139         tag: 'div',
20140         cls: 'x-cbarray-item',
20141         cn : [ 
20142             { tag: 'div' },
20143             {
20144                 tag: 'img',
20145                 width:16,
20146                 height : 16,
20147                 src : Roo.BLANK_IMAGE_URL ,
20148                 align: 'center'
20149             }
20150         ]
20151         
20152     },
20153     
20154  
20155     onRender : function(ct, position)
20156     {
20157         Roo.form.Field.superclass.onRender.call(this, ct, position);
20158         
20159         if(!this.el){
20160             var cfg = this.getAutoCreate();
20161             this.el = ct.createChild(cfg, position);
20162         }
20163         
20164         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20165         
20166         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20167             this.cb.renderer(this.data) :
20168             String.format('{0}',this.data[this.displayField]);
20169         
20170             
20171         this.el.child('div').dom.setAttribute('qtip',
20172                         String.format('{0}',this.data[this.tipField])
20173         );
20174         
20175         this.el.child('img').on('click', this.remove, this);
20176         
20177     },
20178    
20179     remove : function()
20180     {
20181         if(this.cb.disabled){
20182             return;
20183         }
20184         
20185         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20186             this.cb.items.remove(this);
20187             this.el.child('img').un('click', this.remove, this);
20188             this.el.remove();
20189             this.cb.updateHiddenEl();
20190
20191             this.cb.fireEvent('remove', this.cb, this);
20192         }
20193         
20194     }
20195 });/*
20196  * RooJS Library 1.1.1
20197  * Copyright(c) 2008-2011  Alan Knowles
20198  *
20199  * License - LGPL
20200  */
20201  
20202
20203 /**
20204  * @class Roo.form.ComboNested
20205  * @extends Roo.form.ComboBox
20206  * A combobox for that allows selection of nested items in a list,
20207  * eg.
20208  *
20209  *  Book
20210  *    -> red
20211  *    -> green
20212  *  Table
20213  *    -> square
20214  *      ->red
20215  *      ->green
20216  *    -> rectangle
20217  *      ->green
20218  *      
20219  * 
20220  * @constructor
20221  * Create a new ComboNested
20222  * @param {Object} config Configuration options
20223  */
20224 Roo.form.ComboNested = function(config){
20225     Roo.form.ComboCheck.superclass.constructor.call(this, config);
20226     // should verify some data...
20227     // like
20228     // hiddenName = required..
20229     // displayField = required
20230     // valudField == required
20231     var req= [ 'hiddenName', 'displayField', 'valueField' ];
20232     var _t = this;
20233     Roo.each(req, function(e) {
20234         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
20235             throw "Roo.form.ComboNested : missing value for: " + e;
20236         }
20237     });
20238      
20239     
20240 };
20241
20242 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
20243    
20244     /*
20245      * @config {Number} max Number of columns to show
20246      */
20247     
20248     maxColumns : 3,
20249    
20250     list : null, // the outermost div..
20251     innerLists : null, // the
20252     views : null,
20253     stores : null,
20254     // private
20255     onRender : function(ct, position)
20256     {
20257         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
20258         
20259         if(this.hiddenName){
20260             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
20261                     'before', true);
20262             this.hiddenField.value =
20263                 this.hiddenValue !== undefined ? this.hiddenValue :
20264                 this.value !== undefined ? this.value : '';
20265
20266             // prevent input submission
20267             this.el.dom.removeAttribute('name');
20268              
20269              
20270         }
20271         
20272         if(Roo.isGecko){
20273             this.el.dom.setAttribute('autocomplete', 'off');
20274         }
20275
20276         var cls = 'x-combo-list';
20277
20278         this.list = new Roo.Layer({
20279             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20280         });
20281
20282         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20283         this.list.setWidth(lw);
20284         this.list.swallowEvent('mousewheel');
20285         this.assetHeight = 0;
20286
20287         if(this.title){
20288             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20289             this.assetHeight += this.header.getHeight();
20290         }
20291         this.innerLists = [];
20292         this.views = [];
20293         this.stores = [];
20294         for (var i =0 ; i < this.maxColumns; i++) {
20295             this.onRenderList( cls, i);
20296         }
20297         
20298         // always needs footer, as we are going to have an 'OK' button.
20299         this.footer = this.list.createChild({cls:cls+'-ft'});
20300         this.pageTb = new Roo.Toolbar(this.footer);  
20301         var _this = this;
20302         this.pageTb.add(  {
20303             
20304             text: 'Done',
20305             handler: function()
20306             {
20307                 _this.collapse();
20308             }
20309         });
20310         
20311         if ( this.allowBlank && !this.disableClear) {
20312             
20313             this.pageTb.add(new Roo.Toolbar.Fill(), {
20314                 cls: 'x-btn-icon x-btn-clear',
20315                 text: '&#160;',
20316                 handler: function()
20317                 {
20318                     _this.collapse();
20319                     _this.clearValue();
20320                     _this.onSelect(false, -1);
20321                 }
20322             });
20323         }
20324         if (this.footer) {
20325             this.assetHeight += this.footer.getHeight();
20326         }
20327         
20328     },
20329     onRenderList : function (  cls, i)
20330     {
20331         
20332         var lw = Math.floor(
20333                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20334         );
20335         
20336         this.list.setWidth(lw); // default to '1'
20337
20338         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20339         //il.on('mouseover', this.onViewOver, this, { list:  i });
20340         //il.on('mousemove', this.onViewMove, this, { list:  i });
20341         il.setWidth(lw);
20342         il.setStyle({ 'overflow-x' : 'hidden'});
20343
20344         if(!this.tpl){
20345             this.tpl = new Roo.Template({
20346                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20347                 isEmpty: function (value, allValues) {
20348                     //Roo.log(value);
20349                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20350                     return dl ? 'has-children' : 'no-children'
20351                 }
20352             });
20353         }
20354         
20355         var store  = this.store;
20356         if (i > 0) {
20357             store  = new Roo.data.SimpleStore({
20358                 //fields : this.store.reader.meta.fields,
20359                 reader : this.store.reader,
20360                 data : [ ]
20361             });
20362         }
20363         this.stores[i]  = store;
20364                 
20365         
20366         
20367         var view = this.views[i] = new Roo.View(
20368             il,
20369             this.tpl,
20370             {
20371                 singleSelect:true,
20372                 store: store,
20373                 selectedClass: this.selectedClass
20374             }
20375         );
20376         view.getEl().setWidth(lw);
20377         view.getEl().setStyle({
20378             position: i < 1 ? 'relative' : 'absolute',
20379             top: 0,
20380             left: (i * lw ) + 'px',
20381             display : i > 0 ? 'none' : 'block'
20382         });
20383         view.on('selectionchange', this.onSelectChange, this, {list : i });
20384         view.on('dblclick', this.onDoubleClick, this, {list : i });
20385         //view.on('click', this.onViewClick, this, { list : i });
20386
20387         store.on('beforeload', this.onBeforeLoad, this);
20388         store.on('load',  this.onLoad, this, { list  : i});
20389         store.on('loadexception', this.onLoadException, this);
20390
20391         // hide the other vies..
20392         
20393         
20394         
20395     },
20396     onResize : function()  {},
20397     
20398     restrictHeight : function()
20399     {
20400         var mh = 0;
20401         Roo.each(this.innerLists, function(il,i) {
20402             var el = this.views[i].getEl();
20403             el.dom.style.height = '';
20404             var inner = el.dom;
20405             var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
20406             // only adjust heights on other ones..
20407             if (i < 1) {
20408                 
20409                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20410                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20411                 mh = Math.max(el.getHeight(), mh);
20412             }
20413             
20414             
20415         }, this);
20416         
20417         this.list.beginUpdate();
20418         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20419         this.list.alignTo(this.el, this.listAlign);
20420         this.list.endUpdate();
20421         
20422     },
20423      
20424     
20425     // -- store handlers..
20426     // private
20427     onBeforeLoad : function()
20428     {
20429         if(!this.hasFocus){
20430             return;
20431         }
20432         this.innerLists[0].update(this.loadingText ?
20433                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20434         this.restrictHeight();
20435         this.selectedIndex = -1;
20436     },
20437     // private
20438     onLoad : function(a,b,c,d)
20439     {
20440         
20441         if(!this.hasFocus){
20442             return;
20443         }
20444         
20445         if(this.store.getCount() > 0) {
20446             this.expand();
20447             this.restrictHeight();   
20448         } else {
20449             this.onEmptyResults();
20450         }
20451         /*
20452         this.stores[1].loadData([]);
20453         this.stores[2].loadData([]);
20454         this.views
20455         */    
20456     
20457         //this.el.focus();
20458     },
20459     
20460     
20461     // private
20462     onLoadException : function()
20463     {
20464         this.collapse();
20465         Roo.log(this.store.reader.jsonData);
20466         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20467             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20468         }
20469         
20470         
20471     } ,
20472      
20473      
20474
20475     onSelectChange : function (view, sels, opts )
20476     {
20477         var ix = view.getSelectedIndexes();
20478         
20479         
20480         if (opts.list > this.maxColumns - 2) {
20481              
20482             this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
20483             return;
20484         }
20485         
20486         if (!ix.length) {
20487             this.setFromData({});
20488             this.stores[opts.list+1].loadData( [] );
20489             return;
20490         }
20491         
20492         var rec = view.store.getAt(ix[0]);
20493         this.setFromData(rec.data);
20494         
20495         var lw = Math.floor(
20496                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20497         );
20498         var data =  typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
20499         var dl = typeof(data.data) != 'undefined' ? data.total : data.length; ///json is a nested response..
20500         this.stores[opts.list+1].loadData( data );
20501         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20502         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20503         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20504         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1))); 
20505     },
20506     onDoubleClick : function()
20507     {
20508         this.collapse(); //??
20509     },
20510     
20511      
20512     
20513     findRecord : function (prop,value)
20514     {
20515         return this.findRecordInStore(this.store, prop,value);
20516     },
20517     
20518      // private
20519     findRecordInStore : function(store, prop, value)
20520     {
20521         var cstore = new Roo.data.SimpleStore({
20522             //fields : this.store.reader.meta.fields, // we need array reader.. for
20523             reader : this.store.reader,
20524             data : [ ]
20525         });
20526         var _this = this;
20527         var record  = false;
20528         if(store.getCount() > 0){
20529            store.each(function(r){
20530                 if(r.data[prop] == value){
20531                     record = r;
20532                     return false;
20533                 }
20534                 if (r.data.cn && r.data.cn.length) {
20535                     cstore.loadData( r.data.cn);
20536                     var cret = _this.findRecordInStore(cstore, prop, value);
20537                     if (cret !== false) {
20538                         record = cret;
20539                         return false;
20540                     }
20541                 }
20542                 
20543                 return true;
20544             });
20545         }
20546         return record;
20547     }
20548     
20549     
20550     
20551     
20552 });/*
20553  * Based on:
20554  * Ext JS Library 1.1.1
20555  * Copyright(c) 2006-2007, Ext JS, LLC.
20556  *
20557  * Originally Released Under LGPL - original licence link has changed is not relivant.
20558  *
20559  * Fork - LGPL
20560  * <script type="text/javascript">
20561  */
20562 /**
20563  * @class Roo.form.Checkbox
20564  * @extends Roo.form.Field
20565  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20566  * @constructor
20567  * Creates a new Checkbox
20568  * @param {Object} config Configuration options
20569  */
20570 Roo.form.Checkbox = function(config){
20571     Roo.form.Checkbox.superclass.constructor.call(this, config);
20572     this.addEvents({
20573         /**
20574          * @event check
20575          * Fires when the checkbox is checked or unchecked.
20576              * @param {Roo.form.Checkbox} this This checkbox
20577              * @param {Boolean} checked The new checked value
20578              */
20579         check : true
20580     });
20581 };
20582
20583 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20584     /**
20585      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20586      */
20587     focusClass : undefined,
20588     /**
20589      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20590      */
20591     fieldClass: "x-form-field",
20592     /**
20593      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20594      */
20595     checked: false,
20596     /**
20597      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20598      * {tag: "input", type: "checkbox", autocomplete: "off"})
20599      */
20600     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20601     /**
20602      * @cfg {String} boxLabel The text that appears beside the checkbox
20603      */
20604     boxLabel : "",
20605     /**
20606      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20607      */  
20608     inputValue : '1',
20609     /**
20610      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20611      */
20612      valueOff: '0', // value when not checked..
20613
20614     actionMode : 'viewEl', 
20615     //
20616     // private
20617     itemCls : 'x-menu-check-item x-form-item',
20618     groupClass : 'x-menu-group-item',
20619     inputType : 'hidden',
20620     
20621     
20622     inSetChecked: false, // check that we are not calling self...
20623     
20624     inputElement: false, // real input element?
20625     basedOn: false, // ????
20626     
20627     isFormField: true, // not sure where this is needed!!!!
20628
20629     onResize : function(){
20630         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20631         if(!this.boxLabel){
20632             this.el.alignTo(this.wrap, 'c-c');
20633         }
20634     },
20635
20636     initEvents : function(){
20637         Roo.form.Checkbox.superclass.initEvents.call(this);
20638         this.el.on("click", this.onClick,  this);
20639         this.el.on("change", this.onClick,  this);
20640     },
20641
20642
20643     getResizeEl : function(){
20644         return this.wrap;
20645     },
20646
20647     getPositionEl : function(){
20648         return this.wrap;
20649     },
20650
20651     // private
20652     onRender : function(ct, position){
20653         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20654         /*
20655         if(this.inputValue !== undefined){
20656             this.el.dom.value = this.inputValue;
20657         }
20658         */
20659         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20660         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20661         var viewEl = this.wrap.createChild({ 
20662             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20663         this.viewEl = viewEl;   
20664         this.wrap.on('click', this.onClick,  this); 
20665         
20666         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20667         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20668         
20669         
20670         
20671         if(this.boxLabel){
20672             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20673         //    viewEl.on('click', this.onClick,  this); 
20674         }
20675         //if(this.checked){
20676             this.setChecked(this.checked);
20677         //}else{
20678             //this.checked = this.el.dom;
20679         //}
20680
20681     },
20682
20683     // private
20684     initValue : Roo.emptyFn,
20685
20686     /**
20687      * Returns the checked state of the checkbox.
20688      * @return {Boolean} True if checked, else false
20689      */
20690     getValue : function(){
20691         if(this.el){
20692             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20693         }
20694         return this.valueOff;
20695         
20696     },
20697
20698         // private
20699     onClick : function(){ 
20700         if (this.disabled) {
20701             return;
20702         }
20703         this.setChecked(!this.checked);
20704
20705         //if(this.el.dom.checked != this.checked){
20706         //    this.setValue(this.el.dom.checked);
20707        // }
20708     },
20709
20710     /**
20711      * Sets the checked state of the checkbox.
20712      * On is always based on a string comparison between inputValue and the param.
20713      * @param {Boolean/String} value - the value to set 
20714      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20715      */
20716     setValue : function(v,suppressEvent){
20717         
20718         
20719         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20720         //if(this.el && this.el.dom){
20721         //    this.el.dom.checked = this.checked;
20722         //    this.el.dom.defaultChecked = this.checked;
20723         //}
20724         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20725         //this.fireEvent("check", this, this.checked);
20726     },
20727     // private..
20728     setChecked : function(state,suppressEvent)
20729     {
20730         if (this.inSetChecked) {
20731             this.checked = state;
20732             return;
20733         }
20734         
20735     
20736         if(this.wrap){
20737             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20738         }
20739         this.checked = state;
20740         if(suppressEvent !== true){
20741             this.fireEvent('check', this, state);
20742         }
20743         this.inSetChecked = true;
20744         this.el.dom.value = state ? this.inputValue : this.valueOff;
20745         this.inSetChecked = false;
20746         
20747     },
20748     // handle setting of hidden value by some other method!!?!?
20749     setFromHidden: function()
20750     {
20751         if(!this.el){
20752             return;
20753         }
20754         //console.log("SET FROM HIDDEN");
20755         //alert('setFrom hidden');
20756         this.setValue(this.el.dom.value);
20757     },
20758     
20759     onDestroy : function()
20760     {
20761         if(this.viewEl){
20762             Roo.get(this.viewEl).remove();
20763         }
20764          
20765         Roo.form.Checkbox.superclass.onDestroy.call(this);
20766     },
20767     
20768     setBoxLabel : function(str)
20769     {
20770         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20771     }
20772
20773 });/*
20774  * Based on:
20775  * Ext JS Library 1.1.1
20776  * Copyright(c) 2006-2007, Ext JS, LLC.
20777  *
20778  * Originally Released Under LGPL - original licence link has changed is not relivant.
20779  *
20780  * Fork - LGPL
20781  * <script type="text/javascript">
20782  */
20783  
20784 /**
20785  * @class Roo.form.Radio
20786  * @extends Roo.form.Checkbox
20787  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20788  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20789  * @constructor
20790  * Creates a new Radio
20791  * @param {Object} config Configuration options
20792  */
20793 Roo.form.Radio = function(){
20794     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20795 };
20796 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20797     inputType: 'radio',
20798
20799     /**
20800      * If this radio is part of a group, it will return the selected value
20801      * @return {String}
20802      */
20803     getGroupValue : function(){
20804         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20805     },
20806     
20807     
20808     onRender : function(ct, position){
20809         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20810         
20811         if(this.inputValue !== undefined){
20812             this.el.dom.value = this.inputValue;
20813         }
20814          
20815         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20816         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20817         //var viewEl = this.wrap.createChild({ 
20818         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20819         //this.viewEl = viewEl;   
20820         //this.wrap.on('click', this.onClick,  this); 
20821         
20822         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20823         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20824         
20825         
20826         
20827         if(this.boxLabel){
20828             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20829         //    viewEl.on('click', this.onClick,  this); 
20830         }
20831          if(this.checked){
20832             this.el.dom.checked =   'checked' ;
20833         }
20834          
20835     } 
20836     
20837     
20838 });//<script type="text/javascript">
20839
20840 /*
20841  * Based  Ext JS Library 1.1.1
20842  * Copyright(c) 2006-2007, Ext JS, LLC.
20843  * LGPL
20844  *
20845  */
20846  
20847 /**
20848  * @class Roo.HtmlEditorCore
20849  * @extends Roo.Component
20850  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20851  *
20852  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20853  */
20854
20855 Roo.HtmlEditorCore = function(config){
20856     
20857     
20858     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20859     
20860     
20861     this.addEvents({
20862         /**
20863          * @event initialize
20864          * Fires when the editor is fully initialized (including the iframe)
20865          * @param {Roo.HtmlEditorCore} this
20866          */
20867         initialize: true,
20868         /**
20869          * @event activate
20870          * Fires when the editor is first receives the focus. Any insertion must wait
20871          * until after this event.
20872          * @param {Roo.HtmlEditorCore} this
20873          */
20874         activate: true,
20875          /**
20876          * @event beforesync
20877          * Fires before the textarea is updated with content from the editor iframe. Return false
20878          * to cancel the sync.
20879          * @param {Roo.HtmlEditorCore} this
20880          * @param {String} html
20881          */
20882         beforesync: true,
20883          /**
20884          * @event beforepush
20885          * Fires before the iframe editor is updated with content from the textarea. Return false
20886          * to cancel the push.
20887          * @param {Roo.HtmlEditorCore} this
20888          * @param {String} html
20889          */
20890         beforepush: true,
20891          /**
20892          * @event sync
20893          * Fires when the textarea is updated with content from the editor iframe.
20894          * @param {Roo.HtmlEditorCore} this
20895          * @param {String} html
20896          */
20897         sync: true,
20898          /**
20899          * @event push
20900          * Fires when the iframe editor is updated with content from the textarea.
20901          * @param {Roo.HtmlEditorCore} this
20902          * @param {String} html
20903          */
20904         push: true,
20905         
20906         /**
20907          * @event editorevent
20908          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20909          * @param {Roo.HtmlEditorCore} this
20910          */
20911         editorevent: true
20912         
20913     });
20914     
20915     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20916     
20917     // defaults : white / black...
20918     this.applyBlacklists();
20919     
20920     
20921     
20922 };
20923
20924
20925 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20926
20927
20928      /**
20929      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20930      */
20931     
20932     owner : false,
20933     
20934      /**
20935      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20936      *                        Roo.resizable.
20937      */
20938     resizable : false,
20939      /**
20940      * @cfg {Number} height (in pixels)
20941      */   
20942     height: 300,
20943    /**
20944      * @cfg {Number} width (in pixels)
20945      */   
20946     width: 500,
20947     
20948     /**
20949      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20950      * 
20951      */
20952     stylesheets: false,
20953     
20954     // id of frame..
20955     frameId: false,
20956     
20957     // private properties
20958     validationEvent : false,
20959     deferHeight: true,
20960     initialized : false,
20961     activated : false,
20962     sourceEditMode : false,
20963     onFocus : Roo.emptyFn,
20964     iframePad:3,
20965     hideMode:'offsets',
20966     
20967     clearUp: true,
20968     
20969     // blacklist + whitelisted elements..
20970     black: false,
20971     white: false,
20972      
20973     bodyCls : '',
20974
20975     /**
20976      * Protected method that will not generally be called directly. It
20977      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20978      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20979      */
20980     getDocMarkup : function(){
20981         // body styles..
20982         var st = '';
20983         
20984         // inherit styels from page...?? 
20985         if (this.stylesheets === false) {
20986             
20987             Roo.get(document.head).select('style').each(function(node) {
20988                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20989             });
20990             
20991             Roo.get(document.head).select('link').each(function(node) { 
20992                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20993             });
20994             
20995         } else if (!this.stylesheets.length) {
20996                 // simple..
20997                 st = '<style type="text/css">' +
20998                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20999                    '</style>';
21000         } else { 
21001             st = '<style type="text/css">' +
21002                     this.stylesheets +
21003                 '</style>';
21004         }
21005         
21006         st +=  '<style type="text/css">' +
21007             'IMG { cursor: pointer } ' +
21008         '</style>';
21009
21010         var cls = 'roo-htmleditor-body';
21011         
21012         if(this.bodyCls.length){
21013             cls += ' ' + this.bodyCls;
21014         }
21015         
21016         return '<html><head>' + st  +
21017             //<style type="text/css">' +
21018             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21019             //'</style>' +
21020             ' </head><body class="' +  cls + '"></body></html>';
21021     },
21022
21023     // private
21024     onRender : function(ct, position)
21025     {
21026         var _t = this;
21027         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21028         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21029         
21030         
21031         this.el.dom.style.border = '0 none';
21032         this.el.dom.setAttribute('tabIndex', -1);
21033         this.el.addClass('x-hidden hide');
21034         
21035         
21036         
21037         if(Roo.isIE){ // fix IE 1px bogus margin
21038             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21039         }
21040        
21041         
21042         this.frameId = Roo.id();
21043         
21044          
21045         
21046         var iframe = this.owner.wrap.createChild({
21047             tag: 'iframe',
21048             cls: 'form-control', // bootstrap..
21049             id: this.frameId,
21050             name: this.frameId,
21051             frameBorder : 'no',
21052             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21053         }, this.el
21054         );
21055         
21056         
21057         this.iframe = iframe.dom;
21058
21059          this.assignDocWin();
21060         
21061         this.doc.designMode = 'on';
21062        
21063         this.doc.open();
21064         this.doc.write(this.getDocMarkup());
21065         this.doc.close();
21066
21067         
21068         var task = { // must defer to wait for browser to be ready
21069             run : function(){
21070                 //console.log("run task?" + this.doc.readyState);
21071                 this.assignDocWin();
21072                 if(this.doc.body || this.doc.readyState == 'complete'){
21073                     try {
21074                         this.doc.designMode="on";
21075                     } catch (e) {
21076                         return;
21077                     }
21078                     Roo.TaskMgr.stop(task);
21079                     this.initEditor.defer(10, this);
21080                 }
21081             },
21082             interval : 10,
21083             duration: 10000,
21084             scope: this
21085         };
21086         Roo.TaskMgr.start(task);
21087
21088     },
21089
21090     // private
21091     onResize : function(w, h)
21092     {
21093          Roo.log('resize: ' +w + ',' + h );
21094         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21095         if(!this.iframe){
21096             return;
21097         }
21098         if(typeof w == 'number'){
21099             
21100             this.iframe.style.width = w + 'px';
21101         }
21102         if(typeof h == 'number'){
21103             
21104             this.iframe.style.height = h + 'px';
21105             if(this.doc){
21106                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21107             }
21108         }
21109         
21110     },
21111
21112     /**
21113      * Toggles the editor between standard and source edit mode.
21114      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21115      */
21116     toggleSourceEdit : function(sourceEditMode){
21117         
21118         this.sourceEditMode = sourceEditMode === true;
21119         
21120         if(this.sourceEditMode){
21121  
21122             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21123             
21124         }else{
21125             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21126             //this.iframe.className = '';
21127             this.deferFocus();
21128         }
21129         //this.setSize(this.owner.wrap.getSize());
21130         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21131     },
21132
21133     
21134   
21135
21136     /**
21137      * Protected method that will not generally be called directly. If you need/want
21138      * custom HTML cleanup, this is the method you should override.
21139      * @param {String} html The HTML to be cleaned
21140      * return {String} The cleaned HTML
21141      */
21142     cleanHtml : function(html){
21143         html = String(html);
21144         if(html.length > 5){
21145             if(Roo.isSafari){ // strip safari nonsense
21146                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21147             }
21148         }
21149         if(html == '&nbsp;'){
21150             html = '';
21151         }
21152         return html;
21153     },
21154
21155     /**
21156      * HTML Editor -> Textarea
21157      * Protected method that will not generally be called directly. Syncs the contents
21158      * of the editor iframe with the textarea.
21159      */
21160     syncValue : function(){
21161         if(this.initialized){
21162             var bd = (this.doc.body || this.doc.documentElement);
21163             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21164             var html = bd.innerHTML;
21165             if(Roo.isSafari){
21166                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21167                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21168                 if(m && m[1]){
21169                     html = '<div style="'+m[0]+'">' + html + '</div>';
21170                 }
21171             }
21172             html = this.cleanHtml(html);
21173             // fix up the special chars.. normaly like back quotes in word...
21174             // however we do not want to do this with chinese..
21175             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
21176                 
21177                 var cc = match.charCodeAt();
21178
21179                 // Get the character value, handling surrogate pairs
21180                 if (match.length == 2) {
21181                     // It's a surrogate pair, calculate the Unicode code point
21182                     var high = match.charCodeAt(0) - 0xD800;
21183                     var low  = match.charCodeAt(1) - 0xDC00;
21184                     cc = (high * 0x400) + low + 0x10000;
21185                 }  else if (
21186                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21187                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21188                     (cc >= 0xf900 && cc < 0xfb00 )
21189                 ) {
21190                         return match;
21191                 }  
21192          
21193                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
21194                 return "&#" + cc + ";";
21195                 
21196                 
21197             });
21198             
21199             
21200              
21201             if(this.owner.fireEvent('beforesync', this, html) !== false){
21202                 this.el.dom.value = html;
21203                 this.owner.fireEvent('sync', this, html);
21204             }
21205         }
21206     },
21207
21208     /**
21209      * Protected method that will not generally be called directly. Pushes the value of the textarea
21210      * into the iframe editor.
21211      */
21212     pushValue : function(){
21213         if(this.initialized){
21214             var v = this.el.dom.value.trim();
21215             
21216 //            if(v.length < 1){
21217 //                v = '&#160;';
21218 //            }
21219             
21220             if(this.owner.fireEvent('beforepush', this, v) !== false){
21221                 var d = (this.doc.body || this.doc.documentElement);
21222                 d.innerHTML = v;
21223                 this.cleanUpPaste();
21224                 this.el.dom.value = d.innerHTML;
21225                 this.owner.fireEvent('push', this, v);
21226             }
21227         }
21228     },
21229
21230     // private
21231     deferFocus : function(){
21232         this.focus.defer(10, this);
21233     },
21234
21235     // doc'ed in Field
21236     focus : function(){
21237         if(this.win && !this.sourceEditMode){
21238             this.win.focus();
21239         }else{
21240             this.el.focus();
21241         }
21242     },
21243     
21244     assignDocWin: function()
21245     {
21246         var iframe = this.iframe;
21247         
21248          if(Roo.isIE){
21249             this.doc = iframe.contentWindow.document;
21250             this.win = iframe.contentWindow;
21251         } else {
21252 //            if (!Roo.get(this.frameId)) {
21253 //                return;
21254 //            }
21255 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21256 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21257             
21258             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21259                 return;
21260             }
21261             
21262             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21263             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21264         }
21265     },
21266     
21267     // private
21268     initEditor : function(){
21269         //console.log("INIT EDITOR");
21270         this.assignDocWin();
21271         
21272         
21273         
21274         this.doc.designMode="on";
21275         this.doc.open();
21276         this.doc.write(this.getDocMarkup());
21277         this.doc.close();
21278         
21279         var dbody = (this.doc.body || this.doc.documentElement);
21280         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21281         // this copies styles from the containing element into thsi one..
21282         // not sure why we need all of this..
21283         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21284         
21285         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21286         //ss['background-attachment'] = 'fixed'; // w3c
21287         dbody.bgProperties = 'fixed'; // ie
21288         //Roo.DomHelper.applyStyles(dbody, ss);
21289         Roo.EventManager.on(this.doc, {
21290             //'mousedown': this.onEditorEvent,
21291             'mouseup': this.onEditorEvent,
21292             'dblclick': this.onEditorEvent,
21293             'click': this.onEditorEvent,
21294             'keyup': this.onEditorEvent,
21295             buffer:100,
21296             scope: this
21297         });
21298         if(Roo.isGecko){
21299             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21300         }
21301         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21302             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21303         }
21304         this.initialized = true;
21305
21306         this.owner.fireEvent('initialize', this);
21307         this.pushValue();
21308     },
21309
21310     // private
21311     onDestroy : function(){
21312         
21313         
21314         
21315         if(this.rendered){
21316             
21317             //for (var i =0; i < this.toolbars.length;i++) {
21318             //    // fixme - ask toolbars for heights?
21319             //    this.toolbars[i].onDestroy();
21320            // }
21321             
21322             //this.wrap.dom.innerHTML = '';
21323             //this.wrap.remove();
21324         }
21325     },
21326
21327     // private
21328     onFirstFocus : function(){
21329         
21330         this.assignDocWin();
21331         
21332         
21333         this.activated = true;
21334          
21335     
21336         if(Roo.isGecko){ // prevent silly gecko errors
21337             this.win.focus();
21338             var s = this.win.getSelection();
21339             if(!s.focusNode || s.focusNode.nodeType != 3){
21340                 var r = s.getRangeAt(0);
21341                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21342                 r.collapse(true);
21343                 this.deferFocus();
21344             }
21345             try{
21346                 this.execCmd('useCSS', true);
21347                 this.execCmd('styleWithCSS', false);
21348             }catch(e){}
21349         }
21350         this.owner.fireEvent('activate', this);
21351     },
21352
21353     // private
21354     adjustFont: function(btn){
21355         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21356         //if(Roo.isSafari){ // safari
21357         //    adjust *= 2;
21358        // }
21359         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21360         if(Roo.isSafari){ // safari
21361             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21362             v =  (v < 10) ? 10 : v;
21363             v =  (v > 48) ? 48 : v;
21364             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21365             
21366         }
21367         
21368         
21369         v = Math.max(1, v+adjust);
21370         
21371         this.execCmd('FontSize', v  );
21372     },
21373
21374     onEditorEvent : function(e)
21375     {
21376         this.owner.fireEvent('editorevent', this, e);
21377       //  this.updateToolbar();
21378         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21379     },
21380
21381     insertTag : function(tg)
21382     {
21383         // could be a bit smarter... -> wrap the current selected tRoo..
21384         if (tg.toLowerCase() == 'span' ||
21385             tg.toLowerCase() == 'code' ||
21386             tg.toLowerCase() == 'sup' ||
21387             tg.toLowerCase() == 'sub' 
21388             ) {
21389             
21390             range = this.createRange(this.getSelection());
21391             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21392             wrappingNode.appendChild(range.extractContents());
21393             range.insertNode(wrappingNode);
21394
21395             return;
21396             
21397             
21398             
21399         }
21400         this.execCmd("formatblock",   tg);
21401         
21402     },
21403     
21404     insertText : function(txt)
21405     {
21406         
21407         
21408         var range = this.createRange();
21409         range.deleteContents();
21410                //alert(Sender.getAttribute('label'));
21411                
21412         range.insertNode(this.doc.createTextNode(txt));
21413     } ,
21414     
21415      
21416
21417     /**
21418      * Executes a Midas editor command on the editor document and performs necessary focus and
21419      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21420      * @param {String} cmd The Midas command
21421      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21422      */
21423     relayCmd : function(cmd, value){
21424         this.win.focus();
21425         this.execCmd(cmd, value);
21426         this.owner.fireEvent('editorevent', this);
21427         //this.updateToolbar();
21428         this.owner.deferFocus();
21429     },
21430
21431     /**
21432      * Executes a Midas editor command directly on the editor document.
21433      * For visual commands, you should use {@link #relayCmd} instead.
21434      * <b>This should only be called after the editor is initialized.</b>
21435      * @param {String} cmd The Midas command
21436      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21437      */
21438     execCmd : function(cmd, value){
21439         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21440         this.syncValue();
21441     },
21442  
21443  
21444    
21445     /**
21446      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21447      * to insert tRoo.
21448      * @param {String} text | dom node.. 
21449      */
21450     insertAtCursor : function(text)
21451     {
21452         
21453         if(!this.activated){
21454             return;
21455         }
21456         /*
21457         if(Roo.isIE){
21458             this.win.focus();
21459             var r = this.doc.selection.createRange();
21460             if(r){
21461                 r.collapse(true);
21462                 r.pasteHTML(text);
21463                 this.syncValue();
21464                 this.deferFocus();
21465             
21466             }
21467             return;
21468         }
21469         */
21470         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21471             this.win.focus();
21472             
21473             
21474             // from jquery ui (MIT licenced)
21475             var range, node;
21476             var win = this.win;
21477             
21478             if (win.getSelection && win.getSelection().getRangeAt) {
21479                 range = win.getSelection().getRangeAt(0);
21480                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21481                 range.insertNode(node);
21482             } else if (win.document.selection && win.document.selection.createRange) {
21483                 // no firefox support
21484                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21485                 win.document.selection.createRange().pasteHTML(txt);
21486             } else {
21487                 // no firefox support
21488                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21489                 this.execCmd('InsertHTML', txt);
21490             } 
21491             
21492             this.syncValue();
21493             
21494             this.deferFocus();
21495         }
21496     },
21497  // private
21498     mozKeyPress : function(e){
21499         if(e.ctrlKey){
21500             var c = e.getCharCode(), cmd;
21501           
21502             if(c > 0){
21503                 c = String.fromCharCode(c).toLowerCase();
21504                 switch(c){
21505                     case 'b':
21506                         cmd = 'bold';
21507                         break;
21508                     case 'i':
21509                         cmd = 'italic';
21510                         break;
21511                     
21512                     case 'u':
21513                         cmd = 'underline';
21514                         break;
21515                     
21516                     case 'v':
21517                         this.cleanUpPaste.defer(100, this);
21518                         return;
21519                         
21520                 }
21521                 if(cmd){
21522                     this.win.focus();
21523                     this.execCmd(cmd);
21524                     this.deferFocus();
21525                     e.preventDefault();
21526                 }
21527                 
21528             }
21529         }
21530     },
21531
21532     // private
21533     fixKeys : function(){ // load time branching for fastest keydown performance
21534         if(Roo.isIE){
21535             return function(e){
21536                 var k = e.getKey(), r;
21537                 if(k == e.TAB){
21538                     e.stopEvent();
21539                     r = this.doc.selection.createRange();
21540                     if(r){
21541                         r.collapse(true);
21542                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21543                         this.deferFocus();
21544                     }
21545                     return;
21546                 }
21547                 
21548                 if(k == e.ENTER){
21549                     r = this.doc.selection.createRange();
21550                     if(r){
21551                         var target = r.parentElement();
21552                         if(!target || target.tagName.toLowerCase() != 'li'){
21553                             e.stopEvent();
21554                             r.pasteHTML('<br />');
21555                             r.collapse(false);
21556                             r.select();
21557                         }
21558                     }
21559                 }
21560                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21561                     this.cleanUpPaste.defer(100, this);
21562                     return;
21563                 }
21564                 
21565                 
21566             };
21567         }else if(Roo.isOpera){
21568             return function(e){
21569                 var k = e.getKey();
21570                 if(k == e.TAB){
21571                     e.stopEvent();
21572                     this.win.focus();
21573                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21574                     this.deferFocus();
21575                 }
21576                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21577                     this.cleanUpPaste.defer(100, this);
21578                     return;
21579                 }
21580                 
21581             };
21582         }else if(Roo.isSafari){
21583             return function(e){
21584                 var k = e.getKey();
21585                 
21586                 if(k == e.TAB){
21587                     e.stopEvent();
21588                     this.execCmd('InsertText','\t');
21589                     this.deferFocus();
21590                     return;
21591                 }
21592                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21593                     this.cleanUpPaste.defer(100, this);
21594                     return;
21595                 }
21596                 
21597              };
21598         }
21599     }(),
21600     
21601     getAllAncestors: function()
21602     {
21603         var p = this.getSelectedNode();
21604         var a = [];
21605         if (!p) {
21606             a.push(p); // push blank onto stack..
21607             p = this.getParentElement();
21608         }
21609         
21610         
21611         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21612             a.push(p);
21613             p = p.parentNode;
21614         }
21615         a.push(this.doc.body);
21616         return a;
21617     },
21618     lastSel : false,
21619     lastSelNode : false,
21620     
21621     
21622     getSelection : function() 
21623     {
21624         this.assignDocWin();
21625         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21626     },
21627     
21628     getSelectedNode: function() 
21629     {
21630         // this may only work on Gecko!!!
21631         
21632         // should we cache this!!!!
21633         
21634         
21635         
21636          
21637         var range = this.createRange(this.getSelection()).cloneRange();
21638         
21639         if (Roo.isIE) {
21640             var parent = range.parentElement();
21641             while (true) {
21642                 var testRange = range.duplicate();
21643                 testRange.moveToElementText(parent);
21644                 if (testRange.inRange(range)) {
21645                     break;
21646                 }
21647                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21648                     break;
21649                 }
21650                 parent = parent.parentElement;
21651             }
21652             return parent;
21653         }
21654         
21655         // is ancestor a text element.
21656         var ac =  range.commonAncestorContainer;
21657         if (ac.nodeType == 3) {
21658             ac = ac.parentNode;
21659         }
21660         
21661         var ar = ac.childNodes;
21662          
21663         var nodes = [];
21664         var other_nodes = [];
21665         var has_other_nodes = false;
21666         for (var i=0;i<ar.length;i++) {
21667             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21668                 continue;
21669             }
21670             // fullly contained node.
21671             
21672             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21673                 nodes.push(ar[i]);
21674                 continue;
21675             }
21676             
21677             // probably selected..
21678             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21679                 other_nodes.push(ar[i]);
21680                 continue;
21681             }
21682             // outer..
21683             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21684                 continue;
21685             }
21686             
21687             
21688             has_other_nodes = true;
21689         }
21690         if (!nodes.length && other_nodes.length) {
21691             nodes= other_nodes;
21692         }
21693         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21694             return false;
21695         }
21696         
21697         return nodes[0];
21698     },
21699     createRange: function(sel)
21700     {
21701         // this has strange effects when using with 
21702         // top toolbar - not sure if it's a great idea.
21703         //this.editor.contentWindow.focus();
21704         if (typeof sel != "undefined") {
21705             try {
21706                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21707             } catch(e) {
21708                 return this.doc.createRange();
21709             }
21710         } else {
21711             return this.doc.createRange();
21712         }
21713     },
21714     getParentElement: function()
21715     {
21716         
21717         this.assignDocWin();
21718         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21719         
21720         var range = this.createRange(sel);
21721          
21722         try {
21723             var p = range.commonAncestorContainer;
21724             while (p.nodeType == 3) { // text node
21725                 p = p.parentNode;
21726             }
21727             return p;
21728         } catch (e) {
21729             return null;
21730         }
21731     
21732     },
21733     /***
21734      *
21735      * Range intersection.. the hard stuff...
21736      *  '-1' = before
21737      *  '0' = hits..
21738      *  '1' = after.
21739      *         [ -- selected range --- ]
21740      *   [fail]                        [fail]
21741      *
21742      *    basically..
21743      *      if end is before start or  hits it. fail.
21744      *      if start is after end or hits it fail.
21745      *
21746      *   if either hits (but other is outside. - then it's not 
21747      *   
21748      *    
21749      **/
21750     
21751     
21752     // @see http://www.thismuchiknow.co.uk/?p=64.
21753     rangeIntersectsNode : function(range, node)
21754     {
21755         var nodeRange = node.ownerDocument.createRange();
21756         try {
21757             nodeRange.selectNode(node);
21758         } catch (e) {
21759             nodeRange.selectNodeContents(node);
21760         }
21761     
21762         var rangeStartRange = range.cloneRange();
21763         rangeStartRange.collapse(true);
21764     
21765         var rangeEndRange = range.cloneRange();
21766         rangeEndRange.collapse(false);
21767     
21768         var nodeStartRange = nodeRange.cloneRange();
21769         nodeStartRange.collapse(true);
21770     
21771         var nodeEndRange = nodeRange.cloneRange();
21772         nodeEndRange.collapse(false);
21773     
21774         return rangeStartRange.compareBoundaryPoints(
21775                  Range.START_TO_START, nodeEndRange) == -1 &&
21776                rangeEndRange.compareBoundaryPoints(
21777                  Range.START_TO_START, nodeStartRange) == 1;
21778         
21779          
21780     },
21781     rangeCompareNode : function(range, node)
21782     {
21783         var nodeRange = node.ownerDocument.createRange();
21784         try {
21785             nodeRange.selectNode(node);
21786         } catch (e) {
21787             nodeRange.selectNodeContents(node);
21788         }
21789         
21790         
21791         range.collapse(true);
21792     
21793         nodeRange.collapse(true);
21794      
21795         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21796         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21797          
21798         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21799         
21800         var nodeIsBefore   =  ss == 1;
21801         var nodeIsAfter    = ee == -1;
21802         
21803         if (nodeIsBefore && nodeIsAfter) {
21804             return 0; // outer
21805         }
21806         if (!nodeIsBefore && nodeIsAfter) {
21807             return 1; //right trailed.
21808         }
21809         
21810         if (nodeIsBefore && !nodeIsAfter) {
21811             return 2;  // left trailed.
21812         }
21813         // fully contined.
21814         return 3;
21815     },
21816
21817     // private? - in a new class?
21818     cleanUpPaste :  function()
21819     {
21820         // cleans up the whole document..
21821         Roo.log('cleanuppaste');
21822         
21823         this.cleanUpChildren(this.doc.body);
21824         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21825         if (clean != this.doc.body.innerHTML) {
21826             this.doc.body.innerHTML = clean;
21827         }
21828         
21829     },
21830     
21831     cleanWordChars : function(input) {// change the chars to hex code
21832         var he = Roo.HtmlEditorCore;
21833         
21834         var output = input;
21835         Roo.each(he.swapCodes, function(sw) { 
21836             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21837             
21838             output = output.replace(swapper, sw[1]);
21839         });
21840         
21841         return output;
21842     },
21843     
21844     
21845     cleanUpChildren : function (n)
21846     {
21847         if (!n.childNodes.length) {
21848             return;
21849         }
21850         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21851            this.cleanUpChild(n.childNodes[i]);
21852         }
21853     },
21854     
21855     
21856         
21857     
21858     cleanUpChild : function (node)
21859     {
21860         var ed = this;
21861         //console.log(node);
21862         if (node.nodeName == "#text") {
21863             // clean up silly Windows -- stuff?
21864             return; 
21865         }
21866         if (node.nodeName == "#comment") {
21867             node.parentNode.removeChild(node);
21868             // clean up silly Windows -- stuff?
21869             return; 
21870         }
21871         var lcname = node.tagName.toLowerCase();
21872         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21873         // whitelist of tags..
21874         
21875         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21876             // remove node.
21877             node.parentNode.removeChild(node);
21878             return;
21879             
21880         }
21881         
21882         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21883         
21884         // spans with no attributes - just remove them..
21885         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21886             remove_keep_children = true;
21887         }
21888         
21889         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21890         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21891         
21892         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21893         //    remove_keep_children = true;
21894         //}
21895         
21896         if (remove_keep_children) {
21897             this.cleanUpChildren(node);
21898             // inserts everything just before this node...
21899             while (node.childNodes.length) {
21900                 var cn = node.childNodes[0];
21901                 node.removeChild(cn);
21902                 node.parentNode.insertBefore(cn, node);
21903             }
21904             node.parentNode.removeChild(node);
21905             return;
21906         }
21907         
21908         if (!node.attributes || !node.attributes.length) {
21909             
21910           
21911             
21912             
21913             this.cleanUpChildren(node);
21914             return;
21915         }
21916         
21917         function cleanAttr(n,v)
21918         {
21919             
21920             if (v.match(/^\./) || v.match(/^\//)) {
21921                 return;
21922             }
21923             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21924                 return;
21925             }
21926             if (v.match(/^#/)) {
21927                 return;
21928             }
21929 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21930             node.removeAttribute(n);
21931             
21932         }
21933         
21934         var cwhite = this.cwhite;
21935         var cblack = this.cblack;
21936             
21937         function cleanStyle(n,v)
21938         {
21939             if (v.match(/expression/)) { //XSS?? should we even bother..
21940                 node.removeAttribute(n);
21941                 return;
21942             }
21943             
21944             var parts = v.split(/;/);
21945             var clean = [];
21946             
21947             Roo.each(parts, function(p) {
21948                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21949                 if (!p.length) {
21950                     return true;
21951                 }
21952                 var l = p.split(':').shift().replace(/\s+/g,'');
21953                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21954                 
21955                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21956 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21957                     //node.removeAttribute(n);
21958                     return true;
21959                 }
21960                 //Roo.log()
21961                 // only allow 'c whitelisted system attributes'
21962                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21963 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21964                     //node.removeAttribute(n);
21965                     return true;
21966                 }
21967                 
21968                 
21969                  
21970                 
21971                 clean.push(p);
21972                 return true;
21973             });
21974             if (clean.length) { 
21975                 node.setAttribute(n, clean.join(';'));
21976             } else {
21977                 node.removeAttribute(n);
21978             }
21979             
21980         }
21981         
21982         
21983         for (var i = node.attributes.length-1; i > -1 ; i--) {
21984             var a = node.attributes[i];
21985             //console.log(a);
21986             
21987             if (a.name.toLowerCase().substr(0,2)=='on')  {
21988                 node.removeAttribute(a.name);
21989                 continue;
21990             }
21991             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21992                 node.removeAttribute(a.name);
21993                 continue;
21994             }
21995             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21996                 cleanAttr(a.name,a.value); // fixme..
21997                 continue;
21998             }
21999             if (a.name == 'style') {
22000                 cleanStyle(a.name,a.value);
22001                 continue;
22002             }
22003             /// clean up MS crap..
22004             // tecnically this should be a list of valid class'es..
22005             
22006             
22007             if (a.name == 'class') {
22008                 if (a.value.match(/^Mso/)) {
22009                     node.removeAttribute('class');
22010                 }
22011                 
22012                 if (a.value.match(/^body$/)) {
22013                     node.removeAttribute('class');
22014                 }
22015                 continue;
22016             }
22017             
22018             // style cleanup!?
22019             // class cleanup?
22020             
22021         }
22022         
22023         
22024         this.cleanUpChildren(node);
22025         
22026         
22027     },
22028     
22029     /**
22030      * Clean up MS wordisms...
22031      */
22032     cleanWord : function(node)
22033     {
22034         if (!node) {
22035             this.cleanWord(this.doc.body);
22036             return;
22037         }
22038         
22039         if(
22040                 node.nodeName == 'SPAN' &&
22041                 !node.hasAttributes() &&
22042                 node.childNodes.length == 1 &&
22043                 node.firstChild.nodeName == "#text"  
22044         ) {
22045             var textNode = node.firstChild;
22046             node.removeChild(textNode);
22047             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22048                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
22049             }
22050             node.parentNode.insertBefore(textNode, node);
22051             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22052                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
22053             }
22054             node.parentNode.removeChild(node);
22055         }
22056         
22057         if (node.nodeName == "#text") {
22058             // clean up silly Windows -- stuff?
22059             return; 
22060         }
22061         if (node.nodeName == "#comment") {
22062             node.parentNode.removeChild(node);
22063             // clean up silly Windows -- stuff?
22064             return; 
22065         }
22066         
22067         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22068             node.parentNode.removeChild(node);
22069             return;
22070         }
22071         //Roo.log(node.tagName);
22072         // remove - but keep children..
22073         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
22074             //Roo.log('-- removed');
22075             while (node.childNodes.length) {
22076                 var cn = node.childNodes[0];
22077                 node.removeChild(cn);
22078                 node.parentNode.insertBefore(cn, node);
22079                 // move node to parent - and clean it..
22080                 this.cleanWord(cn);
22081             }
22082             node.parentNode.removeChild(node);
22083             /// no need to iterate chidlren = it's got none..
22084             //this.iterateChildren(node, this.cleanWord);
22085             return;
22086         }
22087         // clean styles
22088         if (node.className.length) {
22089             
22090             var cn = node.className.split(/\W+/);
22091             var cna = [];
22092             Roo.each(cn, function(cls) {
22093                 if (cls.match(/Mso[a-zA-Z]+/)) {
22094                     return;
22095                 }
22096                 cna.push(cls);
22097             });
22098             node.className = cna.length ? cna.join(' ') : '';
22099             if (!cna.length) {
22100                 node.removeAttribute("class");
22101             }
22102         }
22103         
22104         if (node.hasAttribute("lang")) {
22105             node.removeAttribute("lang");
22106         }
22107         
22108         if (node.hasAttribute("style")) {
22109             
22110             var styles = node.getAttribute("style").split(";");
22111             var nstyle = [];
22112             Roo.each(styles, function(s) {
22113                 if (!s.match(/:/)) {
22114                     return;
22115                 }
22116                 var kv = s.split(":");
22117                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22118                     return;
22119                 }
22120                 // what ever is left... we allow.
22121                 nstyle.push(s);
22122             });
22123             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22124             if (!nstyle.length) {
22125                 node.removeAttribute('style');
22126             }
22127         }
22128         this.iterateChildren(node, this.cleanWord);
22129         
22130         
22131         
22132     },
22133     /**
22134      * iterateChildren of a Node, calling fn each time, using this as the scole..
22135      * @param {DomNode} node node to iterate children of.
22136      * @param {Function} fn method of this class to call on each item.
22137      */
22138     iterateChildren : function(node, fn)
22139     {
22140         if (!node.childNodes.length) {
22141                 return;
22142         }
22143         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22144            fn.call(this, node.childNodes[i])
22145         }
22146     },
22147     
22148     
22149     /**
22150      * cleanTableWidths.
22151      *
22152      * Quite often pasting from word etc.. results in tables with column and widths.
22153      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22154      *
22155      */
22156     cleanTableWidths : function(node)
22157     {
22158          
22159          
22160         if (!node) {
22161             this.cleanTableWidths(this.doc.body);
22162             return;
22163         }
22164         
22165         // ignore list...
22166         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22167             return; 
22168         }
22169         Roo.log(node.tagName);
22170         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22171             this.iterateChildren(node, this.cleanTableWidths);
22172             return;
22173         }
22174         if (node.hasAttribute('width')) {
22175             node.removeAttribute('width');
22176         }
22177         
22178          
22179         if (node.hasAttribute("style")) {
22180             // pretty basic...
22181             
22182             var styles = node.getAttribute("style").split(";");
22183             var nstyle = [];
22184             Roo.each(styles, function(s) {
22185                 if (!s.match(/:/)) {
22186                     return;
22187                 }
22188                 var kv = s.split(":");
22189                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22190                     return;
22191                 }
22192                 // what ever is left... we allow.
22193                 nstyle.push(s);
22194             });
22195             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22196             if (!nstyle.length) {
22197                 node.removeAttribute('style');
22198             }
22199         }
22200         
22201         this.iterateChildren(node, this.cleanTableWidths);
22202         
22203         
22204     },
22205     
22206     
22207     
22208     
22209     domToHTML : function(currentElement, depth, nopadtext) {
22210         
22211         depth = depth || 0;
22212         nopadtext = nopadtext || false;
22213     
22214         if (!currentElement) {
22215             return this.domToHTML(this.doc.body);
22216         }
22217         
22218         //Roo.log(currentElement);
22219         var j;
22220         var allText = false;
22221         var nodeName = currentElement.nodeName;
22222         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22223         
22224         if  (nodeName == '#text') {
22225             
22226             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22227         }
22228         
22229         
22230         var ret = '';
22231         if (nodeName != 'BODY') {
22232              
22233             var i = 0;
22234             // Prints the node tagName, such as <A>, <IMG>, etc
22235             if (tagName) {
22236                 var attr = [];
22237                 for(i = 0; i < currentElement.attributes.length;i++) {
22238                     // quoting?
22239                     var aname = currentElement.attributes.item(i).name;
22240                     if (!currentElement.attributes.item(i).value.length) {
22241                         continue;
22242                     }
22243                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22244                 }
22245                 
22246                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22247             } 
22248             else {
22249                 
22250                 // eack
22251             }
22252         } else {
22253             tagName = false;
22254         }
22255         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22256             return ret;
22257         }
22258         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22259             nopadtext = true;
22260         }
22261         
22262         
22263         // Traverse the tree
22264         i = 0;
22265         var currentElementChild = currentElement.childNodes.item(i);
22266         var allText = true;
22267         var innerHTML  = '';
22268         lastnode = '';
22269         while (currentElementChild) {
22270             // Formatting code (indent the tree so it looks nice on the screen)
22271             var nopad = nopadtext;
22272             if (lastnode == 'SPAN') {
22273                 nopad  = true;
22274             }
22275             // text
22276             if  (currentElementChild.nodeName == '#text') {
22277                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22278                 toadd = nopadtext ? toadd : toadd.trim();
22279                 if (!nopad && toadd.length > 80) {
22280                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22281                 }
22282                 innerHTML  += toadd;
22283                 
22284                 i++;
22285                 currentElementChild = currentElement.childNodes.item(i);
22286                 lastNode = '';
22287                 continue;
22288             }
22289             allText = false;
22290             
22291             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22292                 
22293             // Recursively traverse the tree structure of the child node
22294             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22295             lastnode = currentElementChild.nodeName;
22296             i++;
22297             currentElementChild=currentElement.childNodes.item(i);
22298         }
22299         
22300         ret += innerHTML;
22301         
22302         if (!allText) {
22303                 // The remaining code is mostly for formatting the tree
22304             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22305         }
22306         
22307         
22308         if (tagName) {
22309             ret+= "</"+tagName+">";
22310         }
22311         return ret;
22312         
22313     },
22314         
22315     applyBlacklists : function()
22316     {
22317         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22318         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22319         
22320         this.white = [];
22321         this.black = [];
22322         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22323             if (b.indexOf(tag) > -1) {
22324                 return;
22325             }
22326             this.white.push(tag);
22327             
22328         }, this);
22329         
22330         Roo.each(w, function(tag) {
22331             if (b.indexOf(tag) > -1) {
22332                 return;
22333             }
22334             if (this.white.indexOf(tag) > -1) {
22335                 return;
22336             }
22337             this.white.push(tag);
22338             
22339         }, this);
22340         
22341         
22342         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22343             if (w.indexOf(tag) > -1) {
22344                 return;
22345             }
22346             this.black.push(tag);
22347             
22348         }, this);
22349         
22350         Roo.each(b, function(tag) {
22351             if (w.indexOf(tag) > -1) {
22352                 return;
22353             }
22354             if (this.black.indexOf(tag) > -1) {
22355                 return;
22356             }
22357             this.black.push(tag);
22358             
22359         }, this);
22360         
22361         
22362         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22363         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22364         
22365         this.cwhite = [];
22366         this.cblack = [];
22367         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22368             if (b.indexOf(tag) > -1) {
22369                 return;
22370             }
22371             this.cwhite.push(tag);
22372             
22373         }, this);
22374         
22375         Roo.each(w, function(tag) {
22376             if (b.indexOf(tag) > -1) {
22377                 return;
22378             }
22379             if (this.cwhite.indexOf(tag) > -1) {
22380                 return;
22381             }
22382             this.cwhite.push(tag);
22383             
22384         }, this);
22385         
22386         
22387         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22388             if (w.indexOf(tag) > -1) {
22389                 return;
22390             }
22391             this.cblack.push(tag);
22392             
22393         }, this);
22394         
22395         Roo.each(b, function(tag) {
22396             if (w.indexOf(tag) > -1) {
22397                 return;
22398             }
22399             if (this.cblack.indexOf(tag) > -1) {
22400                 return;
22401             }
22402             this.cblack.push(tag);
22403             
22404         }, this);
22405     },
22406     
22407     setStylesheets : function(stylesheets)
22408     {
22409         if(typeof(stylesheets) == 'string'){
22410             Roo.get(this.iframe.contentDocument.head).createChild({
22411                 tag : 'link',
22412                 rel : 'stylesheet',
22413                 type : 'text/css',
22414                 href : stylesheets
22415             });
22416             
22417             return;
22418         }
22419         var _this = this;
22420      
22421         Roo.each(stylesheets, function(s) {
22422             if(!s.length){
22423                 return;
22424             }
22425             
22426             Roo.get(_this.iframe.contentDocument.head).createChild({
22427                 tag : 'link',
22428                 rel : 'stylesheet',
22429                 type : 'text/css',
22430                 href : s
22431             });
22432         });
22433
22434         
22435     },
22436     
22437     removeStylesheets : function()
22438     {
22439         var _this = this;
22440         
22441         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22442             s.remove();
22443         });
22444     },
22445     
22446     setStyle : function(style)
22447     {
22448         Roo.get(this.iframe.contentDocument.head).createChild({
22449             tag : 'style',
22450             type : 'text/css',
22451             html : style
22452         });
22453
22454         return;
22455     }
22456     
22457     // hide stuff that is not compatible
22458     /**
22459      * @event blur
22460      * @hide
22461      */
22462     /**
22463      * @event change
22464      * @hide
22465      */
22466     /**
22467      * @event focus
22468      * @hide
22469      */
22470     /**
22471      * @event specialkey
22472      * @hide
22473      */
22474     /**
22475      * @cfg {String} fieldClass @hide
22476      */
22477     /**
22478      * @cfg {String} focusClass @hide
22479      */
22480     /**
22481      * @cfg {String} autoCreate @hide
22482      */
22483     /**
22484      * @cfg {String} inputType @hide
22485      */
22486     /**
22487      * @cfg {String} invalidClass @hide
22488      */
22489     /**
22490      * @cfg {String} invalidText @hide
22491      */
22492     /**
22493      * @cfg {String} msgFx @hide
22494      */
22495     /**
22496      * @cfg {String} validateOnBlur @hide
22497      */
22498 });
22499
22500 Roo.HtmlEditorCore.white = [
22501         'area', 'br', 'img', 'input', 'hr', 'wbr',
22502         
22503        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22504        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22505        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22506        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22507        'table',   'ul',         'xmp', 
22508        
22509        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22510       'thead',   'tr', 
22511      
22512       'dir', 'menu', 'ol', 'ul', 'dl',
22513        
22514       'embed',  'object'
22515 ];
22516
22517
22518 Roo.HtmlEditorCore.black = [
22519     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22520         'applet', // 
22521         'base',   'basefont', 'bgsound', 'blink',  'body', 
22522         'frame',  'frameset', 'head',    'html',   'ilayer', 
22523         'iframe', 'layer',  'link',     'meta',    'object',   
22524         'script', 'style' ,'title',  'xml' // clean later..
22525 ];
22526 Roo.HtmlEditorCore.clean = [
22527     'script', 'style', 'title', 'xml'
22528 ];
22529 Roo.HtmlEditorCore.remove = [
22530     'font'
22531 ];
22532 // attributes..
22533
22534 Roo.HtmlEditorCore.ablack = [
22535     'on'
22536 ];
22537     
22538 Roo.HtmlEditorCore.aclean = [ 
22539     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22540 ];
22541
22542 // protocols..
22543 Roo.HtmlEditorCore.pwhite= [
22544         'http',  'https',  'mailto'
22545 ];
22546
22547 // white listed style attributes.
22548 Roo.HtmlEditorCore.cwhite= [
22549       //  'text-align', /// default is to allow most things..
22550       
22551          
22552 //        'font-size'//??
22553 ];
22554
22555 // black listed style attributes.
22556 Roo.HtmlEditorCore.cblack= [
22557       //  'font-size' -- this can be set by the project 
22558 ];
22559
22560
22561 Roo.HtmlEditorCore.swapCodes   =[ 
22562     [    8211, "--" ], 
22563     [    8212, "--" ], 
22564     [    8216,  "'" ],  
22565     [    8217, "'" ],  
22566     [    8220, '"' ],  
22567     [    8221, '"' ],  
22568     [    8226, "*" ],  
22569     [    8230, "..." ]
22570 ]; 
22571
22572     //<script type="text/javascript">
22573
22574 /*
22575  * Ext JS Library 1.1.1
22576  * Copyright(c) 2006-2007, Ext JS, LLC.
22577  * Licence LGPL
22578  * 
22579  */
22580  
22581  
22582 Roo.form.HtmlEditor = function(config){
22583     
22584     
22585     
22586     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22587     
22588     if (!this.toolbars) {
22589         this.toolbars = [];
22590     }
22591     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22592     
22593     
22594 };
22595
22596 /**
22597  * @class Roo.form.HtmlEditor
22598  * @extends Roo.form.Field
22599  * Provides a lightweight HTML Editor component.
22600  *
22601  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22602  * 
22603  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22604  * supported by this editor.</b><br/><br/>
22605  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22606  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22607  */
22608 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22609     /**
22610      * @cfg {Boolean} clearUp
22611      */
22612     clearUp : true,
22613       /**
22614      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22615      */
22616     toolbars : false,
22617    
22618      /**
22619      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22620      *                        Roo.resizable.
22621      */
22622     resizable : false,
22623      /**
22624      * @cfg {Number} height (in pixels)
22625      */   
22626     height: 300,
22627    /**
22628      * @cfg {Number} width (in pixels)
22629      */   
22630     width: 500,
22631     
22632     /**
22633      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22634      * 
22635      */
22636     stylesheets: false,
22637     
22638     
22639      /**
22640      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22641      * 
22642      */
22643     cblack: false,
22644     /**
22645      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22646      * 
22647      */
22648     cwhite: false,
22649     
22650      /**
22651      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22652      * 
22653      */
22654     black: false,
22655     /**
22656      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22657      * 
22658      */
22659     white: false,
22660     
22661     // id of frame..
22662     frameId: false,
22663     
22664     // private properties
22665     validationEvent : false,
22666     deferHeight: true,
22667     initialized : false,
22668     activated : false,
22669     
22670     onFocus : Roo.emptyFn,
22671     iframePad:3,
22672     hideMode:'offsets',
22673     
22674     actionMode : 'container', // defaults to hiding it...
22675     
22676     defaultAutoCreate : { // modified by initCompnoent..
22677         tag: "textarea",
22678         style:"width:500px;height:300px;",
22679         autocomplete: "new-password"
22680     },
22681
22682     // private
22683     initComponent : function(){
22684         this.addEvents({
22685             /**
22686              * @event initialize
22687              * Fires when the editor is fully initialized (including the iframe)
22688              * @param {HtmlEditor} this
22689              */
22690             initialize: true,
22691             /**
22692              * @event activate
22693              * Fires when the editor is first receives the focus. Any insertion must wait
22694              * until after this event.
22695              * @param {HtmlEditor} this
22696              */
22697             activate: true,
22698              /**
22699              * @event beforesync
22700              * Fires before the textarea is updated with content from the editor iframe. Return false
22701              * to cancel the sync.
22702              * @param {HtmlEditor} this
22703              * @param {String} html
22704              */
22705             beforesync: true,
22706              /**
22707              * @event beforepush
22708              * Fires before the iframe editor is updated with content from the textarea. Return false
22709              * to cancel the push.
22710              * @param {HtmlEditor} this
22711              * @param {String} html
22712              */
22713             beforepush: true,
22714              /**
22715              * @event sync
22716              * Fires when the textarea is updated with content from the editor iframe.
22717              * @param {HtmlEditor} this
22718              * @param {String} html
22719              */
22720             sync: true,
22721              /**
22722              * @event push
22723              * Fires when the iframe editor is updated with content from the textarea.
22724              * @param {HtmlEditor} this
22725              * @param {String} html
22726              */
22727             push: true,
22728              /**
22729              * @event editmodechange
22730              * Fires when the editor switches edit modes
22731              * @param {HtmlEditor} this
22732              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22733              */
22734             editmodechange: true,
22735             /**
22736              * @event editorevent
22737              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22738              * @param {HtmlEditor} this
22739              */
22740             editorevent: true,
22741             /**
22742              * @event firstfocus
22743              * Fires when on first focus - needed by toolbars..
22744              * @param {HtmlEditor} this
22745              */
22746             firstfocus: true,
22747             /**
22748              * @event autosave
22749              * Auto save the htmlEditor value as a file into Events
22750              * @param {HtmlEditor} this
22751              */
22752             autosave: true,
22753             /**
22754              * @event savedpreview
22755              * preview the saved version of htmlEditor
22756              * @param {HtmlEditor} this
22757              */
22758             savedpreview: true,
22759             
22760             /**
22761             * @event stylesheetsclick
22762             * Fires when press the Sytlesheets button
22763             * @param {Roo.HtmlEditorCore} this
22764             */
22765             stylesheetsclick: true
22766         });
22767         this.defaultAutoCreate =  {
22768             tag: "textarea",
22769             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22770             autocomplete: "new-password"
22771         };
22772     },
22773
22774     /**
22775      * Protected method that will not generally be called directly. It
22776      * is called when the editor creates its toolbar. Override this method if you need to
22777      * add custom toolbar buttons.
22778      * @param {HtmlEditor} editor
22779      */
22780     createToolbar : function(editor){
22781         Roo.log("create toolbars");
22782         if (!editor.toolbars || !editor.toolbars.length) {
22783             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22784         }
22785         
22786         for (var i =0 ; i < editor.toolbars.length;i++) {
22787             editor.toolbars[i] = Roo.factory(
22788                     typeof(editor.toolbars[i]) == 'string' ?
22789                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22790                 Roo.form.HtmlEditor);
22791             editor.toolbars[i].init(editor);
22792         }
22793          
22794         
22795     },
22796
22797      
22798     // private
22799     onRender : function(ct, position)
22800     {
22801         var _t = this;
22802         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22803         
22804         this.wrap = this.el.wrap({
22805             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22806         });
22807         
22808         this.editorcore.onRender(ct, position);
22809          
22810         if (this.resizable) {
22811             this.resizeEl = new Roo.Resizable(this.wrap, {
22812                 pinned : true,
22813                 wrap: true,
22814                 dynamic : true,
22815                 minHeight : this.height,
22816                 height: this.height,
22817                 handles : this.resizable,
22818                 width: this.width,
22819                 listeners : {
22820                     resize : function(r, w, h) {
22821                         _t.onResize(w,h); // -something
22822                     }
22823                 }
22824             });
22825             
22826         }
22827         this.createToolbar(this);
22828        
22829         
22830         if(!this.width){
22831             this.setSize(this.wrap.getSize());
22832         }
22833         if (this.resizeEl) {
22834             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22835             // should trigger onReize..
22836         }
22837         
22838         this.keyNav = new Roo.KeyNav(this.el, {
22839             
22840             "tab" : function(e){
22841                 e.preventDefault();
22842                 
22843                 var value = this.getValue();
22844                 
22845                 var start = this.el.dom.selectionStart;
22846                 var end = this.el.dom.selectionEnd;
22847                 
22848                 if(!e.shiftKey){
22849                     
22850                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22851                     this.el.dom.setSelectionRange(end + 1, end + 1);
22852                     return;
22853                 }
22854                 
22855                 var f = value.substring(0, start).split("\t");
22856                 
22857                 if(f.pop().length != 0){
22858                     return;
22859                 }
22860                 
22861                 this.setValue(f.join("\t") + value.substring(end));
22862                 this.el.dom.setSelectionRange(start - 1, start - 1);
22863                 
22864             },
22865             
22866             "home" : function(e){
22867                 e.preventDefault();
22868                 
22869                 var curr = this.el.dom.selectionStart;
22870                 var lines = this.getValue().split("\n");
22871                 
22872                 if(!lines.length){
22873                     return;
22874                 }
22875                 
22876                 if(e.ctrlKey){
22877                     this.el.dom.setSelectionRange(0, 0);
22878                     return;
22879                 }
22880                 
22881                 var pos = 0;
22882                 
22883                 for (var i = 0; i < lines.length;i++) {
22884                     pos += lines[i].length;
22885                     
22886                     if(i != 0){
22887                         pos += 1;
22888                     }
22889                     
22890                     if(pos < curr){
22891                         continue;
22892                     }
22893                     
22894                     pos -= lines[i].length;
22895                     
22896                     break;
22897                 }
22898                 
22899                 if(!e.shiftKey){
22900                     this.el.dom.setSelectionRange(pos, pos);
22901                     return;
22902                 }
22903                 
22904                 this.el.dom.selectionStart = pos;
22905                 this.el.dom.selectionEnd = curr;
22906             },
22907             
22908             "end" : function(e){
22909                 e.preventDefault();
22910                 
22911                 var curr = this.el.dom.selectionStart;
22912                 var lines = this.getValue().split("\n");
22913                 
22914                 if(!lines.length){
22915                     return;
22916                 }
22917                 
22918                 if(e.ctrlKey){
22919                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22920                     return;
22921                 }
22922                 
22923                 var pos = 0;
22924                 
22925                 for (var i = 0; i < lines.length;i++) {
22926                     
22927                     pos += lines[i].length;
22928                     
22929                     if(i != 0){
22930                         pos += 1;
22931                     }
22932                     
22933                     if(pos < curr){
22934                         continue;
22935                     }
22936                     
22937                     break;
22938                 }
22939                 
22940                 if(!e.shiftKey){
22941                     this.el.dom.setSelectionRange(pos, pos);
22942                     return;
22943                 }
22944                 
22945                 this.el.dom.selectionStart = curr;
22946                 this.el.dom.selectionEnd = pos;
22947             },
22948
22949             scope : this,
22950
22951             doRelay : function(foo, bar, hname){
22952                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22953             },
22954
22955             forceKeyDown: true
22956         });
22957         
22958 //        if(this.autosave && this.w){
22959 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22960 //        }
22961     },
22962
22963     // private
22964     onResize : function(w, h)
22965     {
22966         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22967         var ew = false;
22968         var eh = false;
22969         
22970         if(this.el ){
22971             if(typeof w == 'number'){
22972                 var aw = w - this.wrap.getFrameWidth('lr');
22973                 this.el.setWidth(this.adjustWidth('textarea', aw));
22974                 ew = aw;
22975             }
22976             if(typeof h == 'number'){
22977                 var tbh = 0;
22978                 for (var i =0; i < this.toolbars.length;i++) {
22979                     // fixme - ask toolbars for heights?
22980                     tbh += this.toolbars[i].tb.el.getHeight();
22981                     if (this.toolbars[i].footer) {
22982                         tbh += this.toolbars[i].footer.el.getHeight();
22983                     }
22984                 }
22985                 
22986                 
22987                 
22988                 
22989                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22990                 ah -= 5; // knock a few pixes off for look..
22991 //                Roo.log(ah);
22992                 this.el.setHeight(this.adjustWidth('textarea', ah));
22993                 var eh = ah;
22994             }
22995         }
22996         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22997         this.editorcore.onResize(ew,eh);
22998         
22999     },
23000
23001     /**
23002      * Toggles the editor between standard and source edit mode.
23003      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23004      */
23005     toggleSourceEdit : function(sourceEditMode)
23006     {
23007         this.editorcore.toggleSourceEdit(sourceEditMode);
23008         
23009         if(this.editorcore.sourceEditMode){
23010             Roo.log('editor - showing textarea');
23011             
23012 //            Roo.log('in');
23013 //            Roo.log(this.syncValue());
23014             this.editorcore.syncValue();
23015             this.el.removeClass('x-hidden');
23016             this.el.dom.removeAttribute('tabIndex');
23017             this.el.focus();
23018             
23019             for (var i = 0; i < this.toolbars.length; i++) {
23020                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23021                     this.toolbars[i].tb.hide();
23022                     this.toolbars[i].footer.hide();
23023                 }
23024             }
23025             
23026         }else{
23027             Roo.log('editor - hiding textarea');
23028 //            Roo.log('out')
23029 //            Roo.log(this.pushValue()); 
23030             this.editorcore.pushValue();
23031             
23032             this.el.addClass('x-hidden');
23033             this.el.dom.setAttribute('tabIndex', -1);
23034             
23035             for (var i = 0; i < this.toolbars.length; i++) {
23036                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23037                     this.toolbars[i].tb.show();
23038                     this.toolbars[i].footer.show();
23039                 }
23040             }
23041             
23042             //this.deferFocus();
23043         }
23044         
23045         this.setSize(this.wrap.getSize());
23046         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
23047         
23048         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23049     },
23050  
23051     // private (for BoxComponent)
23052     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23053
23054     // private (for BoxComponent)
23055     getResizeEl : function(){
23056         return this.wrap;
23057     },
23058
23059     // private (for BoxComponent)
23060     getPositionEl : function(){
23061         return this.wrap;
23062     },
23063
23064     // private
23065     initEvents : function(){
23066         this.originalValue = this.getValue();
23067     },
23068
23069     /**
23070      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23071      * @method
23072      */
23073     markInvalid : Roo.emptyFn,
23074     /**
23075      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23076      * @method
23077      */
23078     clearInvalid : Roo.emptyFn,
23079
23080     setValue : function(v){
23081         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23082         this.editorcore.pushValue();
23083     },
23084
23085      
23086     // private
23087     deferFocus : function(){
23088         this.focus.defer(10, this);
23089     },
23090
23091     // doc'ed in Field
23092     focus : function(){
23093         this.editorcore.focus();
23094         
23095     },
23096       
23097
23098     // private
23099     onDestroy : function(){
23100         
23101         
23102         
23103         if(this.rendered){
23104             
23105             for (var i =0; i < this.toolbars.length;i++) {
23106                 // fixme - ask toolbars for heights?
23107                 this.toolbars[i].onDestroy();
23108             }
23109             
23110             this.wrap.dom.innerHTML = '';
23111             this.wrap.remove();
23112         }
23113     },
23114
23115     // private
23116     onFirstFocus : function(){
23117         //Roo.log("onFirstFocus");
23118         this.editorcore.onFirstFocus();
23119          for (var i =0; i < this.toolbars.length;i++) {
23120             this.toolbars[i].onFirstFocus();
23121         }
23122         
23123     },
23124     
23125     // private
23126     syncValue : function()
23127     {
23128         this.editorcore.syncValue();
23129     },
23130     
23131     pushValue : function()
23132     {
23133         this.editorcore.pushValue();
23134     },
23135     
23136     setStylesheets : function(stylesheets)
23137     {
23138         this.editorcore.setStylesheets(stylesheets);
23139     },
23140     
23141     removeStylesheets : function()
23142     {
23143         this.editorcore.removeStylesheets();
23144     }
23145      
23146     
23147     // hide stuff that is not compatible
23148     /**
23149      * @event blur
23150      * @hide
23151      */
23152     /**
23153      * @event change
23154      * @hide
23155      */
23156     /**
23157      * @event focus
23158      * @hide
23159      */
23160     /**
23161      * @event specialkey
23162      * @hide
23163      */
23164     /**
23165      * @cfg {String} fieldClass @hide
23166      */
23167     /**
23168      * @cfg {String} focusClass @hide
23169      */
23170     /**
23171      * @cfg {String} autoCreate @hide
23172      */
23173     /**
23174      * @cfg {String} inputType @hide
23175      */
23176     /**
23177      * @cfg {String} invalidClass @hide
23178      */
23179     /**
23180      * @cfg {String} invalidText @hide
23181      */
23182     /**
23183      * @cfg {String} msgFx @hide
23184      */
23185     /**
23186      * @cfg {String} validateOnBlur @hide
23187      */
23188 });
23189  
23190     // <script type="text/javascript">
23191 /*
23192  * Based on
23193  * Ext JS Library 1.1.1
23194  * Copyright(c) 2006-2007, Ext JS, LLC.
23195  *  
23196  
23197  */
23198
23199 /**
23200  * @class Roo.form.HtmlEditorToolbar1
23201  * Basic Toolbar
23202  * 
23203  * Usage:
23204  *
23205  new Roo.form.HtmlEditor({
23206     ....
23207     toolbars : [
23208         new Roo.form.HtmlEditorToolbar1({
23209             disable : { fonts: 1 , format: 1, ..., ... , ...],
23210             btns : [ .... ]
23211         })
23212     }
23213      
23214  * 
23215  * @cfg {Object} disable List of elements to disable..
23216  * @cfg {Array} btns List of additional buttons.
23217  * 
23218  * 
23219  * NEEDS Extra CSS? 
23220  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23221  */
23222  
23223 Roo.form.HtmlEditor.ToolbarStandard = function(config)
23224 {
23225     
23226     Roo.apply(this, config);
23227     
23228     // default disabled, based on 'good practice'..
23229     this.disable = this.disable || {};
23230     Roo.applyIf(this.disable, {
23231         fontSize : true,
23232         colors : true,
23233         specialElements : true
23234     });
23235     
23236     
23237     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23238     // dont call parent... till later.
23239 }
23240
23241 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
23242     
23243     tb: false,
23244     
23245     rendered: false,
23246     
23247     editor : false,
23248     editorcore : false,
23249     /**
23250      * @cfg {Object} disable  List of toolbar elements to disable
23251          
23252      */
23253     disable : false,
23254     
23255     
23256      /**
23257      * @cfg {String} createLinkText The default text for the create link prompt
23258      */
23259     createLinkText : 'Please enter the URL for the link:',
23260     /**
23261      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23262      */
23263     defaultLinkValue : 'http:/'+'/',
23264    
23265     
23266       /**
23267      * @cfg {Array} fontFamilies An array of available font families
23268      */
23269     fontFamilies : [
23270         'Arial',
23271         'Courier New',
23272         'Tahoma',
23273         'Times New Roman',
23274         'Verdana'
23275     ],
23276     
23277     specialChars : [
23278            "&#169;",
23279           "&#174;",     
23280           "&#8482;",    
23281           "&#163;" ,    
23282          // "&#8212;",    
23283           "&#8230;",    
23284           "&#247;" ,    
23285         //  "&#225;" ,     ?? a acute?
23286            "&#8364;"    , //Euro
23287        //   "&#8220;"    ,
23288         //  "&#8221;"    ,
23289         //  "&#8226;"    ,
23290           "&#176;"  //   , // degrees
23291
23292          // "&#233;"     , // e ecute
23293          // "&#250;"     , // u ecute?
23294     ],
23295     
23296     specialElements : [
23297         {
23298             text: "Insert Table",
23299             xtype: 'MenuItem',
23300             xns : Roo.Menu,
23301             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23302                 
23303         },
23304         {    
23305             text: "Insert Image",
23306             xtype: 'MenuItem',
23307             xns : Roo.Menu,
23308             ihtml : '<img src="about:blank"/>'
23309             
23310         }
23311         
23312          
23313     ],
23314     
23315     
23316     inputElements : [ 
23317             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23318             "input:submit", "input:button", "select", "textarea", "label" ],
23319     formats : [
23320         ["p"] ,  
23321         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23322         ["pre"],[ "code"], 
23323         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23324         ['div'],['span'],
23325         ['sup'],['sub']
23326     ],
23327     
23328     cleanStyles : [
23329         "font-size"
23330     ],
23331      /**
23332      * @cfg {String} defaultFont default font to use.
23333      */
23334     defaultFont: 'tahoma',
23335    
23336     fontSelect : false,
23337     
23338     
23339     formatCombo : false,
23340     
23341     init : function(editor)
23342     {
23343         this.editor = editor;
23344         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23345         var editorcore = this.editorcore;
23346         
23347         var _t = this;
23348         
23349         var fid = editorcore.frameId;
23350         var etb = this;
23351         function btn(id, toggle, handler){
23352             var xid = fid + '-'+ id ;
23353             return {
23354                 id : xid,
23355                 cmd : id,
23356                 cls : 'x-btn-icon x-edit-'+id,
23357                 enableToggle:toggle !== false,
23358                 scope: _t, // was editor...
23359                 handler:handler||_t.relayBtnCmd,
23360                 clickEvent:'mousedown',
23361                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23362                 tabIndex:-1
23363             };
23364         }
23365         
23366         
23367         
23368         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23369         this.tb = tb;
23370          // stop form submits
23371         tb.el.on('click', function(e){
23372             e.preventDefault(); // what does this do?
23373         });
23374
23375         if(!this.disable.font) { // && !Roo.isSafari){
23376             /* why no safari for fonts 
23377             editor.fontSelect = tb.el.createChild({
23378                 tag:'select',
23379                 tabIndex: -1,
23380                 cls:'x-font-select',
23381                 html: this.createFontOptions()
23382             });
23383             
23384             editor.fontSelect.on('change', function(){
23385                 var font = editor.fontSelect.dom.value;
23386                 editor.relayCmd('fontname', font);
23387                 editor.deferFocus();
23388             }, editor);
23389             
23390             tb.add(
23391                 editor.fontSelect.dom,
23392                 '-'
23393             );
23394             */
23395             
23396         };
23397         if(!this.disable.formats){
23398             this.formatCombo = new Roo.form.ComboBox({
23399                 store: new Roo.data.SimpleStore({
23400                     id : 'tag',
23401                     fields: ['tag'],
23402                     data : this.formats // from states.js
23403                 }),
23404                 blockFocus : true,
23405                 name : '',
23406                 //autoCreate : {tag: "div",  size: "20"},
23407                 displayField:'tag',
23408                 typeAhead: false,
23409                 mode: 'local',
23410                 editable : false,
23411                 triggerAction: 'all',
23412                 emptyText:'Add tag',
23413                 selectOnFocus:true,
23414                 width:135,
23415                 listeners : {
23416                     'select': function(c, r, i) {
23417                         editorcore.insertTag(r.get('tag'));
23418                         editor.focus();
23419                     }
23420                 }
23421
23422             });
23423             tb.addField(this.formatCombo);
23424             
23425         }
23426         
23427         if(!this.disable.format){
23428             tb.add(
23429                 btn('bold'),
23430                 btn('italic'),
23431                 btn('underline'),
23432                 btn('strikethrough')
23433             );
23434         };
23435         if(!this.disable.fontSize){
23436             tb.add(
23437                 '-',
23438                 
23439                 
23440                 btn('increasefontsize', false, editorcore.adjustFont),
23441                 btn('decreasefontsize', false, editorcore.adjustFont)
23442             );
23443         };
23444         
23445         
23446         if(!this.disable.colors){
23447             tb.add(
23448                 '-', {
23449                     id:editorcore.frameId +'-forecolor',
23450                     cls:'x-btn-icon x-edit-forecolor',
23451                     clickEvent:'mousedown',
23452                     tooltip: this.buttonTips['forecolor'] || undefined,
23453                     tabIndex:-1,
23454                     menu : new Roo.menu.ColorMenu({
23455                         allowReselect: true,
23456                         focus: Roo.emptyFn,
23457                         value:'000000',
23458                         plain:true,
23459                         selectHandler: function(cp, color){
23460                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23461                             editor.deferFocus();
23462                         },
23463                         scope: editorcore,
23464                         clickEvent:'mousedown'
23465                     })
23466                 }, {
23467                     id:editorcore.frameId +'backcolor',
23468                     cls:'x-btn-icon x-edit-backcolor',
23469                     clickEvent:'mousedown',
23470                     tooltip: this.buttonTips['backcolor'] || undefined,
23471                     tabIndex:-1,
23472                     menu : new Roo.menu.ColorMenu({
23473                         focus: Roo.emptyFn,
23474                         value:'FFFFFF',
23475                         plain:true,
23476                         allowReselect: true,
23477                         selectHandler: function(cp, color){
23478                             if(Roo.isGecko){
23479                                 editorcore.execCmd('useCSS', false);
23480                                 editorcore.execCmd('hilitecolor', color);
23481                                 editorcore.execCmd('useCSS', true);
23482                                 editor.deferFocus();
23483                             }else{
23484                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23485                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23486                                 editor.deferFocus();
23487                             }
23488                         },
23489                         scope:editorcore,
23490                         clickEvent:'mousedown'
23491                     })
23492                 }
23493             );
23494         };
23495         // now add all the items...
23496         
23497
23498         if(!this.disable.alignments){
23499             tb.add(
23500                 '-',
23501                 btn('justifyleft'),
23502                 btn('justifycenter'),
23503                 btn('justifyright')
23504             );
23505         };
23506
23507         //if(!Roo.isSafari){
23508             if(!this.disable.links){
23509                 tb.add(
23510                     '-',
23511                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23512                 );
23513             };
23514
23515             if(!this.disable.lists){
23516                 tb.add(
23517                     '-',
23518                     btn('insertorderedlist'),
23519                     btn('insertunorderedlist')
23520                 );
23521             }
23522             if(!this.disable.sourceEdit){
23523                 tb.add(
23524                     '-',
23525                     btn('sourceedit', true, function(btn){
23526                         this.toggleSourceEdit(btn.pressed);
23527                     })
23528                 );
23529             }
23530         //}
23531         
23532         var smenu = { };
23533         // special menu.. - needs to be tidied up..
23534         if (!this.disable.special) {
23535             smenu = {
23536                 text: "&#169;",
23537                 cls: 'x-edit-none',
23538                 
23539                 menu : {
23540                     items : []
23541                 }
23542             };
23543             for (var i =0; i < this.specialChars.length; i++) {
23544                 smenu.menu.items.push({
23545                     
23546                     html: this.specialChars[i],
23547                     handler: function(a,b) {
23548                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23549                         //editor.insertAtCursor(a.html);
23550                         
23551                     },
23552                     tabIndex:-1
23553                 });
23554             }
23555             
23556             
23557             tb.add(smenu);
23558             
23559             
23560         }
23561         
23562         var cmenu = { };
23563         if (!this.disable.cleanStyles) {
23564             cmenu = {
23565                 cls: 'x-btn-icon x-btn-clear',
23566                 
23567                 menu : {
23568                     items : []
23569                 }
23570             };
23571             for (var i =0; i < this.cleanStyles.length; i++) {
23572                 cmenu.menu.items.push({
23573                     actiontype : this.cleanStyles[i],
23574                     html: 'Remove ' + this.cleanStyles[i],
23575                     handler: function(a,b) {
23576 //                        Roo.log(a);
23577 //                        Roo.log(b);
23578                         var c = Roo.get(editorcore.doc.body);
23579                         c.select('[style]').each(function(s) {
23580                             s.dom.style.removeProperty(a.actiontype);
23581                         });
23582                         editorcore.syncValue();
23583                     },
23584                     tabIndex:-1
23585                 });
23586             }
23587              cmenu.menu.items.push({
23588                 actiontype : 'tablewidths',
23589                 html: 'Remove Table Widths',
23590                 handler: function(a,b) {
23591                     editorcore.cleanTableWidths();
23592                     editorcore.syncValue();
23593                 },
23594                 tabIndex:-1
23595             });
23596             cmenu.menu.items.push({
23597                 actiontype : 'word',
23598                 html: 'Remove MS Word Formating',
23599                 handler: function(a,b) {
23600                     editorcore.cleanWord();
23601                     editorcore.syncValue();
23602                 },
23603                 tabIndex:-1
23604             });
23605             
23606             cmenu.menu.items.push({
23607                 actiontype : 'all',
23608                 html: 'Remove All Styles',
23609                 handler: function(a,b) {
23610                     
23611                     var c = Roo.get(editorcore.doc.body);
23612                     c.select('[style]').each(function(s) {
23613                         s.dom.removeAttribute('style');
23614                     });
23615                     editorcore.syncValue();
23616                 },
23617                 tabIndex:-1
23618             });
23619             
23620             cmenu.menu.items.push({
23621                 actiontype : 'all',
23622                 html: 'Remove All CSS Classes',
23623                 handler: function(a,b) {
23624                     
23625                     var c = Roo.get(editorcore.doc.body);
23626                     c.select('[class]').each(function(s) {
23627                         s.dom.removeAttribute('class');
23628                     });
23629                     editorcore.cleanWord();
23630                     editorcore.syncValue();
23631                 },
23632                 tabIndex:-1
23633             });
23634             
23635              cmenu.menu.items.push({
23636                 actiontype : 'tidy',
23637                 html: 'Tidy HTML Source',
23638                 handler: function(a,b) {
23639                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23640                     editorcore.syncValue();
23641                 },
23642                 tabIndex:-1
23643             });
23644             
23645             
23646             tb.add(cmenu);
23647         }
23648          
23649         if (!this.disable.specialElements) {
23650             var semenu = {
23651                 text: "Other;",
23652                 cls: 'x-edit-none',
23653                 menu : {
23654                     items : []
23655                 }
23656             };
23657             for (var i =0; i < this.specialElements.length; i++) {
23658                 semenu.menu.items.push(
23659                     Roo.apply({ 
23660                         handler: function(a,b) {
23661                             editor.insertAtCursor(this.ihtml);
23662                         }
23663                     }, this.specialElements[i])
23664                 );
23665                     
23666             }
23667             
23668             tb.add(semenu);
23669             
23670             
23671         }
23672          
23673         
23674         if (this.btns) {
23675             for(var i =0; i< this.btns.length;i++) {
23676                 var b = Roo.factory(this.btns[i],Roo.form);
23677                 b.cls =  'x-edit-none';
23678                 
23679                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23680                     b.cls += ' x-init-enable';
23681                 }
23682                 
23683                 b.scope = editorcore;
23684                 tb.add(b);
23685             }
23686         
23687         }
23688         
23689         
23690         
23691         // disable everything...
23692         
23693         this.tb.items.each(function(item){
23694             
23695            if(
23696                 item.id != editorcore.frameId+ '-sourceedit' && 
23697                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23698             ){
23699                 
23700                 item.disable();
23701             }
23702         });
23703         this.rendered = true;
23704         
23705         // the all the btns;
23706         editor.on('editorevent', this.updateToolbar, this);
23707         // other toolbars need to implement this..
23708         //editor.on('editmodechange', this.updateToolbar, this);
23709     },
23710     
23711     
23712     relayBtnCmd : function(btn) {
23713         this.editorcore.relayCmd(btn.cmd);
23714     },
23715     // private used internally
23716     createLink : function(){
23717         Roo.log("create link?");
23718         var url = prompt(this.createLinkText, this.defaultLinkValue);
23719         if(url && url != 'http:/'+'/'){
23720             this.editorcore.relayCmd('createlink', url);
23721         }
23722     },
23723
23724     
23725     /**
23726      * Protected method that will not generally be called directly. It triggers
23727      * a toolbar update by reading the markup state of the current selection in the editor.
23728      */
23729     updateToolbar: function(){
23730
23731         if(!this.editorcore.activated){
23732             this.editor.onFirstFocus();
23733             return;
23734         }
23735
23736         var btns = this.tb.items.map, 
23737             doc = this.editorcore.doc,
23738             frameId = this.editorcore.frameId;
23739
23740         if(!this.disable.font && !Roo.isSafari){
23741             /*
23742             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23743             if(name != this.fontSelect.dom.value){
23744                 this.fontSelect.dom.value = name;
23745             }
23746             */
23747         }
23748         if(!this.disable.format){
23749             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23750             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23751             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23752             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23753         }
23754         if(!this.disable.alignments){
23755             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23756             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23757             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23758         }
23759         if(!Roo.isSafari && !this.disable.lists){
23760             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23761             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23762         }
23763         
23764         var ans = this.editorcore.getAllAncestors();
23765         if (this.formatCombo) {
23766             
23767             
23768             var store = this.formatCombo.store;
23769             this.formatCombo.setValue("");
23770             for (var i =0; i < ans.length;i++) {
23771                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23772                     // select it..
23773                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23774                     break;
23775                 }
23776             }
23777         }
23778         
23779         
23780         
23781         // hides menus... - so this cant be on a menu...
23782         Roo.menu.MenuMgr.hideAll();
23783
23784         //this.editorsyncValue();
23785     },
23786    
23787     
23788     createFontOptions : function(){
23789         var buf = [], fs = this.fontFamilies, ff, lc;
23790         
23791         
23792         
23793         for(var i = 0, len = fs.length; i< len; i++){
23794             ff = fs[i];
23795             lc = ff.toLowerCase();
23796             buf.push(
23797                 '<option value="',lc,'" style="font-family:',ff,';"',
23798                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23799                     ff,
23800                 '</option>'
23801             );
23802         }
23803         return buf.join('');
23804     },
23805     
23806     toggleSourceEdit : function(sourceEditMode){
23807         
23808         Roo.log("toolbar toogle");
23809         if(sourceEditMode === undefined){
23810             sourceEditMode = !this.sourceEditMode;
23811         }
23812         this.sourceEditMode = sourceEditMode === true;
23813         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23814         // just toggle the button?
23815         if(btn.pressed !== this.sourceEditMode){
23816             btn.toggle(this.sourceEditMode);
23817             return;
23818         }
23819         
23820         if(sourceEditMode){
23821             Roo.log("disabling buttons");
23822             this.tb.items.each(function(item){
23823                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23824                     item.disable();
23825                 }
23826             });
23827           
23828         }else{
23829             Roo.log("enabling buttons");
23830             if(this.editorcore.initialized){
23831                 this.tb.items.each(function(item){
23832                     item.enable();
23833                 });
23834             }
23835             
23836         }
23837         Roo.log("calling toggole on editor");
23838         // tell the editor that it's been pressed..
23839         this.editor.toggleSourceEdit(sourceEditMode);
23840        
23841     },
23842      /**
23843      * Object collection of toolbar tooltips for the buttons in the editor. The key
23844      * is the command id associated with that button and the value is a valid QuickTips object.
23845      * For example:
23846 <pre><code>
23847 {
23848     bold : {
23849         title: 'Bold (Ctrl+B)',
23850         text: 'Make the selected text bold.',
23851         cls: 'x-html-editor-tip'
23852     },
23853     italic : {
23854         title: 'Italic (Ctrl+I)',
23855         text: 'Make the selected text italic.',
23856         cls: 'x-html-editor-tip'
23857     },
23858     ...
23859 </code></pre>
23860     * @type Object
23861      */
23862     buttonTips : {
23863         bold : {
23864             title: 'Bold (Ctrl+B)',
23865             text: 'Make the selected text bold.',
23866             cls: 'x-html-editor-tip'
23867         },
23868         italic : {
23869             title: 'Italic (Ctrl+I)',
23870             text: 'Make the selected text italic.',
23871             cls: 'x-html-editor-tip'
23872         },
23873         underline : {
23874             title: 'Underline (Ctrl+U)',
23875             text: 'Underline the selected text.',
23876             cls: 'x-html-editor-tip'
23877         },
23878         strikethrough : {
23879             title: 'Strikethrough',
23880             text: 'Strikethrough the selected text.',
23881             cls: 'x-html-editor-tip'
23882         },
23883         increasefontsize : {
23884             title: 'Grow Text',
23885             text: 'Increase the font size.',
23886             cls: 'x-html-editor-tip'
23887         },
23888         decreasefontsize : {
23889             title: 'Shrink Text',
23890             text: 'Decrease the font size.',
23891             cls: 'x-html-editor-tip'
23892         },
23893         backcolor : {
23894             title: 'Text Highlight Color',
23895             text: 'Change the background color of the selected text.',
23896             cls: 'x-html-editor-tip'
23897         },
23898         forecolor : {
23899             title: 'Font Color',
23900             text: 'Change the color of the selected text.',
23901             cls: 'x-html-editor-tip'
23902         },
23903         justifyleft : {
23904             title: 'Align Text Left',
23905             text: 'Align text to the left.',
23906             cls: 'x-html-editor-tip'
23907         },
23908         justifycenter : {
23909             title: 'Center Text',
23910             text: 'Center text in the editor.',
23911             cls: 'x-html-editor-tip'
23912         },
23913         justifyright : {
23914             title: 'Align Text Right',
23915             text: 'Align text to the right.',
23916             cls: 'x-html-editor-tip'
23917         },
23918         insertunorderedlist : {
23919             title: 'Bullet List',
23920             text: 'Start a bulleted list.',
23921             cls: 'x-html-editor-tip'
23922         },
23923         insertorderedlist : {
23924             title: 'Numbered List',
23925             text: 'Start a numbered list.',
23926             cls: 'x-html-editor-tip'
23927         },
23928         createlink : {
23929             title: 'Hyperlink',
23930             text: 'Make the selected text a hyperlink.',
23931             cls: 'x-html-editor-tip'
23932         },
23933         sourceedit : {
23934             title: 'Source Edit',
23935             text: 'Switch to source editing mode.',
23936             cls: 'x-html-editor-tip'
23937         }
23938     },
23939     // private
23940     onDestroy : function(){
23941         if(this.rendered){
23942             
23943             this.tb.items.each(function(item){
23944                 if(item.menu){
23945                     item.menu.removeAll();
23946                     if(item.menu.el){
23947                         item.menu.el.destroy();
23948                     }
23949                 }
23950                 item.destroy();
23951             });
23952              
23953         }
23954     },
23955     onFirstFocus: function() {
23956         this.tb.items.each(function(item){
23957            item.enable();
23958         });
23959     }
23960 });
23961
23962
23963
23964
23965 // <script type="text/javascript">
23966 /*
23967  * Based on
23968  * Ext JS Library 1.1.1
23969  * Copyright(c) 2006-2007, Ext JS, LLC.
23970  *  
23971  
23972  */
23973
23974  
23975 /**
23976  * @class Roo.form.HtmlEditor.ToolbarContext
23977  * Context Toolbar
23978  * 
23979  * Usage:
23980  *
23981  new Roo.form.HtmlEditor({
23982     ....
23983     toolbars : [
23984         { xtype: 'ToolbarStandard', styles : {} }
23985         { xtype: 'ToolbarContext', disable : {} }
23986     ]
23987 })
23988
23989      
23990  * 
23991  * @config : {Object} disable List of elements to disable.. (not done yet.)
23992  * @config : {Object} styles  Map of styles available.
23993  * 
23994  */
23995
23996 Roo.form.HtmlEditor.ToolbarContext = function(config)
23997 {
23998     
23999     Roo.apply(this, config);
24000     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24001     // dont call parent... till later.
24002     this.styles = this.styles || {};
24003 }
24004
24005  
24006
24007 Roo.form.HtmlEditor.ToolbarContext.types = {
24008     'IMG' : {
24009         width : {
24010             title: "Width",
24011             width: 40
24012         },
24013         height:  {
24014             title: "Height",
24015             width: 40
24016         },
24017         align: {
24018             title: "Align",
24019             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24020             width : 80
24021             
24022         },
24023         border: {
24024             title: "Border",
24025             width: 40
24026         },
24027         alt: {
24028             title: "Alt",
24029             width: 120
24030         },
24031         src : {
24032             title: "Src",
24033             width: 220
24034         }
24035         
24036     },
24037     'A' : {
24038         name : {
24039             title: "Name",
24040             width: 50
24041         },
24042         target:  {
24043             title: "Target",
24044             width: 120
24045         },
24046         href:  {
24047             title: "Href",
24048             width: 220
24049         } // border?
24050         
24051     },
24052     'TABLE' : {
24053         rows : {
24054             title: "Rows",
24055             width: 20
24056         },
24057         cols : {
24058             title: "Cols",
24059             width: 20
24060         },
24061         width : {
24062             title: "Width",
24063             width: 40
24064         },
24065         height : {
24066             title: "Height",
24067             width: 40
24068         },
24069         border : {
24070             title: "Border",
24071             width: 20
24072         }
24073     },
24074     'TD' : {
24075         width : {
24076             title: "Width",
24077             width: 40
24078         },
24079         height : {
24080             title: "Height",
24081             width: 40
24082         },   
24083         align: {
24084             title: "Align",
24085             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
24086             width: 80
24087         },
24088         valign: {
24089             title: "Valign",
24090             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
24091             width: 80
24092         },
24093         colspan: {
24094             title: "Colspan",
24095             width: 20
24096             
24097         },
24098          'font-family'  : {
24099             title : "Font",
24100             style : 'fontFamily',
24101             displayField: 'display',
24102             optname : 'font-family',
24103             width: 140
24104         }
24105     },
24106     'INPUT' : {
24107         name : {
24108             title: "name",
24109             width: 120
24110         },
24111         value : {
24112             title: "Value",
24113             width: 120
24114         },
24115         width : {
24116             title: "Width",
24117             width: 40
24118         }
24119     },
24120     'LABEL' : {
24121         'for' : {
24122             title: "For",
24123             width: 120
24124         }
24125     },
24126     'TEXTAREA' : {
24127           name : {
24128             title: "name",
24129             width: 120
24130         },
24131         rows : {
24132             title: "Rows",
24133             width: 20
24134         },
24135         cols : {
24136             title: "Cols",
24137             width: 20
24138         }
24139     },
24140     'SELECT' : {
24141         name : {
24142             title: "name",
24143             width: 120
24144         },
24145         selectoptions : {
24146             title: "Options",
24147             width: 200
24148         }
24149     },
24150     
24151     // should we really allow this??
24152     // should this just be 
24153     'BODY' : {
24154         title : {
24155             title: "Title",
24156             width: 200,
24157             disabled : true
24158         }
24159     },
24160     'SPAN' : {
24161         'font-family'  : {
24162             title : "Font",
24163             style : 'fontFamily',
24164             displayField: 'display',
24165             optname : 'font-family',
24166             width: 140
24167         }
24168     },
24169     'DIV' : {
24170         'font-family'  : {
24171             title : "Font",
24172             style : 'fontFamily',
24173             displayField: 'display',
24174             optname : 'font-family',
24175             width: 140
24176         }
24177     },
24178      'P' : {
24179         'font-family'  : {
24180             title : "Font",
24181             style : 'fontFamily',
24182             displayField: 'display',
24183             optname : 'font-family',
24184             width: 140
24185         }
24186     },
24187     
24188     '*' : {
24189         // empty..
24190     }
24191
24192 };
24193
24194 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
24195 Roo.form.HtmlEditor.ToolbarContext.stores = false;
24196
24197 Roo.form.HtmlEditor.ToolbarContext.options = {
24198         'font-family'  : [ 
24199                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
24200                 [ 'Courier New', 'Courier New'],
24201                 [ 'Tahoma', 'Tahoma'],
24202                 [ 'Times New Roman,serif', 'Times'],
24203                 [ 'Verdana','Verdana' ]
24204         ]
24205 };
24206
24207 // fixme - these need to be configurable..
24208  
24209
24210 //Roo.form.HtmlEditor.ToolbarContext.types
24211
24212
24213 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
24214     
24215     tb: false,
24216     
24217     rendered: false,
24218     
24219     editor : false,
24220     editorcore : false,
24221     /**
24222      * @cfg {Object} disable  List of toolbar elements to disable
24223          
24224      */
24225     disable : false,
24226     /**
24227      * @cfg {Object} styles List of styles 
24228      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
24229      *
24230      * These must be defined in the page, so they get rendered correctly..
24231      * .headline { }
24232      * TD.underline { }
24233      * 
24234      */
24235     styles : false,
24236     
24237     options: false,
24238     
24239     toolbars : false,
24240     
24241     init : function(editor)
24242     {
24243         this.editor = editor;
24244         this.editorcore = editor.editorcore ? editor.editorcore : editor;
24245         var editorcore = this.editorcore;
24246         
24247         var fid = editorcore.frameId;
24248         var etb = this;
24249         function btn(id, toggle, handler){
24250             var xid = fid + '-'+ id ;
24251             return {
24252                 id : xid,
24253                 cmd : id,
24254                 cls : 'x-btn-icon x-edit-'+id,
24255                 enableToggle:toggle !== false,
24256                 scope: editorcore, // was editor...
24257                 handler:handler||editorcore.relayBtnCmd,
24258                 clickEvent:'mousedown',
24259                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24260                 tabIndex:-1
24261             };
24262         }
24263         // create a new element.
24264         var wdiv = editor.wrap.createChild({
24265                 tag: 'div'
24266             }, editor.wrap.dom.firstChild.nextSibling, true);
24267         
24268         // can we do this more than once??
24269         
24270          // stop form submits
24271       
24272  
24273         // disable everything...
24274         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24275         this.toolbars = {};
24276            
24277         for (var i in  ty) {
24278           
24279             this.toolbars[i] = this.buildToolbar(ty[i],i);
24280         }
24281         this.tb = this.toolbars.BODY;
24282         this.tb.el.show();
24283         this.buildFooter();
24284         this.footer.show();
24285         editor.on('hide', function( ) { this.footer.hide() }, this);
24286         editor.on('show', function( ) { this.footer.show() }, this);
24287         
24288          
24289         this.rendered = true;
24290         
24291         // the all the btns;
24292         editor.on('editorevent', this.updateToolbar, this);
24293         // other toolbars need to implement this..
24294         //editor.on('editmodechange', this.updateToolbar, this);
24295     },
24296     
24297     
24298     
24299     /**
24300      * Protected method that will not generally be called directly. It triggers
24301      * a toolbar update by reading the markup state of the current selection in the editor.
24302      *
24303      * Note you can force an update by calling on('editorevent', scope, false)
24304      */
24305     updateToolbar: function(editor,ev,sel){
24306
24307         //Roo.log(ev);
24308         // capture mouse up - this is handy for selecting images..
24309         // perhaps should go somewhere else...
24310         if(!this.editorcore.activated){
24311              this.editor.onFirstFocus();
24312             return;
24313         }
24314         
24315         
24316         
24317         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24318         // selectNode - might want to handle IE?
24319         if (ev &&
24320             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24321             ev.target && ev.target.tagName == 'IMG') {
24322             // they have click on an image...
24323             // let's see if we can change the selection...
24324             sel = ev.target;
24325          
24326               var nodeRange = sel.ownerDocument.createRange();
24327             try {
24328                 nodeRange.selectNode(sel);
24329             } catch (e) {
24330                 nodeRange.selectNodeContents(sel);
24331             }
24332             //nodeRange.collapse(true);
24333             var s = this.editorcore.win.getSelection();
24334             s.removeAllRanges();
24335             s.addRange(nodeRange);
24336         }  
24337         
24338       
24339         var updateFooter = sel ? false : true;
24340         
24341         
24342         var ans = this.editorcore.getAllAncestors();
24343         
24344         // pick
24345         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24346         
24347         if (!sel) { 
24348             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24349             sel = sel ? sel : this.editorcore.doc.body;
24350             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24351             
24352         }
24353         // pick a menu that exists..
24354         var tn = sel.tagName.toUpperCase();
24355         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24356         
24357         tn = sel.tagName.toUpperCase();
24358         
24359         var lastSel = this.tb.selectedNode;
24360         
24361         this.tb.selectedNode = sel;
24362         
24363         // if current menu does not match..
24364         
24365         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24366                 
24367             this.tb.el.hide();
24368             ///console.log("show: " + tn);
24369             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24370             this.tb.el.show();
24371             // update name
24372             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24373             
24374             
24375             // update attributes
24376             if (this.tb.fields) {
24377                 this.tb.fields.each(function(e) {
24378                     if (e.stylename) {
24379                         e.setValue(sel.style[e.stylename]);
24380                         return;
24381                     } 
24382                    e.setValue(sel.getAttribute(e.attrname));
24383                 });
24384             }
24385             
24386             var hasStyles = false;
24387             for(var i in this.styles) {
24388                 hasStyles = true;
24389                 break;
24390             }
24391             
24392             // update styles
24393             if (hasStyles) { 
24394                 var st = this.tb.fields.item(0);
24395                 
24396                 st.store.removeAll();
24397                
24398                 
24399                 var cn = sel.className.split(/\s+/);
24400                 
24401                 var avs = [];
24402                 if (this.styles['*']) {
24403                     
24404                     Roo.each(this.styles['*'], function(v) {
24405                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24406                     });
24407                 }
24408                 if (this.styles[tn]) { 
24409                     Roo.each(this.styles[tn], function(v) {
24410                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24411                     });
24412                 }
24413                 
24414                 st.store.loadData(avs);
24415                 st.collapse();
24416                 st.setValue(cn);
24417             }
24418             // flag our selected Node.
24419             this.tb.selectedNode = sel;
24420            
24421            
24422             Roo.menu.MenuMgr.hideAll();
24423
24424         }
24425         
24426         if (!updateFooter) {
24427             //this.footDisp.dom.innerHTML = ''; 
24428             return;
24429         }
24430         // update the footer
24431         //
24432         var html = '';
24433         
24434         this.footerEls = ans.reverse();
24435         Roo.each(this.footerEls, function(a,i) {
24436             if (!a) { return; }
24437             html += html.length ? ' &gt; '  :  '';
24438             
24439             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24440             
24441         });
24442        
24443         // 
24444         var sz = this.footDisp.up('td').getSize();
24445         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24446         this.footDisp.dom.style.marginLeft = '5px';
24447         
24448         this.footDisp.dom.style.overflow = 'hidden';
24449         
24450         this.footDisp.dom.innerHTML = html;
24451             
24452         //this.editorsyncValue();
24453     },
24454      
24455     
24456    
24457        
24458     // private
24459     onDestroy : function(){
24460         if(this.rendered){
24461             
24462             this.tb.items.each(function(item){
24463                 if(item.menu){
24464                     item.menu.removeAll();
24465                     if(item.menu.el){
24466                         item.menu.el.destroy();
24467                     }
24468                 }
24469                 item.destroy();
24470             });
24471              
24472         }
24473     },
24474     onFirstFocus: function() {
24475         // need to do this for all the toolbars..
24476         this.tb.items.each(function(item){
24477            item.enable();
24478         });
24479     },
24480     buildToolbar: function(tlist, nm)
24481     {
24482         var editor = this.editor;
24483         var editorcore = this.editorcore;
24484          // create a new element.
24485         var wdiv = editor.wrap.createChild({
24486                 tag: 'div'
24487             }, editor.wrap.dom.firstChild.nextSibling, true);
24488         
24489        
24490         var tb = new Roo.Toolbar(wdiv);
24491         // add the name..
24492         
24493         tb.add(nm+ ":&nbsp;");
24494         
24495         var styles = [];
24496         for(var i in this.styles) {
24497             styles.push(i);
24498         }
24499         
24500         // styles...
24501         if (styles && styles.length) {
24502             
24503             // this needs a multi-select checkbox...
24504             tb.addField( new Roo.form.ComboBox({
24505                 store: new Roo.data.SimpleStore({
24506                     id : 'val',
24507                     fields: ['val', 'selected'],
24508                     data : [] 
24509                 }),
24510                 name : '-roo-edit-className',
24511                 attrname : 'className',
24512                 displayField: 'val',
24513                 typeAhead: false,
24514                 mode: 'local',
24515                 editable : false,
24516                 triggerAction: 'all',
24517                 emptyText:'Select Style',
24518                 selectOnFocus:true,
24519                 width: 130,
24520                 listeners : {
24521                     'select': function(c, r, i) {
24522                         // initial support only for on class per el..
24523                         tb.selectedNode.className =  r ? r.get('val') : '';
24524                         editorcore.syncValue();
24525                     }
24526                 }
24527     
24528             }));
24529         }
24530         
24531         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24532         var tbops = tbc.options;
24533         
24534         for (var i in tlist) {
24535             
24536             var item = tlist[i];
24537             tb.add(item.title + ":&nbsp;");
24538             
24539             
24540             //optname == used so you can configure the options available..
24541             var opts = item.opts ? item.opts : false;
24542             if (item.optname) {
24543                 opts = tbops[item.optname];
24544            
24545             }
24546             
24547             if (opts) {
24548                 // opts == pulldown..
24549                 tb.addField( new Roo.form.ComboBox({
24550                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24551                         id : 'val',
24552                         fields: ['val', 'display'],
24553                         data : opts  
24554                     }),
24555                     name : '-roo-edit-' + i,
24556                     attrname : i,
24557                     stylename : item.style ? item.style : false,
24558                     displayField: item.displayField ? item.displayField : 'val',
24559                     valueField :  'val',
24560                     typeAhead: false,
24561                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24562                     editable : false,
24563                     triggerAction: 'all',
24564                     emptyText:'Select',
24565                     selectOnFocus:true,
24566                     width: item.width ? item.width  : 130,
24567                     listeners : {
24568                         'select': function(c, r, i) {
24569                             if (c.stylename) {
24570                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24571                                 return;
24572                             }
24573                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24574                         }
24575                     }
24576
24577                 }));
24578                 continue;
24579                     
24580                  
24581                 
24582                 tb.addField( new Roo.form.TextField({
24583                     name: i,
24584                     width: 100,
24585                     //allowBlank:false,
24586                     value: ''
24587                 }));
24588                 continue;
24589             }
24590             tb.addField( new Roo.form.TextField({
24591                 name: '-roo-edit-' + i,
24592                 attrname : i,
24593                 
24594                 width: item.width,
24595                 //allowBlank:true,
24596                 value: '',
24597                 listeners: {
24598                     'change' : function(f, nv, ov) {
24599                         tb.selectedNode.setAttribute(f.attrname, nv);
24600                         editorcore.syncValue();
24601                     }
24602                 }
24603             }));
24604              
24605         }
24606         
24607         var _this = this;
24608         
24609         if(nm == 'BODY'){
24610             tb.addSeparator();
24611         
24612             tb.addButton( {
24613                 text: 'Stylesheets',
24614
24615                 listeners : {
24616                     click : function ()
24617                     {
24618                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24619                     }
24620                 }
24621             });
24622         }
24623         
24624         tb.addFill();
24625         tb.addButton( {
24626             text: 'Remove Tag',
24627     
24628             listeners : {
24629                 click : function ()
24630                 {
24631                     // remove
24632                     // undo does not work.
24633                      
24634                     var sn = tb.selectedNode;
24635                     
24636                     var pn = sn.parentNode;
24637                     
24638                     var stn =  sn.childNodes[0];
24639                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24640                     while (sn.childNodes.length) {
24641                         var node = sn.childNodes[0];
24642                         sn.removeChild(node);
24643                         //Roo.log(node);
24644                         pn.insertBefore(node, sn);
24645                         
24646                     }
24647                     pn.removeChild(sn);
24648                     var range = editorcore.createRange();
24649         
24650                     range.setStart(stn,0);
24651                     range.setEnd(en,0); //????
24652                     //range.selectNode(sel);
24653                     
24654                     
24655                     var selection = editorcore.getSelection();
24656                     selection.removeAllRanges();
24657                     selection.addRange(range);
24658                     
24659                     
24660                     
24661                     //_this.updateToolbar(null, null, pn);
24662                     _this.updateToolbar(null, null, null);
24663                     _this.footDisp.dom.innerHTML = ''; 
24664                 }
24665             }
24666             
24667                     
24668                 
24669             
24670         });
24671         
24672         
24673         tb.el.on('click', function(e){
24674             e.preventDefault(); // what does this do?
24675         });
24676         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24677         tb.el.hide();
24678         tb.name = nm;
24679         // dont need to disable them... as they will get hidden
24680         return tb;
24681          
24682         
24683     },
24684     buildFooter : function()
24685     {
24686         
24687         var fel = this.editor.wrap.createChild();
24688         this.footer = new Roo.Toolbar(fel);
24689         // toolbar has scrolly on left / right?
24690         var footDisp= new Roo.Toolbar.Fill();
24691         var _t = this;
24692         this.footer.add(
24693             {
24694                 text : '&lt;',
24695                 xtype: 'Button',
24696                 handler : function() {
24697                     _t.footDisp.scrollTo('left',0,true)
24698                 }
24699             }
24700         );
24701         this.footer.add( footDisp );
24702         this.footer.add( 
24703             {
24704                 text : '&gt;',
24705                 xtype: 'Button',
24706                 handler : function() {
24707                     // no animation..
24708                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24709                 }
24710             }
24711         );
24712         var fel = Roo.get(footDisp.el);
24713         fel.addClass('x-editor-context');
24714         this.footDispWrap = fel; 
24715         this.footDispWrap.overflow  = 'hidden';
24716         
24717         this.footDisp = fel.createChild();
24718         this.footDispWrap.on('click', this.onContextClick, this)
24719         
24720         
24721     },
24722     onContextClick : function (ev,dom)
24723     {
24724         ev.preventDefault();
24725         var  cn = dom.className;
24726         //Roo.log(cn);
24727         if (!cn.match(/x-ed-loc-/)) {
24728             return;
24729         }
24730         var n = cn.split('-').pop();
24731         var ans = this.footerEls;
24732         var sel = ans[n];
24733         
24734          // pick
24735         var range = this.editorcore.createRange();
24736         
24737         range.selectNodeContents(sel);
24738         //range.selectNode(sel);
24739         
24740         
24741         var selection = this.editorcore.getSelection();
24742         selection.removeAllRanges();
24743         selection.addRange(range);
24744         
24745         
24746         
24747         this.updateToolbar(null, null, sel);
24748         
24749         
24750     }
24751     
24752     
24753     
24754     
24755     
24756 });
24757
24758
24759
24760
24761
24762 /*
24763  * Based on:
24764  * Ext JS Library 1.1.1
24765  * Copyright(c) 2006-2007, Ext JS, LLC.
24766  *
24767  * Originally Released Under LGPL - original licence link has changed is not relivant.
24768  *
24769  * Fork - LGPL
24770  * <script type="text/javascript">
24771  */
24772  
24773 /**
24774  * @class Roo.form.BasicForm
24775  * @extends Roo.util.Observable
24776  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24777  * @constructor
24778  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24779  * @param {Object} config Configuration options
24780  */
24781 Roo.form.BasicForm = function(el, config){
24782     this.allItems = [];
24783     this.childForms = [];
24784     Roo.apply(this, config);
24785     /*
24786      * The Roo.form.Field items in this form.
24787      * @type MixedCollection
24788      */
24789      
24790      
24791     this.items = new Roo.util.MixedCollection(false, function(o){
24792         return o.id || (o.id = Roo.id());
24793     });
24794     this.addEvents({
24795         /**
24796          * @event beforeaction
24797          * Fires before any action is performed. Return false to cancel the action.
24798          * @param {Form} this
24799          * @param {Action} action The action to be performed
24800          */
24801         beforeaction: true,
24802         /**
24803          * @event actionfailed
24804          * Fires when an action fails.
24805          * @param {Form} this
24806          * @param {Action} action The action that failed
24807          */
24808         actionfailed : true,
24809         /**
24810          * @event actioncomplete
24811          * Fires when an action is completed.
24812          * @param {Form} this
24813          * @param {Action} action The action that completed
24814          */
24815         actioncomplete : true
24816     });
24817     if(el){
24818         this.initEl(el);
24819     }
24820     Roo.form.BasicForm.superclass.constructor.call(this);
24821     
24822     Roo.form.BasicForm.popover.apply();
24823 };
24824
24825 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24826     /**
24827      * @cfg {String} method
24828      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24829      */
24830     /**
24831      * @cfg {DataReader} reader
24832      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24833      * This is optional as there is built-in support for processing JSON.
24834      */
24835     /**
24836      * @cfg {DataReader} errorReader
24837      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24838      * This is completely optional as there is built-in support for processing JSON.
24839      */
24840     /**
24841      * @cfg {String} url
24842      * The URL to use for form actions if one isn't supplied in the action options.
24843      */
24844     /**
24845      * @cfg {Boolean} fileUpload
24846      * Set to true if this form is a file upload.
24847      */
24848      
24849     /**
24850      * @cfg {Object} baseParams
24851      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24852      */
24853      /**
24854      
24855     /**
24856      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24857      */
24858     timeout: 30,
24859
24860     // private
24861     activeAction : null,
24862
24863     /**
24864      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24865      * or setValues() data instead of when the form was first created.
24866      */
24867     trackResetOnLoad : false,
24868     
24869     
24870     /**
24871      * childForms - used for multi-tab forms
24872      * @type {Array}
24873      */
24874     childForms : false,
24875     
24876     /**
24877      * allItems - full list of fields.
24878      * @type {Array}
24879      */
24880     allItems : false,
24881     
24882     /**
24883      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24884      * element by passing it or its id or mask the form itself by passing in true.
24885      * @type Mixed
24886      */
24887     waitMsgTarget : false,
24888     
24889     /**
24890      * @type Boolean
24891      */
24892     disableMask : false,
24893     
24894     /**
24895      * @cfg {Boolean} errorMask (true|false) default false
24896      */
24897     errorMask : false,
24898     
24899     /**
24900      * @cfg {Number} maskOffset Default 100
24901      */
24902     maskOffset : 100,
24903
24904     // private
24905     initEl : function(el){
24906         this.el = Roo.get(el);
24907         this.id = this.el.id || Roo.id();
24908         this.el.on('submit', this.onSubmit, this);
24909         this.el.addClass('x-form');
24910     },
24911
24912     // private
24913     onSubmit : function(e){
24914         e.stopEvent();
24915     },
24916
24917     /**
24918      * Returns true if client-side validation on the form is successful.
24919      * @return Boolean
24920      */
24921     isValid : function(){
24922         var valid = true;
24923         var target = false;
24924         this.items.each(function(f){
24925             if(f.validate()){
24926                 return;
24927             }
24928             
24929             valid = false;
24930                 
24931             if(!target && f.el.isVisible(true)){
24932                 target = f;
24933             }
24934         });
24935         
24936         if(this.errorMask && !valid){
24937             Roo.form.BasicForm.popover.mask(this, target);
24938         }
24939         
24940         return valid;
24941     },
24942
24943     /**
24944      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24945      * @return Boolean
24946      */
24947     isDirty : function(){
24948         var dirty = false;
24949         this.items.each(function(f){
24950            if(f.isDirty()){
24951                dirty = true;
24952                return false;
24953            }
24954         });
24955         return dirty;
24956     },
24957     
24958     /**
24959      * Returns true if any fields in this form have changed since their original load. (New version)
24960      * @return Boolean
24961      */
24962     
24963     hasChanged : function()
24964     {
24965         var dirty = false;
24966         this.items.each(function(f){
24967            if(f.hasChanged()){
24968                dirty = true;
24969                return false;
24970            }
24971         });
24972         return dirty;
24973         
24974     },
24975     /**
24976      * Resets all hasChanged to 'false' -
24977      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24978      * So hasChanged storage is only to be used for this purpose
24979      * @return Boolean
24980      */
24981     resetHasChanged : function()
24982     {
24983         this.items.each(function(f){
24984            f.resetHasChanged();
24985         });
24986         
24987     },
24988     
24989     
24990     /**
24991      * Performs a predefined action (submit or load) or custom actions you define on this form.
24992      * @param {String} actionName The name of the action type
24993      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24994      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24995      * accept other config options):
24996      * <pre>
24997 Property          Type             Description
24998 ----------------  ---------------  ----------------------------------------------------------------------------------
24999 url               String           The url for the action (defaults to the form's url)
25000 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25001 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25002 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25003                                    validate the form on the client (defaults to false)
25004      * </pre>
25005      * @return {BasicForm} this
25006      */
25007     doAction : function(action, options){
25008         if(typeof action == 'string'){
25009             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25010         }
25011         if(this.fireEvent('beforeaction', this, action) !== false){
25012             this.beforeAction(action);
25013             action.run.defer(100, action);
25014         }
25015         return this;
25016     },
25017
25018     /**
25019      * Shortcut to do a submit action.
25020      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25021      * @return {BasicForm} this
25022      */
25023     submit : function(options){
25024         this.doAction('submit', options);
25025         return this;
25026     },
25027
25028     /**
25029      * Shortcut to do a load action.
25030      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25031      * @return {BasicForm} this
25032      */
25033     load : function(options){
25034         this.doAction('load', options);
25035         return this;
25036     },
25037
25038     /**
25039      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25040      * @param {Record} record The record to edit
25041      * @return {BasicForm} this
25042      */
25043     updateRecord : function(record){
25044         record.beginEdit();
25045         var fs = record.fields;
25046         fs.each(function(f){
25047             var field = this.findField(f.name);
25048             if(field){
25049                 record.set(f.name, field.getValue());
25050             }
25051         }, this);
25052         record.endEdit();
25053         return this;
25054     },
25055
25056     /**
25057      * Loads an Roo.data.Record into this form.
25058      * @param {Record} record The record to load
25059      * @return {BasicForm} this
25060      */
25061     loadRecord : function(record){
25062         this.setValues(record.data);
25063         return this;
25064     },
25065
25066     // private
25067     beforeAction : function(action){
25068         var o = action.options;
25069         
25070         if(!this.disableMask) {
25071             if(this.waitMsgTarget === true){
25072                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
25073             }else if(this.waitMsgTarget){
25074                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25075                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
25076             }else {
25077                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
25078             }
25079         }
25080         
25081          
25082     },
25083
25084     // private
25085     afterAction : function(action, success){
25086         this.activeAction = null;
25087         var o = action.options;
25088         
25089         if(!this.disableMask) {
25090             if(this.waitMsgTarget === true){
25091                 this.el.unmask();
25092             }else if(this.waitMsgTarget){
25093                 this.waitMsgTarget.unmask();
25094             }else{
25095                 Roo.MessageBox.updateProgress(1);
25096                 Roo.MessageBox.hide();
25097             }
25098         }
25099         
25100         if(success){
25101             if(o.reset){
25102                 this.reset();
25103             }
25104             Roo.callback(o.success, o.scope, [this, action]);
25105             this.fireEvent('actioncomplete', this, action);
25106             
25107         }else{
25108             
25109             // failure condition..
25110             // we have a scenario where updates need confirming.
25111             // eg. if a locking scenario exists..
25112             // we look for { errors : { needs_confirm : true }} in the response.
25113             if (
25114                 (typeof(action.result) != 'undefined')  &&
25115                 (typeof(action.result.errors) != 'undefined')  &&
25116                 (typeof(action.result.errors.needs_confirm) != 'undefined')
25117            ){
25118                 var _t = this;
25119                 Roo.MessageBox.confirm(
25120                     "Change requires confirmation",
25121                     action.result.errorMsg,
25122                     function(r) {
25123                         if (r != 'yes') {
25124                             return;
25125                         }
25126                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
25127                     }
25128                     
25129                 );
25130                 
25131                 
25132                 
25133                 return;
25134             }
25135             
25136             Roo.callback(o.failure, o.scope, [this, action]);
25137             // show an error message if no failed handler is set..
25138             if (!this.hasListener('actionfailed')) {
25139                 Roo.MessageBox.alert("Error",
25140                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
25141                         action.result.errorMsg :
25142                         "Saving Failed, please check your entries or try again"
25143                 );
25144             }
25145             
25146             this.fireEvent('actionfailed', this, action);
25147         }
25148         
25149     },
25150
25151     /**
25152      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25153      * @param {String} id The value to search for
25154      * @return Field
25155      */
25156     findField : function(id){
25157         var field = this.items.get(id);
25158         if(!field){
25159             this.items.each(function(f){
25160                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25161                     field = f;
25162                     return false;
25163                 }
25164             });
25165         }
25166         return field || null;
25167     },
25168
25169     /**
25170      * Add a secondary form to this one, 
25171      * Used to provide tabbed forms. One form is primary, with hidden values 
25172      * which mirror the elements from the other forms.
25173      * 
25174      * @param {Roo.form.Form} form to add.
25175      * 
25176      */
25177     addForm : function(form)
25178     {
25179        
25180         if (this.childForms.indexOf(form) > -1) {
25181             // already added..
25182             return;
25183         }
25184         this.childForms.push(form);
25185         var n = '';
25186         Roo.each(form.allItems, function (fe) {
25187             
25188             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
25189             if (this.findField(n)) { // already added..
25190                 return;
25191             }
25192             var add = new Roo.form.Hidden({
25193                 name : n
25194             });
25195             add.render(this.el);
25196             
25197             this.add( add );
25198         }, this);
25199         
25200     },
25201     /**
25202      * Mark fields in this form invalid in bulk.
25203      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25204      * @return {BasicForm} this
25205      */
25206     markInvalid : function(errors){
25207         if(errors instanceof Array){
25208             for(var i = 0, len = errors.length; i < len; i++){
25209                 var fieldError = errors[i];
25210                 var f = this.findField(fieldError.id);
25211                 if(f){
25212                     f.markInvalid(fieldError.msg);
25213                 }
25214             }
25215         }else{
25216             var field, id;
25217             for(id in errors){
25218                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25219                     field.markInvalid(errors[id]);
25220                 }
25221             }
25222         }
25223         Roo.each(this.childForms || [], function (f) {
25224             f.markInvalid(errors);
25225         });
25226         
25227         return this;
25228     },
25229
25230     /**
25231      * Set values for fields in this form in bulk.
25232      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25233      * @return {BasicForm} this
25234      */
25235     setValues : function(values){
25236         if(values instanceof Array){ // array of objects
25237             for(var i = 0, len = values.length; i < len; i++){
25238                 var v = values[i];
25239                 var f = this.findField(v.id);
25240                 if(f){
25241                     f.setValue(v.value);
25242                     if(this.trackResetOnLoad){
25243                         f.originalValue = f.getValue();
25244                     }
25245                 }
25246             }
25247         }else{ // object hash
25248             var field, id;
25249             for(id in values){
25250                 if(typeof values[id] != 'function' && (field = this.findField(id))){
25251                     
25252                     if (field.setFromData && 
25253                         field.valueField && 
25254                         field.displayField &&
25255                         // combos' with local stores can 
25256                         // be queried via setValue()
25257                         // to set their value..
25258                         (field.store && !field.store.isLocal)
25259                         ) {
25260                         // it's a combo
25261                         var sd = { };
25262                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25263                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25264                         field.setFromData(sd);
25265                         
25266                     } else {
25267                         field.setValue(values[id]);
25268                     }
25269                     
25270                     
25271                     if(this.trackResetOnLoad){
25272                         field.originalValue = field.getValue();
25273                     }
25274                 }
25275             }
25276         }
25277         this.resetHasChanged();
25278         
25279         
25280         Roo.each(this.childForms || [], function (f) {
25281             f.setValues(values);
25282             f.resetHasChanged();
25283         });
25284                 
25285         return this;
25286     },
25287  
25288     /**
25289      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25290      * they are returned as an array.
25291      * @param {Boolean} asString
25292      * @return {Object}
25293      */
25294     getValues : function(asString){
25295         if (this.childForms) {
25296             // copy values from the child forms
25297             Roo.each(this.childForms, function (f) {
25298                 this.setValues(f.getValues());
25299             }, this);
25300         }
25301         
25302         // use formdata
25303         if (typeof(FormData) != 'undefined' && asString !== true) {
25304             var fd = (new FormData(this.el.dom)).entries();
25305             var ret = {};
25306             var ent = fd.next();
25307             while (!ent.done) {
25308                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25309                 ent = fd.next();
25310             };
25311             return ret;
25312         }
25313         
25314         
25315         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25316         if(asString === true){
25317             return fs;
25318         }
25319         return Roo.urlDecode(fs);
25320     },
25321     
25322     /**
25323      * Returns the fields in this form as an object with key/value pairs. 
25324      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25325      * @return {Object}
25326      */
25327     getFieldValues : function(with_hidden)
25328     {
25329         if (this.childForms) {
25330             // copy values from the child forms
25331             // should this call getFieldValues - probably not as we do not currently copy
25332             // hidden fields when we generate..
25333             Roo.each(this.childForms, function (f) {
25334                 this.setValues(f.getValues());
25335             }, this);
25336         }
25337         
25338         var ret = {};
25339         this.items.each(function(f){
25340             if (!f.getName()) {
25341                 return;
25342             }
25343             var v = f.getValue();
25344             if (f.inputType =='radio') {
25345                 if (typeof(ret[f.getName()]) == 'undefined') {
25346                     ret[f.getName()] = ''; // empty..
25347                 }
25348                 
25349                 if (!f.el.dom.checked) {
25350                     return;
25351                     
25352                 }
25353                 v = f.el.dom.value;
25354                 
25355             }
25356             
25357             // not sure if this supported any more..
25358             if ((typeof(v) == 'object') && f.getRawValue) {
25359                 v = f.getRawValue() ; // dates..
25360             }
25361             // combo boxes where name != hiddenName...
25362             if (f.name != f.getName()) {
25363                 ret[f.name] = f.getRawValue();
25364             }
25365             ret[f.getName()] = v;
25366         });
25367         
25368         return ret;
25369     },
25370
25371     /**
25372      * Clears all invalid messages in this form.
25373      * @return {BasicForm} this
25374      */
25375     clearInvalid : function(){
25376         this.items.each(function(f){
25377            f.clearInvalid();
25378         });
25379         
25380         Roo.each(this.childForms || [], function (f) {
25381             f.clearInvalid();
25382         });
25383         
25384         
25385         return this;
25386     },
25387
25388     /**
25389      * Resets this form.
25390      * @return {BasicForm} this
25391      */
25392     reset : function(){
25393         this.items.each(function(f){
25394             f.reset();
25395         });
25396         
25397         Roo.each(this.childForms || [], function (f) {
25398             f.reset();
25399         });
25400         this.resetHasChanged();
25401         
25402         return this;
25403     },
25404
25405     /**
25406      * Add Roo.form components to this form.
25407      * @param {Field} field1
25408      * @param {Field} field2 (optional)
25409      * @param {Field} etc (optional)
25410      * @return {BasicForm} this
25411      */
25412     add : function(){
25413         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25414         return this;
25415     },
25416
25417
25418     /**
25419      * Removes a field from the items collection (does NOT remove its markup).
25420      * @param {Field} field
25421      * @return {BasicForm} this
25422      */
25423     remove : function(field){
25424         this.items.remove(field);
25425         return this;
25426     },
25427
25428     /**
25429      * Looks at the fields in this form, checks them for an id attribute,
25430      * and calls applyTo on the existing dom element with that id.
25431      * @return {BasicForm} this
25432      */
25433     render : function(){
25434         this.items.each(function(f){
25435             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25436                 f.applyTo(f.id);
25437             }
25438         });
25439         return this;
25440     },
25441
25442     /**
25443      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25444      * @param {Object} values
25445      * @return {BasicForm} this
25446      */
25447     applyToFields : function(o){
25448         this.items.each(function(f){
25449            Roo.apply(f, o);
25450         });
25451         return this;
25452     },
25453
25454     /**
25455      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25456      * @param {Object} values
25457      * @return {BasicForm} this
25458      */
25459     applyIfToFields : function(o){
25460         this.items.each(function(f){
25461            Roo.applyIf(f, o);
25462         });
25463         return this;
25464     }
25465 });
25466
25467 // back compat
25468 Roo.BasicForm = Roo.form.BasicForm;
25469
25470 Roo.apply(Roo.form.BasicForm, {
25471     
25472     popover : {
25473         
25474         padding : 5,
25475         
25476         isApplied : false,
25477         
25478         isMasked : false,
25479         
25480         form : false,
25481         
25482         target : false,
25483         
25484         intervalID : false,
25485         
25486         maskEl : false,
25487         
25488         apply : function()
25489         {
25490             if(this.isApplied){
25491                 return;
25492             }
25493             
25494             this.maskEl = {
25495                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25496                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25497                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25498                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25499             };
25500             
25501             this.maskEl.top.enableDisplayMode("block");
25502             this.maskEl.left.enableDisplayMode("block");
25503             this.maskEl.bottom.enableDisplayMode("block");
25504             this.maskEl.right.enableDisplayMode("block");
25505             
25506             Roo.get(document.body).on('click', function(){
25507                 this.unmask();
25508             }, this);
25509             
25510             Roo.get(document.body).on('touchstart', function(){
25511                 this.unmask();
25512             }, this);
25513             
25514             this.isApplied = true
25515         },
25516         
25517         mask : function(form, target)
25518         {
25519             this.form = form;
25520             
25521             this.target = target;
25522             
25523             if(!this.form.errorMask || !target.el){
25524                 return;
25525             }
25526             
25527             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25528             
25529             var ot = this.target.el.calcOffsetsTo(scrollable);
25530             
25531             var scrollTo = ot[1] - this.form.maskOffset;
25532             
25533             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25534             
25535             scrollable.scrollTo('top', scrollTo);
25536             
25537             var el = this.target.wrap || this.target.el;
25538             
25539             var box = el.getBox();
25540             
25541             this.maskEl.top.setStyle('position', 'absolute');
25542             this.maskEl.top.setStyle('z-index', 10000);
25543             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25544             this.maskEl.top.setLeft(0);
25545             this.maskEl.top.setTop(0);
25546             this.maskEl.top.show();
25547             
25548             this.maskEl.left.setStyle('position', 'absolute');
25549             this.maskEl.left.setStyle('z-index', 10000);
25550             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25551             this.maskEl.left.setLeft(0);
25552             this.maskEl.left.setTop(box.y - this.padding);
25553             this.maskEl.left.show();
25554
25555             this.maskEl.bottom.setStyle('position', 'absolute');
25556             this.maskEl.bottom.setStyle('z-index', 10000);
25557             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25558             this.maskEl.bottom.setLeft(0);
25559             this.maskEl.bottom.setTop(box.bottom + this.padding);
25560             this.maskEl.bottom.show();
25561
25562             this.maskEl.right.setStyle('position', 'absolute');
25563             this.maskEl.right.setStyle('z-index', 10000);
25564             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25565             this.maskEl.right.setLeft(box.right + this.padding);
25566             this.maskEl.right.setTop(box.y - this.padding);
25567             this.maskEl.right.show();
25568
25569             this.intervalID = window.setInterval(function() {
25570                 Roo.form.BasicForm.popover.unmask();
25571             }, 10000);
25572
25573             window.onwheel = function(){ return false;};
25574             
25575             (function(){ this.isMasked = true; }).defer(500, this);
25576             
25577         },
25578         
25579         unmask : function()
25580         {
25581             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25582                 return;
25583             }
25584             
25585             this.maskEl.top.setStyle('position', 'absolute');
25586             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25587             this.maskEl.top.hide();
25588
25589             this.maskEl.left.setStyle('position', 'absolute');
25590             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25591             this.maskEl.left.hide();
25592
25593             this.maskEl.bottom.setStyle('position', 'absolute');
25594             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25595             this.maskEl.bottom.hide();
25596
25597             this.maskEl.right.setStyle('position', 'absolute');
25598             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25599             this.maskEl.right.hide();
25600             
25601             window.onwheel = function(){ return true;};
25602             
25603             if(this.intervalID){
25604                 window.clearInterval(this.intervalID);
25605                 this.intervalID = false;
25606             }
25607             
25608             this.isMasked = false;
25609             
25610         }
25611         
25612     }
25613     
25614 });/*
25615  * Based on:
25616  * Ext JS Library 1.1.1
25617  * Copyright(c) 2006-2007, Ext JS, LLC.
25618  *
25619  * Originally Released Under LGPL - original licence link has changed is not relivant.
25620  *
25621  * Fork - LGPL
25622  * <script type="text/javascript">
25623  */
25624
25625 /**
25626  * @class Roo.form.Form
25627  * @extends Roo.form.BasicForm
25628  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25629  * @constructor
25630  * @param {Object} config Configuration options
25631  */
25632 Roo.form.Form = function(config){
25633     var xitems =  [];
25634     if (config.items) {
25635         xitems = config.items;
25636         delete config.items;
25637     }
25638    
25639     
25640     Roo.form.Form.superclass.constructor.call(this, null, config);
25641     this.url = this.url || this.action;
25642     if(!this.root){
25643         this.root = new Roo.form.Layout(Roo.applyIf({
25644             id: Roo.id()
25645         }, config));
25646     }
25647     this.active = this.root;
25648     /**
25649      * Array of all the buttons that have been added to this form via {@link addButton}
25650      * @type Array
25651      */
25652     this.buttons = [];
25653     this.allItems = [];
25654     this.addEvents({
25655         /**
25656          * @event clientvalidation
25657          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25658          * @param {Form} this
25659          * @param {Boolean} valid true if the form has passed client-side validation
25660          */
25661         clientvalidation: true,
25662         /**
25663          * @event rendered
25664          * Fires when the form is rendered
25665          * @param {Roo.form.Form} form
25666          */
25667         rendered : true
25668     });
25669     
25670     if (this.progressUrl) {
25671             // push a hidden field onto the list of fields..
25672             this.addxtype( {
25673                     xns: Roo.form, 
25674                     xtype : 'Hidden', 
25675                     name : 'UPLOAD_IDENTIFIER' 
25676             });
25677         }
25678         
25679     
25680     Roo.each(xitems, this.addxtype, this);
25681     
25682 };
25683
25684 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25685     /**
25686      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25687      */
25688     /**
25689      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25690      */
25691     /**
25692      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25693      */
25694     buttonAlign:'center',
25695
25696     /**
25697      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25698      */
25699     minButtonWidth:75,
25700
25701     /**
25702      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25703      * This property cascades to child containers if not set.
25704      */
25705     labelAlign:'left',
25706
25707     /**
25708      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25709      * fires a looping event with that state. This is required to bind buttons to the valid
25710      * state using the config value formBind:true on the button.
25711      */
25712     monitorValid : false,
25713
25714     /**
25715      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25716      */
25717     monitorPoll : 200,
25718     
25719     /**
25720      * @cfg {String} progressUrl - Url to return progress data 
25721      */
25722     
25723     progressUrl : false,
25724     /**
25725      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25726      * sending a formdata with extra parameters - eg uploaded elements.
25727      */
25728     
25729     formData : false,
25730     
25731     /**
25732      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25733      * fields are added and the column is closed. If no fields are passed the column remains open
25734      * until end() is called.
25735      * @param {Object} config The config to pass to the column
25736      * @param {Field} field1 (optional)
25737      * @param {Field} field2 (optional)
25738      * @param {Field} etc (optional)
25739      * @return Column The column container object
25740      */
25741     column : function(c){
25742         var col = new Roo.form.Column(c);
25743         this.start(col);
25744         if(arguments.length > 1){ // duplicate code required because of Opera
25745             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25746             this.end();
25747         }
25748         return col;
25749     },
25750
25751     /**
25752      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25753      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25754      * until end() is called.
25755      * @param {Object} config The config to pass to the fieldset
25756      * @param {Field} field1 (optional)
25757      * @param {Field} field2 (optional)
25758      * @param {Field} etc (optional)
25759      * @return FieldSet The fieldset container object
25760      */
25761     fieldset : function(c){
25762         var fs = new Roo.form.FieldSet(c);
25763         this.start(fs);
25764         if(arguments.length > 1){ // duplicate code required because of Opera
25765             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25766             this.end();
25767         }
25768         return fs;
25769     },
25770
25771     /**
25772      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25773      * fields are added and the container is closed. If no fields are passed the container remains open
25774      * until end() is called.
25775      * @param {Object} config The config to pass to the Layout
25776      * @param {Field} field1 (optional)
25777      * @param {Field} field2 (optional)
25778      * @param {Field} etc (optional)
25779      * @return Layout The container object
25780      */
25781     container : function(c){
25782         var l = new Roo.form.Layout(c);
25783         this.start(l);
25784         if(arguments.length > 1){ // duplicate code required because of Opera
25785             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25786             this.end();
25787         }
25788         return l;
25789     },
25790
25791     /**
25792      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25793      * @param {Object} container A Roo.form.Layout or subclass of Layout
25794      * @return {Form} this
25795      */
25796     start : function(c){
25797         // cascade label info
25798         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25799         this.active.stack.push(c);
25800         c.ownerCt = this.active;
25801         this.active = c;
25802         return this;
25803     },
25804
25805     /**
25806      * Closes the current open container
25807      * @return {Form} this
25808      */
25809     end : function(){
25810         if(this.active == this.root){
25811             return this;
25812         }
25813         this.active = this.active.ownerCt;
25814         return this;
25815     },
25816
25817     /**
25818      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25819      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25820      * as the label of the field.
25821      * @param {Field} field1
25822      * @param {Field} field2 (optional)
25823      * @param {Field} etc. (optional)
25824      * @return {Form} this
25825      */
25826     add : function(){
25827         this.active.stack.push.apply(this.active.stack, arguments);
25828         this.allItems.push.apply(this.allItems,arguments);
25829         var r = [];
25830         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25831             if(a[i].isFormField){
25832                 r.push(a[i]);
25833             }
25834         }
25835         if(r.length > 0){
25836             Roo.form.Form.superclass.add.apply(this, r);
25837         }
25838         return this;
25839     },
25840     
25841
25842     
25843     
25844     
25845      /**
25846      * Find any element that has been added to a form, using it's ID or name
25847      * This can include framesets, columns etc. along with regular fields..
25848      * @param {String} id - id or name to find.
25849      
25850      * @return {Element} e - or false if nothing found.
25851      */
25852     findbyId : function(id)
25853     {
25854         var ret = false;
25855         if (!id) {
25856             return ret;
25857         }
25858         Roo.each(this.allItems, function(f){
25859             if (f.id == id || f.name == id ){
25860                 ret = f;
25861                 return false;
25862             }
25863         });
25864         return ret;
25865     },
25866
25867     
25868     
25869     /**
25870      * Render this form into the passed container. This should only be called once!
25871      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25872      * @return {Form} this
25873      */
25874     render : function(ct)
25875     {
25876         
25877         
25878         
25879         ct = Roo.get(ct);
25880         var o = this.autoCreate || {
25881             tag: 'form',
25882             method : this.method || 'POST',
25883             id : this.id || Roo.id()
25884         };
25885         this.initEl(ct.createChild(o));
25886
25887         this.root.render(this.el);
25888         
25889        
25890              
25891         this.items.each(function(f){
25892             f.render('x-form-el-'+f.id);
25893         });
25894
25895         if(this.buttons.length > 0){
25896             // tables are required to maintain order and for correct IE layout
25897             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25898                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25899                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25900             }}, null, true);
25901             var tr = tb.getElementsByTagName('tr')[0];
25902             for(var i = 0, len = this.buttons.length; i < len; i++) {
25903                 var b = this.buttons[i];
25904                 var td = document.createElement('td');
25905                 td.className = 'x-form-btn-td';
25906                 b.render(tr.appendChild(td));
25907             }
25908         }
25909         if(this.monitorValid){ // initialize after render
25910             this.startMonitoring();
25911         }
25912         this.fireEvent('rendered', this);
25913         return this;
25914     },
25915
25916     /**
25917      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25918      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25919      * object or a valid Roo.DomHelper element config
25920      * @param {Function} handler The function called when the button is clicked
25921      * @param {Object} scope (optional) The scope of the handler function
25922      * @return {Roo.Button}
25923      */
25924     addButton : function(config, handler, scope){
25925         var bc = {
25926             handler: handler,
25927             scope: scope,
25928             minWidth: this.minButtonWidth,
25929             hideParent:true
25930         };
25931         if(typeof config == "string"){
25932             bc.text = config;
25933         }else{
25934             Roo.apply(bc, config);
25935         }
25936         var btn = new Roo.Button(null, bc);
25937         this.buttons.push(btn);
25938         return btn;
25939     },
25940
25941      /**
25942      * Adds a series of form elements (using the xtype property as the factory method.
25943      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25944      * @param {Object} config 
25945      */
25946     
25947     addxtype : function()
25948     {
25949         var ar = Array.prototype.slice.call(arguments, 0);
25950         var ret = false;
25951         for(var i = 0; i < ar.length; i++) {
25952             if (!ar[i]) {
25953                 continue; // skip -- if this happends something invalid got sent, we 
25954                 // should ignore it, as basically that interface element will not show up
25955                 // and that should be pretty obvious!!
25956             }
25957             
25958             if (Roo.form[ar[i].xtype]) {
25959                 ar[i].form = this;
25960                 var fe = Roo.factory(ar[i], Roo.form);
25961                 if (!ret) {
25962                     ret = fe;
25963                 }
25964                 fe.form = this;
25965                 if (fe.store) {
25966                     fe.store.form = this;
25967                 }
25968                 if (fe.isLayout) {  
25969                          
25970                     this.start(fe);
25971                     this.allItems.push(fe);
25972                     if (fe.items && fe.addxtype) {
25973                         fe.addxtype.apply(fe, fe.items);
25974                         delete fe.items;
25975                     }
25976                      this.end();
25977                     continue;
25978                 }
25979                 
25980                 
25981                  
25982                 this.add(fe);
25983               //  console.log('adding ' + ar[i].xtype);
25984             }
25985             if (ar[i].xtype == 'Button') {  
25986                 //console.log('adding button');
25987                 //console.log(ar[i]);
25988                 this.addButton(ar[i]);
25989                 this.allItems.push(fe);
25990                 continue;
25991             }
25992             
25993             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25994                 alert('end is not supported on xtype any more, use items');
25995             //    this.end();
25996             //    //console.log('adding end');
25997             }
25998             
25999         }
26000         return ret;
26001     },
26002     
26003     /**
26004      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26005      * option "monitorValid"
26006      */
26007     startMonitoring : function(){
26008         if(!this.bound){
26009             this.bound = true;
26010             Roo.TaskMgr.start({
26011                 run : this.bindHandler,
26012                 interval : this.monitorPoll || 200,
26013                 scope: this
26014             });
26015         }
26016     },
26017
26018     /**
26019      * Stops monitoring of the valid state of this form
26020      */
26021     stopMonitoring : function(){
26022         this.bound = false;
26023     },
26024
26025     // private
26026     bindHandler : function(){
26027         if(!this.bound){
26028             return false; // stops binding
26029         }
26030         var valid = true;
26031         this.items.each(function(f){
26032             if(!f.isValid(true)){
26033                 valid = false;
26034                 return false;
26035             }
26036         });
26037         for(var i = 0, len = this.buttons.length; i < len; i++){
26038             var btn = this.buttons[i];
26039             if(btn.formBind === true && btn.disabled === valid){
26040                 btn.setDisabled(!valid);
26041             }
26042         }
26043         this.fireEvent('clientvalidation', this, valid);
26044     }
26045     
26046     
26047     
26048     
26049     
26050     
26051     
26052     
26053 });
26054
26055
26056 // back compat
26057 Roo.Form = Roo.form.Form;
26058 /*
26059  * Based on:
26060  * Ext JS Library 1.1.1
26061  * Copyright(c) 2006-2007, Ext JS, LLC.
26062  *
26063  * Originally Released Under LGPL - original licence link has changed is not relivant.
26064  *
26065  * Fork - LGPL
26066  * <script type="text/javascript">
26067  */
26068
26069 // as we use this in bootstrap.
26070 Roo.namespace('Roo.form');
26071  /**
26072  * @class Roo.form.Action
26073  * Internal Class used to handle form actions
26074  * @constructor
26075  * @param {Roo.form.BasicForm} el The form element or its id
26076  * @param {Object} config Configuration options
26077  */
26078
26079  
26080  
26081 // define the action interface
26082 Roo.form.Action = function(form, options){
26083     this.form = form;
26084     this.options = options || {};
26085 };
26086 /**
26087  * Client Validation Failed
26088  * @const 
26089  */
26090 Roo.form.Action.CLIENT_INVALID = 'client';
26091 /**
26092  * Server Validation Failed
26093  * @const 
26094  */
26095 Roo.form.Action.SERVER_INVALID = 'server';
26096  /**
26097  * Connect to Server Failed
26098  * @const 
26099  */
26100 Roo.form.Action.CONNECT_FAILURE = 'connect';
26101 /**
26102  * Reading Data from Server Failed
26103  * @const 
26104  */
26105 Roo.form.Action.LOAD_FAILURE = 'load';
26106
26107 Roo.form.Action.prototype = {
26108     type : 'default',
26109     failureType : undefined,
26110     response : undefined,
26111     result : undefined,
26112
26113     // interface method
26114     run : function(options){
26115
26116     },
26117
26118     // interface method
26119     success : function(response){
26120
26121     },
26122
26123     // interface method
26124     handleResponse : function(response){
26125
26126     },
26127
26128     // default connection failure
26129     failure : function(response){
26130         
26131         this.response = response;
26132         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26133         this.form.afterAction(this, false);
26134     },
26135
26136     processResponse : function(response){
26137         this.response = response;
26138         if(!response.responseText){
26139             return true;
26140         }
26141         this.result = this.handleResponse(response);
26142         return this.result;
26143     },
26144
26145     // utility functions used internally
26146     getUrl : function(appendParams){
26147         var url = this.options.url || this.form.url || this.form.el.dom.action;
26148         if(appendParams){
26149             var p = this.getParams();
26150             if(p){
26151                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26152             }
26153         }
26154         return url;
26155     },
26156
26157     getMethod : function(){
26158         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26159     },
26160
26161     getParams : function(){
26162         var bp = this.form.baseParams;
26163         var p = this.options.params;
26164         if(p){
26165             if(typeof p == "object"){
26166                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26167             }else if(typeof p == 'string' && bp){
26168                 p += '&' + Roo.urlEncode(bp);
26169             }
26170         }else if(bp){
26171             p = Roo.urlEncode(bp);
26172         }
26173         return p;
26174     },
26175
26176     createCallback : function(){
26177         return {
26178             success: this.success,
26179             failure: this.failure,
26180             scope: this,
26181             timeout: (this.form.timeout*1000),
26182             upload: this.form.fileUpload ? this.success : undefined
26183         };
26184     }
26185 };
26186
26187 Roo.form.Action.Submit = function(form, options){
26188     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26189 };
26190
26191 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26192     type : 'submit',
26193
26194     haveProgress : false,
26195     uploadComplete : false,
26196     
26197     // uploadProgress indicator.
26198     uploadProgress : function()
26199     {
26200         if (!this.form.progressUrl) {
26201             return;
26202         }
26203         
26204         if (!this.haveProgress) {
26205             Roo.MessageBox.progress("Uploading", "Uploading");
26206         }
26207         if (this.uploadComplete) {
26208            Roo.MessageBox.hide();
26209            return;
26210         }
26211         
26212         this.haveProgress = true;
26213    
26214         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26215         
26216         var c = new Roo.data.Connection();
26217         c.request({
26218             url : this.form.progressUrl,
26219             params: {
26220                 id : uid
26221             },
26222             method: 'GET',
26223             success : function(req){
26224                //console.log(data);
26225                 var rdata = false;
26226                 var edata;
26227                 try  {
26228                    rdata = Roo.decode(req.responseText)
26229                 } catch (e) {
26230                     Roo.log("Invalid data from server..");
26231                     Roo.log(edata);
26232                     return;
26233                 }
26234                 if (!rdata || !rdata.success) {
26235                     Roo.log(rdata);
26236                     Roo.MessageBox.alert(Roo.encode(rdata));
26237                     return;
26238                 }
26239                 var data = rdata.data;
26240                 
26241                 if (this.uploadComplete) {
26242                    Roo.MessageBox.hide();
26243                    return;
26244                 }
26245                    
26246                 if (data){
26247                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26248                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26249                     );
26250                 }
26251                 this.uploadProgress.defer(2000,this);
26252             },
26253        
26254             failure: function(data) {
26255                 Roo.log('progress url failed ');
26256                 Roo.log(data);
26257             },
26258             scope : this
26259         });
26260            
26261     },
26262     
26263     
26264     run : function()
26265     {
26266         // run get Values on the form, so it syncs any secondary forms.
26267         this.form.getValues();
26268         
26269         var o = this.options;
26270         var method = this.getMethod();
26271         var isPost = method == 'POST';
26272         if(o.clientValidation === false || this.form.isValid()){
26273             
26274             if (this.form.progressUrl) {
26275                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26276                     (new Date() * 1) + '' + Math.random());
26277                     
26278             } 
26279             
26280             
26281             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26282                 form:this.form.el.dom,
26283                 url:this.getUrl(!isPost),
26284                 method: method,
26285                 params:isPost ? this.getParams() : null,
26286                 isUpload: this.form.fileUpload,
26287                 formData : this.form.formData
26288             }));
26289             
26290             this.uploadProgress();
26291
26292         }else if (o.clientValidation !== false){ // client validation failed
26293             this.failureType = Roo.form.Action.CLIENT_INVALID;
26294             this.form.afterAction(this, false);
26295         }
26296     },
26297
26298     success : function(response)
26299     {
26300         this.uploadComplete= true;
26301         if (this.haveProgress) {
26302             Roo.MessageBox.hide();
26303         }
26304         
26305         
26306         var result = this.processResponse(response);
26307         if(result === true || result.success){
26308             this.form.afterAction(this, true);
26309             return;
26310         }
26311         if(result.errors){
26312             this.form.markInvalid(result.errors);
26313             this.failureType = Roo.form.Action.SERVER_INVALID;
26314         }
26315         this.form.afterAction(this, false);
26316     },
26317     failure : function(response)
26318     {
26319         this.uploadComplete= true;
26320         if (this.haveProgress) {
26321             Roo.MessageBox.hide();
26322         }
26323         
26324         this.response = response;
26325         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26326         this.form.afterAction(this, false);
26327     },
26328     
26329     handleResponse : function(response){
26330         if(this.form.errorReader){
26331             var rs = this.form.errorReader.read(response);
26332             var errors = [];
26333             if(rs.records){
26334                 for(var i = 0, len = rs.records.length; i < len; i++) {
26335                     var r = rs.records[i];
26336                     errors[i] = r.data;
26337                 }
26338             }
26339             if(errors.length < 1){
26340                 errors = null;
26341             }
26342             return {
26343                 success : rs.success,
26344                 errors : errors
26345             };
26346         }
26347         var ret = false;
26348         try {
26349             ret = Roo.decode(response.responseText);
26350         } catch (e) {
26351             ret = {
26352                 success: false,
26353                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26354                 errors : []
26355             };
26356         }
26357         return ret;
26358         
26359     }
26360 });
26361
26362
26363 Roo.form.Action.Load = function(form, options){
26364     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26365     this.reader = this.form.reader;
26366 };
26367
26368 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26369     type : 'load',
26370
26371     run : function(){
26372         
26373         Roo.Ajax.request(Roo.apply(
26374                 this.createCallback(), {
26375                     method:this.getMethod(),
26376                     url:this.getUrl(false),
26377                     params:this.getParams()
26378         }));
26379     },
26380
26381     success : function(response){
26382         
26383         var result = this.processResponse(response);
26384         if(result === true || !result.success || !result.data){
26385             this.failureType = Roo.form.Action.LOAD_FAILURE;
26386             this.form.afterAction(this, false);
26387             return;
26388         }
26389         this.form.clearInvalid();
26390         this.form.setValues(result.data);
26391         this.form.afterAction(this, true);
26392     },
26393
26394     handleResponse : function(response){
26395         if(this.form.reader){
26396             var rs = this.form.reader.read(response);
26397             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26398             return {
26399                 success : rs.success,
26400                 data : data
26401             };
26402         }
26403         return Roo.decode(response.responseText);
26404     }
26405 });
26406
26407 Roo.form.Action.ACTION_TYPES = {
26408     'load' : Roo.form.Action.Load,
26409     'submit' : Roo.form.Action.Submit
26410 };/*
26411  * Based on:
26412  * Ext JS Library 1.1.1
26413  * Copyright(c) 2006-2007, Ext JS, LLC.
26414  *
26415  * Originally Released Under LGPL - original licence link has changed is not relivant.
26416  *
26417  * Fork - LGPL
26418  * <script type="text/javascript">
26419  */
26420  
26421 /**
26422  * @class Roo.form.Layout
26423  * @extends Roo.Component
26424  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26425  * @constructor
26426  * @param {Object} config Configuration options
26427  */
26428 Roo.form.Layout = function(config){
26429     var xitems = [];
26430     if (config.items) {
26431         xitems = config.items;
26432         delete config.items;
26433     }
26434     Roo.form.Layout.superclass.constructor.call(this, config);
26435     this.stack = [];
26436     Roo.each(xitems, this.addxtype, this);
26437      
26438 };
26439
26440 Roo.extend(Roo.form.Layout, Roo.Component, {
26441     /**
26442      * @cfg {String/Object} autoCreate
26443      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26444      */
26445     /**
26446      * @cfg {String/Object/Function} style
26447      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26448      * a function which returns such a specification.
26449      */
26450     /**
26451      * @cfg {String} labelAlign
26452      * Valid values are "left," "top" and "right" (defaults to "left")
26453      */
26454     /**
26455      * @cfg {Number} labelWidth
26456      * Fixed width in pixels of all field labels (defaults to undefined)
26457      */
26458     /**
26459      * @cfg {Boolean} clear
26460      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26461      */
26462     clear : true,
26463     /**
26464      * @cfg {String} labelSeparator
26465      * The separator to use after field labels (defaults to ':')
26466      */
26467     labelSeparator : ':',
26468     /**
26469      * @cfg {Boolean} hideLabels
26470      * True to suppress the display of field labels in this layout (defaults to false)
26471      */
26472     hideLabels : false,
26473
26474     // private
26475     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26476     
26477     isLayout : true,
26478     
26479     // private
26480     onRender : function(ct, position){
26481         if(this.el){ // from markup
26482             this.el = Roo.get(this.el);
26483         }else {  // generate
26484             var cfg = this.getAutoCreate();
26485             this.el = ct.createChild(cfg, position);
26486         }
26487         if(this.style){
26488             this.el.applyStyles(this.style);
26489         }
26490         if(this.labelAlign){
26491             this.el.addClass('x-form-label-'+this.labelAlign);
26492         }
26493         if(this.hideLabels){
26494             this.labelStyle = "display:none";
26495             this.elementStyle = "padding-left:0;";
26496         }else{
26497             if(typeof this.labelWidth == 'number'){
26498                 this.labelStyle = "width:"+this.labelWidth+"px;";
26499                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26500             }
26501             if(this.labelAlign == 'top'){
26502                 this.labelStyle = "width:auto;";
26503                 this.elementStyle = "padding-left:0;";
26504             }
26505         }
26506         var stack = this.stack;
26507         var slen = stack.length;
26508         if(slen > 0){
26509             if(!this.fieldTpl){
26510                 var t = new Roo.Template(
26511                     '<div class="x-form-item {5}">',
26512                         '<label for="{0}" style="{2}">{1}{4}</label>',
26513                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26514                         '</div>',
26515                     '</div><div class="x-form-clear-left"></div>'
26516                 );
26517                 t.disableFormats = true;
26518                 t.compile();
26519                 Roo.form.Layout.prototype.fieldTpl = t;
26520             }
26521             for(var i = 0; i < slen; i++) {
26522                 if(stack[i].isFormField){
26523                     this.renderField(stack[i]);
26524                 }else{
26525                     this.renderComponent(stack[i]);
26526                 }
26527             }
26528         }
26529         if(this.clear){
26530             this.el.createChild({cls:'x-form-clear'});
26531         }
26532     },
26533
26534     // private
26535     renderField : function(f){
26536         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26537                f.id, //0
26538                f.fieldLabel, //1
26539                f.labelStyle||this.labelStyle||'', //2
26540                this.elementStyle||'', //3
26541                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26542                f.itemCls||this.itemCls||''  //5
26543        ], true).getPrevSibling());
26544     },
26545
26546     // private
26547     renderComponent : function(c){
26548         c.render(c.isLayout ? this.el : this.el.createChild());    
26549     },
26550     /**
26551      * Adds a object form elements (using the xtype property as the factory method.)
26552      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26553      * @param {Object} config 
26554      */
26555     addxtype : function(o)
26556     {
26557         // create the lement.
26558         o.form = this.form;
26559         var fe = Roo.factory(o, Roo.form);
26560         this.form.allItems.push(fe);
26561         this.stack.push(fe);
26562         
26563         if (fe.isFormField) {
26564             this.form.items.add(fe);
26565         }
26566          
26567         return fe;
26568     }
26569 });
26570
26571 /**
26572  * @class Roo.form.Column
26573  * @extends Roo.form.Layout
26574  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26575  * @constructor
26576  * @param {Object} config Configuration options
26577  */
26578 Roo.form.Column = function(config){
26579     Roo.form.Column.superclass.constructor.call(this, config);
26580 };
26581
26582 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26583     /**
26584      * @cfg {Number/String} width
26585      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26586      */
26587     /**
26588      * @cfg {String/Object} autoCreate
26589      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26590      */
26591
26592     // private
26593     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26594
26595     // private
26596     onRender : function(ct, position){
26597         Roo.form.Column.superclass.onRender.call(this, ct, position);
26598         if(this.width){
26599             this.el.setWidth(this.width);
26600         }
26601     }
26602 });
26603
26604
26605 /**
26606  * @class Roo.form.Row
26607  * @extends Roo.form.Layout
26608  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26609  * @constructor
26610  * @param {Object} config Configuration options
26611  */
26612
26613  
26614 Roo.form.Row = function(config){
26615     Roo.form.Row.superclass.constructor.call(this, config);
26616 };
26617  
26618 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26619       /**
26620      * @cfg {Number/String} width
26621      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26622      */
26623     /**
26624      * @cfg {Number/String} height
26625      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26626      */
26627     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26628     
26629     padWidth : 20,
26630     // private
26631     onRender : function(ct, position){
26632         //console.log('row render');
26633         if(!this.rowTpl){
26634             var t = new Roo.Template(
26635                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26636                     '<label for="{0}" style="{2}">{1}{4}</label>',
26637                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26638                     '</div>',
26639                 '</div>'
26640             );
26641             t.disableFormats = true;
26642             t.compile();
26643             Roo.form.Layout.prototype.rowTpl = t;
26644         }
26645         this.fieldTpl = this.rowTpl;
26646         
26647         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26648         var labelWidth = 100;
26649         
26650         if ((this.labelAlign != 'top')) {
26651             if (typeof this.labelWidth == 'number') {
26652                 labelWidth = this.labelWidth
26653             }
26654             this.padWidth =  20 + labelWidth;
26655             
26656         }
26657         
26658         Roo.form.Column.superclass.onRender.call(this, ct, position);
26659         if(this.width){
26660             this.el.setWidth(this.width);
26661         }
26662         if(this.height){
26663             this.el.setHeight(this.height);
26664         }
26665     },
26666     
26667     // private
26668     renderField : function(f){
26669         f.fieldEl = this.fieldTpl.append(this.el, [
26670                f.id, f.fieldLabel,
26671                f.labelStyle||this.labelStyle||'',
26672                this.elementStyle||'',
26673                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26674                f.itemCls||this.itemCls||'',
26675                f.width ? f.width + this.padWidth : 160 + this.padWidth
26676        ],true);
26677     }
26678 });
26679  
26680
26681 /**
26682  * @class Roo.form.FieldSet
26683  * @extends Roo.form.Layout
26684  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26685  * @constructor
26686  * @param {Object} config Configuration options
26687  */
26688 Roo.form.FieldSet = function(config){
26689     Roo.form.FieldSet.superclass.constructor.call(this, config);
26690 };
26691
26692 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26693     /**
26694      * @cfg {String} legend
26695      * The text to display as the legend for the FieldSet (defaults to '')
26696      */
26697     /**
26698      * @cfg {String/Object} autoCreate
26699      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26700      */
26701
26702     // private
26703     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26704
26705     // private
26706     onRender : function(ct, position){
26707         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26708         if(this.legend){
26709             this.setLegend(this.legend);
26710         }
26711     },
26712
26713     // private
26714     setLegend : function(text){
26715         if(this.rendered){
26716             this.el.child('legend').update(text);
26717         }
26718     }
26719 });/*
26720  * Based on:
26721  * Ext JS Library 1.1.1
26722  * Copyright(c) 2006-2007, Ext JS, LLC.
26723  *
26724  * Originally Released Under LGPL - original licence link has changed is not relivant.
26725  *
26726  * Fork - LGPL
26727  * <script type="text/javascript">
26728  */
26729 /**
26730  * @class Roo.form.VTypes
26731  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26732  * @singleton
26733  */
26734 Roo.form.VTypes = function(){
26735     // closure these in so they are only created once.
26736     var alpha = /^[a-zA-Z_]+$/;
26737     var alphanum = /^[a-zA-Z0-9_]+$/;
26738     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26739     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26740
26741     // All these messages and functions are configurable
26742     return {
26743         /**
26744          * The function used to validate email addresses
26745          * @param {String} value The email address
26746          */
26747         'email' : function(v){
26748             return email.test(v);
26749         },
26750         /**
26751          * The error text to display when the email validation function returns false
26752          * @type String
26753          */
26754         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26755         /**
26756          * The keystroke filter mask to be applied on email input
26757          * @type RegExp
26758          */
26759         'emailMask' : /[a-z0-9_\.\-@]/i,
26760
26761         /**
26762          * The function used to validate URLs
26763          * @param {String} value The URL
26764          */
26765         'url' : function(v){
26766             return url.test(v);
26767         },
26768         /**
26769          * The error text to display when the url validation function returns false
26770          * @type String
26771          */
26772         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26773         
26774         /**
26775          * The function used to validate alpha values
26776          * @param {String} value The value
26777          */
26778         'alpha' : function(v){
26779             return alpha.test(v);
26780         },
26781         /**
26782          * The error text to display when the alpha validation function returns false
26783          * @type String
26784          */
26785         'alphaText' : 'This field should only contain letters and _',
26786         /**
26787          * The keystroke filter mask to be applied on alpha input
26788          * @type RegExp
26789          */
26790         'alphaMask' : /[a-z_]/i,
26791
26792         /**
26793          * The function used to validate alphanumeric values
26794          * @param {String} value The value
26795          */
26796         'alphanum' : function(v){
26797             return alphanum.test(v);
26798         },
26799         /**
26800          * The error text to display when the alphanumeric validation function returns false
26801          * @type String
26802          */
26803         'alphanumText' : 'This field should only contain letters, numbers and _',
26804         /**
26805          * The keystroke filter mask to be applied on alphanumeric input
26806          * @type RegExp
26807          */
26808         'alphanumMask' : /[a-z0-9_]/i
26809     };
26810 }();//<script type="text/javascript">
26811
26812 /**
26813  * @class Roo.form.FCKeditor
26814  * @extends Roo.form.TextArea
26815  * Wrapper around the FCKEditor http://www.fckeditor.net
26816  * @constructor
26817  * Creates a new FCKeditor
26818  * @param {Object} config Configuration options
26819  */
26820 Roo.form.FCKeditor = function(config){
26821     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26822     this.addEvents({
26823          /**
26824          * @event editorinit
26825          * Fired when the editor is initialized - you can add extra handlers here..
26826          * @param {FCKeditor} this
26827          * @param {Object} the FCK object.
26828          */
26829         editorinit : true
26830     });
26831     
26832     
26833 };
26834 Roo.form.FCKeditor.editors = { };
26835 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26836 {
26837     //defaultAutoCreate : {
26838     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26839     //},
26840     // private
26841     /**
26842      * @cfg {Object} fck options - see fck manual for details.
26843      */
26844     fckconfig : false,
26845     
26846     /**
26847      * @cfg {Object} fck toolbar set (Basic or Default)
26848      */
26849     toolbarSet : 'Basic',
26850     /**
26851      * @cfg {Object} fck BasePath
26852      */ 
26853     basePath : '/fckeditor/',
26854     
26855     
26856     frame : false,
26857     
26858     value : '',
26859     
26860    
26861     onRender : function(ct, position)
26862     {
26863         if(!this.el){
26864             this.defaultAutoCreate = {
26865                 tag: "textarea",
26866                 style:"width:300px;height:60px;",
26867                 autocomplete: "new-password"
26868             };
26869         }
26870         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26871         /*
26872         if(this.grow){
26873             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26874             if(this.preventScrollbars){
26875                 this.el.setStyle("overflow", "hidden");
26876             }
26877             this.el.setHeight(this.growMin);
26878         }
26879         */
26880         //console.log('onrender' + this.getId() );
26881         Roo.form.FCKeditor.editors[this.getId()] = this;
26882          
26883
26884         this.replaceTextarea() ;
26885         
26886     },
26887     
26888     getEditor : function() {
26889         return this.fckEditor;
26890     },
26891     /**
26892      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26893      * @param {Mixed} value The value to set
26894      */
26895     
26896     
26897     setValue : function(value)
26898     {
26899         //console.log('setValue: ' + value);
26900         
26901         if(typeof(value) == 'undefined') { // not sure why this is happending...
26902             return;
26903         }
26904         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26905         
26906         //if(!this.el || !this.getEditor()) {
26907         //    this.value = value;
26908             //this.setValue.defer(100,this,[value]);    
26909         //    return;
26910         //} 
26911         
26912         if(!this.getEditor()) {
26913             return;
26914         }
26915         
26916         this.getEditor().SetData(value);
26917         
26918         //
26919
26920     },
26921
26922     /**
26923      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26924      * @return {Mixed} value The field value
26925      */
26926     getValue : function()
26927     {
26928         
26929         if (this.frame && this.frame.dom.style.display == 'none') {
26930             return Roo.form.FCKeditor.superclass.getValue.call(this);
26931         }
26932         
26933         if(!this.el || !this.getEditor()) {
26934            
26935            // this.getValue.defer(100,this); 
26936             return this.value;
26937         }
26938        
26939         
26940         var value=this.getEditor().GetData();
26941         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26942         return Roo.form.FCKeditor.superclass.getValue.call(this);
26943         
26944
26945     },
26946
26947     /**
26948      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26949      * @return {Mixed} value The field value
26950      */
26951     getRawValue : function()
26952     {
26953         if (this.frame && this.frame.dom.style.display == 'none') {
26954             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26955         }
26956         
26957         if(!this.el || !this.getEditor()) {
26958             //this.getRawValue.defer(100,this); 
26959             return this.value;
26960             return;
26961         }
26962         
26963         
26964         
26965         var value=this.getEditor().GetData();
26966         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26967         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26968          
26969     },
26970     
26971     setSize : function(w,h) {
26972         
26973         
26974         
26975         //if (this.frame && this.frame.dom.style.display == 'none') {
26976         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26977         //    return;
26978         //}
26979         //if(!this.el || !this.getEditor()) {
26980         //    this.setSize.defer(100,this, [w,h]); 
26981         //    return;
26982         //}
26983         
26984         
26985         
26986         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26987         
26988         this.frame.dom.setAttribute('width', w);
26989         this.frame.dom.setAttribute('height', h);
26990         this.frame.setSize(w,h);
26991         
26992     },
26993     
26994     toggleSourceEdit : function(value) {
26995         
26996       
26997          
26998         this.el.dom.style.display = value ? '' : 'none';
26999         this.frame.dom.style.display = value ?  'none' : '';
27000         
27001     },
27002     
27003     
27004     focus: function(tag)
27005     {
27006         if (this.frame.dom.style.display == 'none') {
27007             return Roo.form.FCKeditor.superclass.focus.call(this);
27008         }
27009         if(!this.el || !this.getEditor()) {
27010             this.focus.defer(100,this, [tag]); 
27011             return;
27012         }
27013         
27014         
27015         
27016         
27017         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27018         this.getEditor().Focus();
27019         if (tgs.length) {
27020             if (!this.getEditor().Selection.GetSelection()) {
27021                 this.focus.defer(100,this, [tag]); 
27022                 return;
27023             }
27024             
27025             
27026             var r = this.getEditor().EditorDocument.createRange();
27027             r.setStart(tgs[0],0);
27028             r.setEnd(tgs[0],0);
27029             this.getEditor().Selection.GetSelection().removeAllRanges();
27030             this.getEditor().Selection.GetSelection().addRange(r);
27031             this.getEditor().Focus();
27032         }
27033         
27034     },
27035     
27036     
27037     
27038     replaceTextarea : function()
27039     {
27040         if ( document.getElementById( this.getId() + '___Frame' ) ) {
27041             return ;
27042         }
27043         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27044         //{
27045             // We must check the elements firstly using the Id and then the name.
27046         var oTextarea = document.getElementById( this.getId() );
27047         
27048         var colElementsByName = document.getElementsByName( this.getId() ) ;
27049          
27050         oTextarea.style.display = 'none' ;
27051
27052         if ( oTextarea.tabIndex ) {            
27053             this.TabIndex = oTextarea.tabIndex ;
27054         }
27055         
27056         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27057         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27058         this.frame = Roo.get(this.getId() + '___Frame')
27059     },
27060     
27061     _getConfigHtml : function()
27062     {
27063         var sConfig = '' ;
27064
27065         for ( var o in this.fckconfig ) {
27066             sConfig += sConfig.length > 0  ? '&amp;' : '';
27067             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27068         }
27069
27070         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27071     },
27072     
27073     
27074     _getIFrameHtml : function()
27075     {
27076         var sFile = 'fckeditor.html' ;
27077         /* no idea what this is about..
27078         try
27079         {
27080             if ( (/fcksource=true/i).test( window.top.location.search ) )
27081                 sFile = 'fckeditor.original.html' ;
27082         }
27083         catch (e) { 
27084         */
27085
27086         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27087         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27088         
27089         
27090         var html = '<iframe id="' + this.getId() +
27091             '___Frame" src="' + sLink +
27092             '" width="' + this.width +
27093             '" height="' + this.height + '"' +
27094             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27095             ' frameborder="0" scrolling="no"></iframe>' ;
27096
27097         return html ;
27098     },
27099     
27100     _insertHtmlBefore : function( html, element )
27101     {
27102         if ( element.insertAdjacentHTML )       {
27103             // IE
27104             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27105         } else { // Gecko
27106             var oRange = document.createRange() ;
27107             oRange.setStartBefore( element ) ;
27108             var oFragment = oRange.createContextualFragment( html );
27109             element.parentNode.insertBefore( oFragment, element ) ;
27110         }
27111     }
27112     
27113     
27114   
27115     
27116     
27117     
27118     
27119
27120 });
27121
27122 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27123
27124 function FCKeditor_OnComplete(editorInstance){
27125     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27126     f.fckEditor = editorInstance;
27127     //console.log("loaded");
27128     f.fireEvent('editorinit', f, editorInstance);
27129
27130   
27131
27132  
27133
27134
27135
27136
27137
27138
27139
27140
27141
27142
27143
27144
27145
27146
27147
27148 //<script type="text/javascript">
27149 /**
27150  * @class Roo.form.GridField
27151  * @extends Roo.form.Field
27152  * Embed a grid (or editable grid into a form)
27153  * STATUS ALPHA
27154  * 
27155  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27156  * it needs 
27157  * xgrid.store = Roo.data.Store
27158  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27159  * xgrid.store.reader = Roo.data.JsonReader 
27160  * 
27161  * 
27162  * @constructor
27163  * Creates a new GridField
27164  * @param {Object} config Configuration options
27165  */
27166 Roo.form.GridField = function(config){
27167     Roo.form.GridField.superclass.constructor.call(this, config);
27168      
27169 };
27170
27171 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27172     /**
27173      * @cfg {Number} width  - used to restrict width of grid..
27174      */
27175     width : 100,
27176     /**
27177      * @cfg {Number} height - used to restrict height of grid..
27178      */
27179     height : 50,
27180      /**
27181      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27182          * 
27183          *}
27184      */
27185     xgrid : false, 
27186     /**
27187      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27188      * {tag: "input", type: "checkbox", autocomplete: "off"})
27189      */
27190    // defaultAutoCreate : { tag: 'div' },
27191     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
27192     /**
27193      * @cfg {String} addTitle Text to include for adding a title.
27194      */
27195     addTitle : false,
27196     //
27197     onResize : function(){
27198         Roo.form.Field.superclass.onResize.apply(this, arguments);
27199     },
27200
27201     initEvents : function(){
27202         // Roo.form.Checkbox.superclass.initEvents.call(this);
27203         // has no events...
27204        
27205     },
27206
27207
27208     getResizeEl : function(){
27209         return this.wrap;
27210     },
27211
27212     getPositionEl : function(){
27213         return this.wrap;
27214     },
27215
27216     // private
27217     onRender : function(ct, position){
27218         
27219         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27220         var style = this.style;
27221         delete this.style;
27222         
27223         Roo.form.GridField.superclass.onRender.call(this, ct, position);
27224         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27225         this.viewEl = this.wrap.createChild({ tag: 'div' });
27226         if (style) {
27227             this.viewEl.applyStyles(style);
27228         }
27229         if (this.width) {
27230             this.viewEl.setWidth(this.width);
27231         }
27232         if (this.height) {
27233             this.viewEl.setHeight(this.height);
27234         }
27235         //if(this.inputValue !== undefined){
27236         //this.setValue(this.value);
27237         
27238         
27239         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27240         
27241         
27242         this.grid.render();
27243         this.grid.getDataSource().on('remove', this.refreshValue, this);
27244         this.grid.getDataSource().on('update', this.refreshValue, this);
27245         this.grid.on('afteredit', this.refreshValue, this);
27246  
27247     },
27248      
27249     
27250     /**
27251      * Sets the value of the item. 
27252      * @param {String} either an object  or a string..
27253      */
27254     setValue : function(v){
27255         //this.value = v;
27256         v = v || []; // empty set..
27257         // this does not seem smart - it really only affects memoryproxy grids..
27258         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27259             var ds = this.grid.getDataSource();
27260             // assumes a json reader..
27261             var data = {}
27262             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27263             ds.loadData( data);
27264         }
27265         // clear selection so it does not get stale.
27266         if (this.grid.sm) { 
27267             this.grid.sm.clearSelections();
27268         }
27269         
27270         Roo.form.GridField.superclass.setValue.call(this, v);
27271         this.refreshValue();
27272         // should load data in the grid really....
27273     },
27274     
27275     // private
27276     refreshValue: function() {
27277          var val = [];
27278         this.grid.getDataSource().each(function(r) {
27279             val.push(r.data);
27280         });
27281         this.el.dom.value = Roo.encode(val);
27282     }
27283     
27284      
27285     
27286     
27287 });/*
27288  * Based on:
27289  * Ext JS Library 1.1.1
27290  * Copyright(c) 2006-2007, Ext JS, LLC.
27291  *
27292  * Originally Released Under LGPL - original licence link has changed is not relivant.
27293  *
27294  * Fork - LGPL
27295  * <script type="text/javascript">
27296  */
27297 /**
27298  * @class Roo.form.DisplayField
27299  * @extends Roo.form.Field
27300  * A generic Field to display non-editable data.
27301  * @cfg {Boolean} closable (true|false) default false
27302  * @constructor
27303  * Creates a new Display Field item.
27304  * @param {Object} config Configuration options
27305  */
27306 Roo.form.DisplayField = function(config){
27307     Roo.form.DisplayField.superclass.constructor.call(this, config);
27308     
27309     this.addEvents({
27310         /**
27311          * @event close
27312          * Fires after the click the close btn
27313              * @param {Roo.form.DisplayField} this
27314              */
27315         close : true
27316     });
27317 };
27318
27319 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27320     inputType:      'hidden',
27321     allowBlank:     true,
27322     readOnly:         true,
27323     
27324  
27325     /**
27326      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27327      */
27328     focusClass : undefined,
27329     /**
27330      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27331      */
27332     fieldClass: 'x-form-field',
27333     
27334      /**
27335      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27336      */
27337     valueRenderer: undefined,
27338     
27339     width: 100,
27340     /**
27341      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27342      * {tag: "input", type: "checkbox", autocomplete: "off"})
27343      */
27344      
27345  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27346  
27347     closable : false,
27348     
27349     onResize : function(){
27350         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27351         
27352     },
27353
27354     initEvents : function(){
27355         // Roo.form.Checkbox.superclass.initEvents.call(this);
27356         // has no events...
27357         
27358         if(this.closable){
27359             this.closeEl.on('click', this.onClose, this);
27360         }
27361        
27362     },
27363
27364
27365     getResizeEl : function(){
27366         return this.wrap;
27367     },
27368
27369     getPositionEl : function(){
27370         return this.wrap;
27371     },
27372
27373     // private
27374     onRender : function(ct, position){
27375         
27376         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27377         //if(this.inputValue !== undefined){
27378         this.wrap = this.el.wrap();
27379         
27380         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27381         
27382         if(this.closable){
27383             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27384         }
27385         
27386         if (this.bodyStyle) {
27387             this.viewEl.applyStyles(this.bodyStyle);
27388         }
27389         //this.viewEl.setStyle('padding', '2px');
27390         
27391         this.setValue(this.value);
27392         
27393     },
27394 /*
27395     // private
27396     initValue : Roo.emptyFn,
27397
27398   */
27399
27400         // private
27401     onClick : function(){
27402         
27403     },
27404
27405     /**
27406      * Sets the checked state of the checkbox.
27407      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27408      */
27409     setValue : function(v){
27410         this.value = v;
27411         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27412         // this might be called before we have a dom element..
27413         if (!this.viewEl) {
27414             return;
27415         }
27416         this.viewEl.dom.innerHTML = html;
27417         Roo.form.DisplayField.superclass.setValue.call(this, v);
27418
27419     },
27420     
27421     onClose : function(e)
27422     {
27423         e.preventDefault();
27424         
27425         this.fireEvent('close', this);
27426     }
27427 });/*
27428  * 
27429  * Licence- LGPL
27430  * 
27431  */
27432
27433 /**
27434  * @class Roo.form.DayPicker
27435  * @extends Roo.form.Field
27436  * A Day picker show [M] [T] [W] ....
27437  * @constructor
27438  * Creates a new Day Picker
27439  * @param {Object} config Configuration options
27440  */
27441 Roo.form.DayPicker= function(config){
27442     Roo.form.DayPicker.superclass.constructor.call(this, config);
27443      
27444 };
27445
27446 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27447     /**
27448      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27449      */
27450     focusClass : undefined,
27451     /**
27452      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27453      */
27454     fieldClass: "x-form-field",
27455    
27456     /**
27457      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27458      * {tag: "input", type: "checkbox", autocomplete: "off"})
27459      */
27460     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27461     
27462    
27463     actionMode : 'viewEl', 
27464     //
27465     // private
27466  
27467     inputType : 'hidden',
27468     
27469      
27470     inputElement: false, // real input element?
27471     basedOn: false, // ????
27472     
27473     isFormField: true, // not sure where this is needed!!!!
27474
27475     onResize : function(){
27476         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27477         if(!this.boxLabel){
27478             this.el.alignTo(this.wrap, 'c-c');
27479         }
27480     },
27481
27482     initEvents : function(){
27483         Roo.form.Checkbox.superclass.initEvents.call(this);
27484         this.el.on("click", this.onClick,  this);
27485         this.el.on("change", this.onClick,  this);
27486     },
27487
27488
27489     getResizeEl : function(){
27490         return this.wrap;
27491     },
27492
27493     getPositionEl : function(){
27494         return this.wrap;
27495     },
27496
27497     
27498     // private
27499     onRender : function(ct, position){
27500         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27501        
27502         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27503         
27504         var r1 = '<table><tr>';
27505         var r2 = '<tr class="x-form-daypick-icons">';
27506         for (var i=0; i < 7; i++) {
27507             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27508             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27509         }
27510         
27511         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27512         viewEl.select('img').on('click', this.onClick, this);
27513         this.viewEl = viewEl;   
27514         
27515         
27516         // this will not work on Chrome!!!
27517         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27518         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27519         
27520         
27521           
27522
27523     },
27524
27525     // private
27526     initValue : Roo.emptyFn,
27527
27528     /**
27529      * Returns the checked state of the checkbox.
27530      * @return {Boolean} True if checked, else false
27531      */
27532     getValue : function(){
27533         return this.el.dom.value;
27534         
27535     },
27536
27537         // private
27538     onClick : function(e){ 
27539         //this.setChecked(!this.checked);
27540         Roo.get(e.target).toggleClass('x-menu-item-checked');
27541         this.refreshValue();
27542         //if(this.el.dom.checked != this.checked){
27543         //    this.setValue(this.el.dom.checked);
27544        // }
27545     },
27546     
27547     // private
27548     refreshValue : function()
27549     {
27550         var val = '';
27551         this.viewEl.select('img',true).each(function(e,i,n)  {
27552             val += e.is(".x-menu-item-checked") ? String(n) : '';
27553         });
27554         this.setValue(val, true);
27555     },
27556
27557     /**
27558      * Sets the checked state of the checkbox.
27559      * On is always based on a string comparison between inputValue and the param.
27560      * @param {Boolean/String} value - the value to set 
27561      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27562      */
27563     setValue : function(v,suppressEvent){
27564         if (!this.el.dom) {
27565             return;
27566         }
27567         var old = this.el.dom.value ;
27568         this.el.dom.value = v;
27569         if (suppressEvent) {
27570             return ;
27571         }
27572          
27573         // update display..
27574         this.viewEl.select('img',true).each(function(e,i,n)  {
27575             
27576             var on = e.is(".x-menu-item-checked");
27577             var newv = v.indexOf(String(n)) > -1;
27578             if (on != newv) {
27579                 e.toggleClass('x-menu-item-checked');
27580             }
27581             
27582         });
27583         
27584         
27585         this.fireEvent('change', this, v, old);
27586         
27587         
27588     },
27589    
27590     // handle setting of hidden value by some other method!!?!?
27591     setFromHidden: function()
27592     {
27593         if(!this.el){
27594             return;
27595         }
27596         //console.log("SET FROM HIDDEN");
27597         //alert('setFrom hidden');
27598         this.setValue(this.el.dom.value);
27599     },
27600     
27601     onDestroy : function()
27602     {
27603         if(this.viewEl){
27604             Roo.get(this.viewEl).remove();
27605         }
27606          
27607         Roo.form.DayPicker.superclass.onDestroy.call(this);
27608     }
27609
27610 });/*
27611  * RooJS Library 1.1.1
27612  * Copyright(c) 2008-2011  Alan Knowles
27613  *
27614  * License - LGPL
27615  */
27616  
27617
27618 /**
27619  * @class Roo.form.ComboCheck
27620  * @extends Roo.form.ComboBox
27621  * A combobox for multiple select items.
27622  *
27623  * FIXME - could do with a reset button..
27624  * 
27625  * @constructor
27626  * Create a new ComboCheck
27627  * @param {Object} config Configuration options
27628  */
27629 Roo.form.ComboCheck = function(config){
27630     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27631     // should verify some data...
27632     // like
27633     // hiddenName = required..
27634     // displayField = required
27635     // valudField == required
27636     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27637     var _t = this;
27638     Roo.each(req, function(e) {
27639         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27640             throw "Roo.form.ComboCheck : missing value for: " + e;
27641         }
27642     });
27643     
27644     
27645 };
27646
27647 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27648      
27649      
27650     editable : false,
27651      
27652     selectedClass: 'x-menu-item-checked', 
27653     
27654     // private
27655     onRender : function(ct, position){
27656         var _t = this;
27657         
27658         
27659         
27660         if(!this.tpl){
27661             var cls = 'x-combo-list';
27662
27663             
27664             this.tpl =  new Roo.Template({
27665                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27666                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27667                    '<span>{' + this.displayField + '}</span>' +
27668                     '</div>' 
27669                 
27670             });
27671         }
27672  
27673         
27674         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27675         this.view.singleSelect = false;
27676         this.view.multiSelect = true;
27677         this.view.toggleSelect = true;
27678         this.pageTb.add(new Roo.Toolbar.Fill(), {
27679             
27680             text: 'Done',
27681             handler: function()
27682             {
27683                 _t.collapse();
27684             }
27685         });
27686     },
27687     
27688     onViewOver : function(e, t){
27689         // do nothing...
27690         return;
27691         
27692     },
27693     
27694     onViewClick : function(doFocus,index){
27695         return;
27696         
27697     },
27698     select: function () {
27699         //Roo.log("SELECT CALLED");
27700     },
27701      
27702     selectByValue : function(xv, scrollIntoView){
27703         var ar = this.getValueArray();
27704         var sels = [];
27705         
27706         Roo.each(ar, function(v) {
27707             if(v === undefined || v === null){
27708                 return;
27709             }
27710             var r = this.findRecord(this.valueField, v);
27711             if(r){
27712                 sels.push(this.store.indexOf(r))
27713                 
27714             }
27715         },this);
27716         this.view.select(sels);
27717         return false;
27718     },
27719     
27720     
27721     
27722     onSelect : function(record, index){
27723        // Roo.log("onselect Called");
27724        // this is only called by the clear button now..
27725         this.view.clearSelections();
27726         this.setValue('[]');
27727         if (this.value != this.valueBefore) {
27728             this.fireEvent('change', this, this.value, this.valueBefore);
27729             this.valueBefore = this.value;
27730         }
27731     },
27732     getValueArray : function()
27733     {
27734         var ar = [] ;
27735         
27736         try {
27737             //Roo.log(this.value);
27738             if (typeof(this.value) == 'undefined') {
27739                 return [];
27740             }
27741             var ar = Roo.decode(this.value);
27742             return  ar instanceof Array ? ar : []; //?? valid?
27743             
27744         } catch(e) {
27745             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27746             return [];
27747         }
27748          
27749     },
27750     expand : function ()
27751     {
27752         
27753         Roo.form.ComboCheck.superclass.expand.call(this);
27754         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27755         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27756         
27757
27758     },
27759     
27760     collapse : function(){
27761         Roo.form.ComboCheck.superclass.collapse.call(this);
27762         var sl = this.view.getSelectedIndexes();
27763         var st = this.store;
27764         var nv = [];
27765         var tv = [];
27766         var r;
27767         Roo.each(sl, function(i) {
27768             r = st.getAt(i);
27769             nv.push(r.get(this.valueField));
27770         },this);
27771         this.setValue(Roo.encode(nv));
27772         if (this.value != this.valueBefore) {
27773
27774             this.fireEvent('change', this, this.value, this.valueBefore);
27775             this.valueBefore = this.value;
27776         }
27777         
27778     },
27779     
27780     setValue : function(v){
27781         // Roo.log(v);
27782         this.value = v;
27783         
27784         var vals = this.getValueArray();
27785         var tv = [];
27786         Roo.each(vals, function(k) {
27787             var r = this.findRecord(this.valueField, k);
27788             if(r){
27789                 tv.push(r.data[this.displayField]);
27790             }else if(this.valueNotFoundText !== undefined){
27791                 tv.push( this.valueNotFoundText );
27792             }
27793         },this);
27794        // Roo.log(tv);
27795         
27796         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27797         this.hiddenField.value = v;
27798         this.value = v;
27799     }
27800     
27801 });/*
27802  * Based on:
27803  * Ext JS Library 1.1.1
27804  * Copyright(c) 2006-2007, Ext JS, LLC.
27805  *
27806  * Originally Released Under LGPL - original licence link has changed is not relivant.
27807  *
27808  * Fork - LGPL
27809  * <script type="text/javascript">
27810  */
27811  
27812 /**
27813  * @class Roo.form.Signature
27814  * @extends Roo.form.Field
27815  * Signature field.  
27816  * @constructor
27817  * 
27818  * @param {Object} config Configuration options
27819  */
27820
27821 Roo.form.Signature = function(config){
27822     Roo.form.Signature.superclass.constructor.call(this, config);
27823     
27824     this.addEvents({// not in used??
27825          /**
27826          * @event confirm
27827          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27828              * @param {Roo.form.Signature} combo This combo box
27829              */
27830         'confirm' : true,
27831         /**
27832          * @event reset
27833          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27834              * @param {Roo.form.ComboBox} combo This combo box
27835              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27836              */
27837         'reset' : true
27838     });
27839 };
27840
27841 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27842     /**
27843      * @cfg {Object} labels Label to use when rendering a form.
27844      * defaults to 
27845      * labels : { 
27846      *      clear : "Clear",
27847      *      confirm : "Confirm"
27848      *  }
27849      */
27850     labels : { 
27851         clear : "Clear",
27852         confirm : "Confirm"
27853     },
27854     /**
27855      * @cfg {Number} width The signature panel width (defaults to 300)
27856      */
27857     width: 300,
27858     /**
27859      * @cfg {Number} height The signature panel height (defaults to 100)
27860      */
27861     height : 100,
27862     /**
27863      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27864      */
27865     allowBlank : false,
27866     
27867     //private
27868     // {Object} signPanel The signature SVG panel element (defaults to {})
27869     signPanel : {},
27870     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27871     isMouseDown : false,
27872     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27873     isConfirmed : false,
27874     // {String} signatureTmp SVG mapping string (defaults to empty string)
27875     signatureTmp : '',
27876     
27877     
27878     defaultAutoCreate : { // modified by initCompnoent..
27879         tag: "input",
27880         type:"hidden"
27881     },
27882
27883     // private
27884     onRender : function(ct, position){
27885         
27886         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27887         
27888         this.wrap = this.el.wrap({
27889             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27890         });
27891         
27892         this.createToolbar(this);
27893         this.signPanel = this.wrap.createChild({
27894                 tag: 'div',
27895                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27896             }, this.el
27897         );
27898             
27899         this.svgID = Roo.id();
27900         this.svgEl = this.signPanel.createChild({
27901               xmlns : 'http://www.w3.org/2000/svg',
27902               tag : 'svg',
27903               id : this.svgID + "-svg",
27904               width: this.width,
27905               height: this.height,
27906               viewBox: '0 0 '+this.width+' '+this.height,
27907               cn : [
27908                 {
27909                     tag: "rect",
27910                     id: this.svgID + "-svg-r",
27911                     width: this.width,
27912                     height: this.height,
27913                     fill: "#ffa"
27914                 },
27915                 {
27916                     tag: "line",
27917                     id: this.svgID + "-svg-l",
27918                     x1: "0", // start
27919                     y1: (this.height*0.8), // start set the line in 80% of height
27920                     x2: this.width, // end
27921                     y2: (this.height*0.8), // end set the line in 80% of height
27922                     'stroke': "#666",
27923                     'stroke-width': "1",
27924                     'stroke-dasharray': "3",
27925                     'shape-rendering': "crispEdges",
27926                     'pointer-events': "none"
27927                 },
27928                 {
27929                     tag: "path",
27930                     id: this.svgID + "-svg-p",
27931                     'stroke': "navy",
27932                     'stroke-width': "3",
27933                     'fill': "none",
27934                     'pointer-events': 'none'
27935                 }
27936               ]
27937         });
27938         this.createSVG();
27939         this.svgBox = this.svgEl.dom.getScreenCTM();
27940     },
27941     createSVG : function(){ 
27942         var svg = this.signPanel;
27943         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27944         var t = this;
27945
27946         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27947         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27948         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27949         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27950         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27951         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27952         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27953         
27954     },
27955     isTouchEvent : function(e){
27956         return e.type.match(/^touch/);
27957     },
27958     getCoords : function (e) {
27959         var pt    = this.svgEl.dom.createSVGPoint();
27960         pt.x = e.clientX; 
27961         pt.y = e.clientY;
27962         if (this.isTouchEvent(e)) {
27963             pt.x =  e.targetTouches[0].clientX;
27964             pt.y = e.targetTouches[0].clientY;
27965         }
27966         var a = this.svgEl.dom.getScreenCTM();
27967         var b = a.inverse();
27968         var mx = pt.matrixTransform(b);
27969         return mx.x + ',' + mx.y;
27970     },
27971     //mouse event headler 
27972     down : function (e) {
27973         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27974         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27975         
27976         this.isMouseDown = true;
27977         
27978         e.preventDefault();
27979     },
27980     move : function (e) {
27981         if (this.isMouseDown) {
27982             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27983             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27984         }
27985         
27986         e.preventDefault();
27987     },
27988     up : function (e) {
27989         this.isMouseDown = false;
27990         var sp = this.signatureTmp.split(' ');
27991         
27992         if(sp.length > 1){
27993             if(!sp[sp.length-2].match(/^L/)){
27994                 sp.pop();
27995                 sp.pop();
27996                 sp.push("");
27997                 this.signatureTmp = sp.join(" ");
27998             }
27999         }
28000         if(this.getValue() != this.signatureTmp){
28001             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28002             this.isConfirmed = false;
28003         }
28004         e.preventDefault();
28005     },
28006     
28007     /**
28008      * Protected method that will not generally be called directly. It
28009      * is called when the editor creates its toolbar. Override this method if you need to
28010      * add custom toolbar buttons.
28011      * @param {HtmlEditor} editor
28012      */
28013     createToolbar : function(editor){
28014          function btn(id, toggle, handler){
28015             var xid = fid + '-'+ id ;
28016             return {
28017                 id : xid,
28018                 cmd : id,
28019                 cls : 'x-btn-icon x-edit-'+id,
28020                 enableToggle:toggle !== false,
28021                 scope: editor, // was editor...
28022                 handler:handler||editor.relayBtnCmd,
28023                 clickEvent:'mousedown',
28024                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28025                 tabIndex:-1
28026             };
28027         }
28028         
28029         
28030         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
28031         this.tb = tb;
28032         this.tb.add(
28033            {
28034                 cls : ' x-signature-btn x-signature-'+id,
28035                 scope: editor, // was editor...
28036                 handler: this.reset,
28037                 clickEvent:'mousedown',
28038                 text: this.labels.clear
28039             },
28040             {
28041                  xtype : 'Fill',
28042                  xns: Roo.Toolbar
28043             }, 
28044             {
28045                 cls : '  x-signature-btn x-signature-'+id,
28046                 scope: editor, // was editor...
28047                 handler: this.confirmHandler,
28048                 clickEvent:'mousedown',
28049                 text: this.labels.confirm
28050             }
28051         );
28052     
28053     },
28054     //public
28055     /**
28056      * when user is clicked confirm then show this image.....
28057      * 
28058      * @return {String} Image Data URI
28059      */
28060     getImageDataURI : function(){
28061         var svg = this.svgEl.dom.parentNode.innerHTML;
28062         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
28063         return src; 
28064     },
28065     /**
28066      * 
28067      * @return {Boolean} this.isConfirmed
28068      */
28069     getConfirmed : function(){
28070         return this.isConfirmed;
28071     },
28072     /**
28073      * 
28074      * @return {Number} this.width
28075      */
28076     getWidth : function(){
28077         return this.width;
28078     },
28079     /**
28080      * 
28081      * @return {Number} this.height
28082      */
28083     getHeight : function(){
28084         return this.height;
28085     },
28086     // private
28087     getSignature : function(){
28088         return this.signatureTmp;
28089     },
28090     // private
28091     reset : function(){
28092         this.signatureTmp = '';
28093         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28094         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
28095         this.isConfirmed = false;
28096         Roo.form.Signature.superclass.reset.call(this);
28097     },
28098     setSignature : function(s){
28099         this.signatureTmp = s;
28100         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28101         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
28102         this.setValue(s);
28103         this.isConfirmed = false;
28104         Roo.form.Signature.superclass.reset.call(this);
28105     }, 
28106     test : function(){
28107 //        Roo.log(this.signPanel.dom.contentWindow.up())
28108     },
28109     //private
28110     setConfirmed : function(){
28111         
28112         
28113         
28114 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
28115     },
28116     // private
28117     confirmHandler : function(){
28118         if(!this.getSignature()){
28119             return;
28120         }
28121         
28122         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
28123         this.setValue(this.getSignature());
28124         this.isConfirmed = true;
28125         
28126         this.fireEvent('confirm', this);
28127     },
28128     // private
28129     // Subclasses should provide the validation implementation by overriding this
28130     validateValue : function(value){
28131         if(this.allowBlank){
28132             return true;
28133         }
28134         
28135         if(this.isConfirmed){
28136             return true;
28137         }
28138         return false;
28139     }
28140 });/*
28141  * Based on:
28142  * Ext JS Library 1.1.1
28143  * Copyright(c) 2006-2007, Ext JS, LLC.
28144  *
28145  * Originally Released Under LGPL - original licence link has changed is not relivant.
28146  *
28147  * Fork - LGPL
28148  * <script type="text/javascript">
28149  */
28150  
28151
28152 /**
28153  * @class Roo.form.ComboBox
28154  * @extends Roo.form.TriggerField
28155  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
28156  * @constructor
28157  * Create a new ComboBox.
28158  * @param {Object} config Configuration options
28159  */
28160 Roo.form.Select = function(config){
28161     Roo.form.Select.superclass.constructor.call(this, config);
28162      
28163 };
28164
28165 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
28166     /**
28167      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
28168      */
28169     /**
28170      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
28171      * rendering into an Roo.Editor, defaults to false)
28172      */
28173     /**
28174      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
28175      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
28176      */
28177     /**
28178      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
28179      */
28180     /**
28181      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
28182      * the dropdown list (defaults to undefined, with no header element)
28183      */
28184
28185      /**
28186      * @cfg {String/Roo.Template} tpl The template to use to render the output
28187      */
28188      
28189     // private
28190     defaultAutoCreate : {tag: "select"  },
28191     /**
28192      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
28193      */
28194     listWidth: undefined,
28195     /**
28196      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
28197      * mode = 'remote' or 'text' if mode = 'local')
28198      */
28199     displayField: undefined,
28200     /**
28201      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
28202      * mode = 'remote' or 'value' if mode = 'local'). 
28203      * Note: use of a valueField requires the user make a selection
28204      * in order for a value to be mapped.
28205      */
28206     valueField: undefined,
28207     
28208     
28209     /**
28210      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
28211      * field's data value (defaults to the underlying DOM element's name)
28212      */
28213     hiddenName: undefined,
28214     /**
28215      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
28216      */
28217     listClass: '',
28218     /**
28219      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
28220      */
28221     selectedClass: 'x-combo-selected',
28222     /**
28223      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
28224      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
28225      * which displays a downward arrow icon).
28226      */
28227     triggerClass : 'x-form-arrow-trigger',
28228     /**
28229      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28230      */
28231     shadow:'sides',
28232     /**
28233      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
28234      * anchor positions (defaults to 'tl-bl')
28235      */
28236     listAlign: 'tl-bl?',
28237     /**
28238      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
28239      */
28240     maxHeight: 300,
28241     /**
28242      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
28243      * query specified by the allQuery config option (defaults to 'query')
28244      */
28245     triggerAction: 'query',
28246     /**
28247      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
28248      * (defaults to 4, does not apply if editable = false)
28249      */
28250     minChars : 4,
28251     /**
28252      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28253      * delay (typeAheadDelay) if it matches a known value (defaults to false)
28254      */
28255     typeAhead: false,
28256     /**
28257      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28258      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28259      */
28260     queryDelay: 500,
28261     /**
28262      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28263      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28264      */
28265     pageSize: 0,
28266     /**
28267      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28268      * when editable = true (defaults to false)
28269      */
28270     selectOnFocus:false,
28271     /**
28272      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28273      */
28274     queryParam: 'query',
28275     /**
28276      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28277      * when mode = 'remote' (defaults to 'Loading...')
28278      */
28279     loadingText: 'Loading...',
28280     /**
28281      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28282      */
28283     resizable: false,
28284     /**
28285      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28286      */
28287     handleHeight : 8,
28288     /**
28289      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28290      * traditional select (defaults to true)
28291      */
28292     editable: true,
28293     /**
28294      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28295      */
28296     allQuery: '',
28297     /**
28298      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28299      */
28300     mode: 'remote',
28301     /**
28302      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28303      * listWidth has a higher value)
28304      */
28305     minListWidth : 70,
28306     /**
28307      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28308      * allow the user to set arbitrary text into the field (defaults to false)
28309      */
28310     forceSelection:false,
28311     /**
28312      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28313      * if typeAhead = true (defaults to 250)
28314      */
28315     typeAheadDelay : 250,
28316     /**
28317      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28318      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28319      */
28320     valueNotFoundText : undefined,
28321     
28322     /**
28323      * @cfg {String} defaultValue The value displayed after loading the store.
28324      */
28325     defaultValue: '',
28326     
28327     /**
28328      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28329      */
28330     blockFocus : false,
28331     
28332     /**
28333      * @cfg {Boolean} disableClear Disable showing of clear button.
28334      */
28335     disableClear : false,
28336     /**
28337      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28338      */
28339     alwaysQuery : false,
28340     
28341     //private
28342     addicon : false,
28343     editicon: false,
28344     
28345     // element that contains real text value.. (when hidden is used..)
28346      
28347     // private
28348     onRender : function(ct, position){
28349         Roo.form.Field.prototype.onRender.call(this, ct, position);
28350         
28351         if(this.store){
28352             this.store.on('beforeload', this.onBeforeLoad, this);
28353             this.store.on('load', this.onLoad, this);
28354             this.store.on('loadexception', this.onLoadException, this);
28355             this.store.load({});
28356         }
28357         
28358         
28359         
28360     },
28361
28362     // private
28363     initEvents : function(){
28364         //Roo.form.ComboBox.superclass.initEvents.call(this);
28365  
28366     },
28367
28368     onDestroy : function(){
28369        
28370         if(this.store){
28371             this.store.un('beforeload', this.onBeforeLoad, this);
28372             this.store.un('load', this.onLoad, this);
28373             this.store.un('loadexception', this.onLoadException, this);
28374         }
28375         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28376     },
28377
28378     // private
28379     fireKey : function(e){
28380         if(e.isNavKeyPress() && !this.list.isVisible()){
28381             this.fireEvent("specialkey", this, e);
28382         }
28383     },
28384
28385     // private
28386     onResize: function(w, h){
28387         
28388         return; 
28389     
28390         
28391     },
28392
28393     /**
28394      * Allow or prevent the user from directly editing the field text.  If false is passed,
28395      * the user will only be able to select from the items defined in the dropdown list.  This method
28396      * is the runtime equivalent of setting the 'editable' config option at config time.
28397      * @param {Boolean} value True to allow the user to directly edit the field text
28398      */
28399     setEditable : function(value){
28400          
28401     },
28402
28403     // private
28404     onBeforeLoad : function(){
28405         
28406         Roo.log("Select before load");
28407         return;
28408     
28409         this.innerList.update(this.loadingText ?
28410                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28411         //this.restrictHeight();
28412         this.selectedIndex = -1;
28413     },
28414
28415     // private
28416     onLoad : function(){
28417
28418     
28419         var dom = this.el.dom;
28420         dom.innerHTML = '';
28421          var od = dom.ownerDocument;
28422          
28423         if (this.emptyText) {
28424             var op = od.createElement('option');
28425             op.setAttribute('value', '');
28426             op.innerHTML = String.format('{0}', this.emptyText);
28427             dom.appendChild(op);
28428         }
28429         if(this.store.getCount() > 0){
28430            
28431             var vf = this.valueField;
28432             var df = this.displayField;
28433             this.store.data.each(function(r) {
28434                 // which colmsn to use... testing - cdoe / title..
28435                 var op = od.createElement('option');
28436                 op.setAttribute('value', r.data[vf]);
28437                 op.innerHTML = String.format('{0}', r.data[df]);
28438                 dom.appendChild(op);
28439             });
28440             if (typeof(this.defaultValue != 'undefined')) {
28441                 this.setValue(this.defaultValue);
28442             }
28443             
28444              
28445         }else{
28446             //this.onEmptyResults();
28447         }
28448         //this.el.focus();
28449     },
28450     // private
28451     onLoadException : function()
28452     {
28453         dom.innerHTML = '';
28454             
28455         Roo.log("Select on load exception");
28456         return;
28457     
28458         this.collapse();
28459         Roo.log(this.store.reader.jsonData);
28460         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28461             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28462         }
28463         
28464         
28465     },
28466     // private
28467     onTypeAhead : function(){
28468          
28469     },
28470
28471     // private
28472     onSelect : function(record, index){
28473         Roo.log('on select?');
28474         return;
28475         if(this.fireEvent('beforeselect', this, record, index) !== false){
28476             this.setFromData(index > -1 ? record.data : false);
28477             this.collapse();
28478             this.fireEvent('select', this, record, index);
28479         }
28480     },
28481
28482     /**
28483      * Returns the currently selected field value or empty string if no value is set.
28484      * @return {String} value The selected value
28485      */
28486     getValue : function(){
28487         var dom = this.el.dom;
28488         this.value = dom.options[dom.selectedIndex].value;
28489         return this.value;
28490         
28491     },
28492
28493     /**
28494      * Clears any text/value currently set in the field
28495      */
28496     clearValue : function(){
28497         this.value = '';
28498         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28499         
28500     },
28501
28502     /**
28503      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28504      * will be displayed in the field.  If the value does not match the data value of an existing item,
28505      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28506      * Otherwise the field will be blank (although the value will still be set).
28507      * @param {String} value The value to match
28508      */
28509     setValue : function(v){
28510         var d = this.el.dom;
28511         for (var i =0; i < d.options.length;i++) {
28512             if (v == d.options[i].value) {
28513                 d.selectedIndex = i;
28514                 this.value = v;
28515                 return;
28516             }
28517         }
28518         this.clearValue();
28519     },
28520     /**
28521      * @property {Object} the last set data for the element
28522      */
28523     
28524     lastData : false,
28525     /**
28526      * Sets the value of the field based on a object which is related to the record format for the store.
28527      * @param {Object} value the value to set as. or false on reset?
28528      */
28529     setFromData : function(o){
28530         Roo.log('setfrom data?');
28531          
28532         
28533         
28534     },
28535     // private
28536     reset : function(){
28537         this.clearValue();
28538     },
28539     // private
28540     findRecord : function(prop, value){
28541         
28542         return false;
28543     
28544         var record;
28545         if(this.store.getCount() > 0){
28546             this.store.each(function(r){
28547                 if(r.data[prop] == value){
28548                     record = r;
28549                     return false;
28550                 }
28551                 return true;
28552             });
28553         }
28554         return record;
28555     },
28556     
28557     getName: function()
28558     {
28559         // returns hidden if it's set..
28560         if (!this.rendered) {return ''};
28561         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28562         
28563     },
28564      
28565
28566     
28567
28568     // private
28569     onEmptyResults : function(){
28570         Roo.log('empty results');
28571         //this.collapse();
28572     },
28573
28574     /**
28575      * Returns true if the dropdown list is expanded, else false.
28576      */
28577     isExpanded : function(){
28578         return false;
28579     },
28580
28581     /**
28582      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28583      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28584      * @param {String} value The data value of the item to select
28585      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28586      * selected item if it is not currently in view (defaults to true)
28587      * @return {Boolean} True if the value matched an item in the list, else false
28588      */
28589     selectByValue : function(v, scrollIntoView){
28590         Roo.log('select By Value');
28591         return false;
28592     
28593         if(v !== undefined && v !== null){
28594             var r = this.findRecord(this.valueField || this.displayField, v);
28595             if(r){
28596                 this.select(this.store.indexOf(r), scrollIntoView);
28597                 return true;
28598             }
28599         }
28600         return false;
28601     },
28602
28603     /**
28604      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28605      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28606      * @param {Number} index The zero-based index of the list item to select
28607      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28608      * selected item if it is not currently in view (defaults to true)
28609      */
28610     select : function(index, scrollIntoView){
28611         Roo.log('select ');
28612         return  ;
28613         
28614         this.selectedIndex = index;
28615         this.view.select(index);
28616         if(scrollIntoView !== false){
28617             var el = this.view.getNode(index);
28618             if(el){
28619                 this.innerList.scrollChildIntoView(el, false);
28620             }
28621         }
28622     },
28623
28624       
28625
28626     // private
28627     validateBlur : function(){
28628         
28629         return;
28630         
28631     },
28632
28633     // private
28634     initQuery : function(){
28635         this.doQuery(this.getRawValue());
28636     },
28637
28638     // private
28639     doForce : function(){
28640         if(this.el.dom.value.length > 0){
28641             this.el.dom.value =
28642                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28643              
28644         }
28645     },
28646
28647     /**
28648      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28649      * query allowing the query action to be canceled if needed.
28650      * @param {String} query The SQL query to execute
28651      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28652      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28653      * saved in the current store (defaults to false)
28654      */
28655     doQuery : function(q, forceAll){
28656         
28657         Roo.log('doQuery?');
28658         if(q === undefined || q === null){
28659             q = '';
28660         }
28661         var qe = {
28662             query: q,
28663             forceAll: forceAll,
28664             combo: this,
28665             cancel:false
28666         };
28667         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28668             return false;
28669         }
28670         q = qe.query;
28671         forceAll = qe.forceAll;
28672         if(forceAll === true || (q.length >= this.minChars)){
28673             if(this.lastQuery != q || this.alwaysQuery){
28674                 this.lastQuery = q;
28675                 if(this.mode == 'local'){
28676                     this.selectedIndex = -1;
28677                     if(forceAll){
28678                         this.store.clearFilter();
28679                     }else{
28680                         this.store.filter(this.displayField, q);
28681                     }
28682                     this.onLoad();
28683                 }else{
28684                     this.store.baseParams[this.queryParam] = q;
28685                     this.store.load({
28686                         params: this.getParams(q)
28687                     });
28688                     this.expand();
28689                 }
28690             }else{
28691                 this.selectedIndex = -1;
28692                 this.onLoad();   
28693             }
28694         }
28695     },
28696
28697     // private
28698     getParams : function(q){
28699         var p = {};
28700         //p[this.queryParam] = q;
28701         if(this.pageSize){
28702             p.start = 0;
28703             p.limit = this.pageSize;
28704         }
28705         return p;
28706     },
28707
28708     /**
28709      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28710      */
28711     collapse : function(){
28712         
28713     },
28714
28715     // private
28716     collapseIf : function(e){
28717         
28718     },
28719
28720     /**
28721      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28722      */
28723     expand : function(){
28724         
28725     } ,
28726
28727     // private
28728      
28729
28730     /** 
28731     * @cfg {Boolean} grow 
28732     * @hide 
28733     */
28734     /** 
28735     * @cfg {Number} growMin 
28736     * @hide 
28737     */
28738     /** 
28739     * @cfg {Number} growMax 
28740     * @hide 
28741     */
28742     /**
28743      * @hide
28744      * @method autoSize
28745      */
28746     
28747     setWidth : function()
28748     {
28749         
28750     },
28751     getResizeEl : function(){
28752         return this.el;
28753     }
28754 });//<script type="text/javasscript">
28755  
28756
28757 /**
28758  * @class Roo.DDView
28759  * A DnD enabled version of Roo.View.
28760  * @param {Element/String} container The Element in which to create the View.
28761  * @param {String} tpl The template string used to create the markup for each element of the View
28762  * @param {Object} config The configuration properties. These include all the config options of
28763  * {@link Roo.View} plus some specific to this class.<br>
28764  * <p>
28765  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28766  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28767  * <p>
28768  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28769 .x-view-drag-insert-above {
28770         border-top:1px dotted #3366cc;
28771 }
28772 .x-view-drag-insert-below {
28773         border-bottom:1px dotted #3366cc;
28774 }
28775 </code></pre>
28776  * 
28777  */
28778  
28779 Roo.DDView = function(container, tpl, config) {
28780     Roo.DDView.superclass.constructor.apply(this, arguments);
28781     this.getEl().setStyle("outline", "0px none");
28782     this.getEl().unselectable();
28783     if (this.dragGroup) {
28784                 this.setDraggable(this.dragGroup.split(","));
28785     }
28786     if (this.dropGroup) {
28787                 this.setDroppable(this.dropGroup.split(","));
28788     }
28789     if (this.deletable) {
28790         this.setDeletable();
28791     }
28792     this.isDirtyFlag = false;
28793         this.addEvents({
28794                 "drop" : true
28795         });
28796 };
28797
28798 Roo.extend(Roo.DDView, Roo.View, {
28799 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28800 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28801 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28802 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28803
28804         isFormField: true,
28805
28806         reset: Roo.emptyFn,
28807         
28808         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28809
28810         validate: function() {
28811                 return true;
28812         },
28813         
28814         destroy: function() {
28815                 this.purgeListeners();
28816                 this.getEl.removeAllListeners();
28817                 this.getEl().remove();
28818                 if (this.dragZone) {
28819                         if (this.dragZone.destroy) {
28820                                 this.dragZone.destroy();
28821                         }
28822                 }
28823                 if (this.dropZone) {
28824                         if (this.dropZone.destroy) {
28825                                 this.dropZone.destroy();
28826                         }
28827                 }
28828         },
28829
28830 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28831         getName: function() {
28832                 return this.name;
28833         },
28834
28835 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28836         setValue: function(v) {
28837                 if (!this.store) {
28838                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28839                 }
28840                 var data = {};
28841                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28842                 this.store.proxy = new Roo.data.MemoryProxy(data);
28843                 this.store.load();
28844         },
28845
28846 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28847         getValue: function() {
28848                 var result = '(';
28849                 this.store.each(function(rec) {
28850                         result += rec.id + ',';
28851                 });
28852                 return result.substr(0, result.length - 1) + ')';
28853         },
28854         
28855         getIds: function() {
28856                 var i = 0, result = new Array(this.store.getCount());
28857                 this.store.each(function(rec) {
28858                         result[i++] = rec.id;
28859                 });
28860                 return result;
28861         },
28862         
28863         isDirty: function() {
28864                 return this.isDirtyFlag;
28865         },
28866
28867 /**
28868  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28869  *      whole Element becomes the target, and this causes the drop gesture to append.
28870  */
28871     getTargetFromEvent : function(e) {
28872                 var target = e.getTarget();
28873                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28874                 target = target.parentNode;
28875                 }
28876                 if (!target) {
28877                         target = this.el.dom.lastChild || this.el.dom;
28878                 }
28879                 return target;
28880     },
28881
28882 /**
28883  *      Create the drag data which consists of an object which has the property "ddel" as
28884  *      the drag proxy element. 
28885  */
28886     getDragData : function(e) {
28887         var target = this.findItemFromChild(e.getTarget());
28888                 if(target) {
28889                         this.handleSelection(e);
28890                         var selNodes = this.getSelectedNodes();
28891             var dragData = {
28892                 source: this,
28893                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28894                 nodes: selNodes,
28895                 records: []
28896                         };
28897                         var selectedIndices = this.getSelectedIndexes();
28898                         for (var i = 0; i < selectedIndices.length; i++) {
28899                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28900                         }
28901                         if (selNodes.length == 1) {
28902                                 dragData.ddel = target.cloneNode(true); // the div element
28903                         } else {
28904                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28905                                 div.className = 'multi-proxy';
28906                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28907                                         div.appendChild(selNodes[i].cloneNode(true));
28908                                 }
28909                                 dragData.ddel = div;
28910                         }
28911             //console.log(dragData)
28912             //console.log(dragData.ddel.innerHTML)
28913                         return dragData;
28914                 }
28915         //console.log('nodragData')
28916                 return false;
28917     },
28918     
28919 /**     Specify to which ddGroup items in this DDView may be dragged. */
28920     setDraggable: function(ddGroup) {
28921         if (ddGroup instanceof Array) {
28922                 Roo.each(ddGroup, this.setDraggable, this);
28923                 return;
28924         }
28925         if (this.dragZone) {
28926                 this.dragZone.addToGroup(ddGroup);
28927         } else {
28928                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28929                                 containerScroll: true,
28930                                 ddGroup: ddGroup 
28931
28932                         });
28933 //                      Draggability implies selection. DragZone's mousedown selects the element.
28934                         if (!this.multiSelect) { this.singleSelect = true; }
28935
28936 //                      Wire the DragZone's handlers up to methods in *this*
28937                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28938                 }
28939     },
28940
28941 /**     Specify from which ddGroup this DDView accepts drops. */
28942     setDroppable: function(ddGroup) {
28943         if (ddGroup instanceof Array) {
28944                 Roo.each(ddGroup, this.setDroppable, this);
28945                 return;
28946         }
28947         if (this.dropZone) {
28948                 this.dropZone.addToGroup(ddGroup);
28949         } else {
28950                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28951                                 containerScroll: true,
28952                                 ddGroup: ddGroup
28953                         });
28954
28955 //                      Wire the DropZone's handlers up to methods in *this*
28956                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28957                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28958                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28959                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28960                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28961                 }
28962     },
28963
28964 /**     Decide whether to drop above or below a View node. */
28965     getDropPoint : function(e, n, dd){
28966         if (n == this.el.dom) { return "above"; }
28967                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28968                 var c = t + (b - t) / 2;
28969                 var y = Roo.lib.Event.getPageY(e);
28970                 if(y <= c) {
28971                         return "above";
28972                 }else{
28973                         return "below";
28974                 }
28975     },
28976
28977     onNodeEnter : function(n, dd, e, data){
28978                 return false;
28979     },
28980     
28981     onNodeOver : function(n, dd, e, data){
28982                 var pt = this.getDropPoint(e, n, dd);
28983                 // set the insert point style on the target node
28984                 var dragElClass = this.dropNotAllowed;
28985                 if (pt) {
28986                         var targetElClass;
28987                         if (pt == "above"){
28988                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28989                                 targetElClass = "x-view-drag-insert-above";
28990                         } else {
28991                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28992                                 targetElClass = "x-view-drag-insert-below";
28993                         }
28994                         if (this.lastInsertClass != targetElClass){
28995                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28996                                 this.lastInsertClass = targetElClass;
28997                         }
28998                 }
28999                 return dragElClass;
29000         },
29001
29002     onNodeOut : function(n, dd, e, data){
29003                 this.removeDropIndicators(n);
29004     },
29005
29006     onNodeDrop : function(n, dd, e, data){
29007         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29008                 return false;
29009         }
29010         var pt = this.getDropPoint(e, n, dd);
29011                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29012                 if (pt == "below") { insertAt++; }
29013                 for (var i = 0; i < data.records.length; i++) {
29014                         var r = data.records[i];
29015                         var dup = this.store.getById(r.id);
29016                         if (dup && (dd != this.dragZone)) {
29017                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29018                         } else {
29019                                 if (data.copy) {
29020                                         this.store.insert(insertAt++, r.copy());
29021                                 } else {
29022                                         data.source.isDirtyFlag = true;
29023                                         r.store.remove(r);
29024                                         this.store.insert(insertAt++, r);
29025                                 }
29026                                 this.isDirtyFlag = true;
29027                         }
29028                 }
29029                 this.dragZone.cachedTarget = null;
29030                 return true;
29031     },
29032
29033     removeDropIndicators : function(n){
29034                 if(n){
29035                         Roo.fly(n).removeClass([
29036                                 "x-view-drag-insert-above",
29037                                 "x-view-drag-insert-below"]);
29038                         this.lastInsertClass = "_noclass";
29039                 }
29040     },
29041
29042 /**
29043  *      Utility method. Add a delete option to the DDView's context menu.
29044  *      @param {String} imageUrl The URL of the "delete" icon image.
29045  */
29046         setDeletable: function(imageUrl) {
29047                 if (!this.singleSelect && !this.multiSelect) {
29048                         this.singleSelect = true;
29049                 }
29050                 var c = this.getContextMenu();
29051                 this.contextMenu.on("itemclick", function(item) {
29052                         switch (item.id) {
29053                                 case "delete":
29054                                         this.remove(this.getSelectedIndexes());
29055                                         break;
29056                         }
29057                 }, this);
29058                 this.contextMenu.add({
29059                         icon: imageUrl,
29060                         id: "delete",
29061                         text: 'Delete'
29062                 });
29063         },
29064         
29065 /**     Return the context menu for this DDView. */
29066         getContextMenu: function() {
29067                 if (!this.contextMenu) {
29068 //                      Create the View's context menu
29069                         this.contextMenu = new Roo.menu.Menu({
29070                                 id: this.id + "-contextmenu"
29071                         });
29072                         this.el.on("contextmenu", this.showContextMenu, this);
29073                 }
29074                 return this.contextMenu;
29075         },
29076         
29077         disableContextMenu: function() {
29078                 if (this.contextMenu) {
29079                         this.el.un("contextmenu", this.showContextMenu, this);
29080                 }
29081         },
29082
29083         showContextMenu: function(e, item) {
29084         item = this.findItemFromChild(e.getTarget());
29085                 if (item) {
29086                         e.stopEvent();
29087                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29088                         this.contextMenu.showAt(e.getXY());
29089             }
29090     },
29091
29092 /**
29093  *      Remove {@link Roo.data.Record}s at the specified indices.
29094  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29095  */
29096     remove: function(selectedIndices) {
29097                 selectedIndices = [].concat(selectedIndices);
29098                 for (var i = 0; i < selectedIndices.length; i++) {
29099                         var rec = this.store.getAt(selectedIndices[i]);
29100                         this.store.remove(rec);
29101                 }
29102     },
29103
29104 /**
29105  *      Double click fires the event, but also, if this is draggable, and there is only one other
29106  *      related DropZone, it transfers the selected node.
29107  */
29108     onDblClick : function(e){
29109         var item = this.findItemFromChild(e.getTarget());
29110         if(item){
29111             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29112                 return false;
29113             }
29114             if (this.dragGroup) {
29115                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29116                     while (targets.indexOf(this.dropZone) > -1) {
29117                             targets.remove(this.dropZone);
29118                                 }
29119                     if (targets.length == 1) {
29120                                         this.dragZone.cachedTarget = null;
29121                         var el = Roo.get(targets[0].getEl());
29122                         var box = el.getBox(true);
29123                         targets[0].onNodeDrop(el.dom, {
29124                                 target: el.dom,
29125                                 xy: [box.x, box.y + box.height - 1]
29126                         }, null, this.getDragData(e));
29127                     }
29128                 }
29129         }
29130     },
29131     
29132     handleSelection: function(e) {
29133                 this.dragZone.cachedTarget = null;
29134         var item = this.findItemFromChild(e.getTarget());
29135         if (!item) {
29136                 this.clearSelections(true);
29137                 return;
29138         }
29139                 if (item && (this.multiSelect || this.singleSelect)){
29140                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29141                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29142                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29143                                 this.unselect(item);
29144                         } else {
29145                                 this.select(item, this.multiSelect && e.ctrlKey);
29146                                 this.lastSelection = item;
29147                         }
29148                 }
29149     },
29150
29151     onItemClick : function(item, index, e){
29152                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29153                         return false;
29154                 }
29155                 return true;
29156     },
29157
29158     unselect : function(nodeInfo, suppressEvent){
29159                 var node = this.getNode(nodeInfo);
29160                 if(node && this.isSelected(node)){
29161                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29162                                 Roo.fly(node).removeClass(this.selectedClass);
29163                                 this.selections.remove(node);
29164                                 if(!suppressEvent){
29165                                         this.fireEvent("selectionchange", this, this.selections);
29166                                 }
29167                         }
29168                 }
29169     }
29170 });
29171 /*
29172  * Based on:
29173  * Ext JS Library 1.1.1
29174  * Copyright(c) 2006-2007, Ext JS, LLC.
29175  *
29176  * Originally Released Under LGPL - original licence link has changed is not relivant.
29177  *
29178  * Fork - LGPL
29179  * <script type="text/javascript">
29180  */
29181  
29182 /**
29183  * @class Roo.LayoutManager
29184  * @extends Roo.util.Observable
29185  * Base class for layout managers.
29186  */
29187 Roo.LayoutManager = function(container, config){
29188     Roo.LayoutManager.superclass.constructor.call(this);
29189     this.el = Roo.get(container);
29190     // ie scrollbar fix
29191     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29192         document.body.scroll = "no";
29193     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29194         this.el.position('relative');
29195     }
29196     this.id = this.el.id;
29197     this.el.addClass("x-layout-container");
29198     /** false to disable window resize monitoring @type Boolean */
29199     this.monitorWindowResize = true;
29200     this.regions = {};
29201     this.addEvents({
29202         /**
29203          * @event layout
29204          * Fires when a layout is performed. 
29205          * @param {Roo.LayoutManager} this
29206          */
29207         "layout" : true,
29208         /**
29209          * @event regionresized
29210          * Fires when the user resizes a region. 
29211          * @param {Roo.LayoutRegion} region The resized region
29212          * @param {Number} newSize The new size (width for east/west, height for north/south)
29213          */
29214         "regionresized" : true,
29215         /**
29216          * @event regioncollapsed
29217          * Fires when a region is collapsed. 
29218          * @param {Roo.LayoutRegion} region The collapsed region
29219          */
29220         "regioncollapsed" : true,
29221         /**
29222          * @event regionexpanded
29223          * Fires when a region is expanded.  
29224          * @param {Roo.LayoutRegion} region The expanded region
29225          */
29226         "regionexpanded" : true
29227     });
29228     this.updating = false;
29229     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29230 };
29231
29232 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29233     /**
29234      * Returns true if this layout is currently being updated
29235      * @return {Boolean}
29236      */
29237     isUpdating : function(){
29238         return this.updating; 
29239     },
29240     
29241     /**
29242      * Suspend the LayoutManager from doing auto-layouts while
29243      * making multiple add or remove calls
29244      */
29245     beginUpdate : function(){
29246         this.updating = true;    
29247     },
29248     
29249     /**
29250      * Restore auto-layouts and optionally disable the manager from performing a layout
29251      * @param {Boolean} noLayout true to disable a layout update 
29252      */
29253     endUpdate : function(noLayout){
29254         this.updating = false;
29255         if(!noLayout){
29256             this.layout();
29257         }    
29258     },
29259     
29260     layout: function(){
29261         
29262     },
29263     
29264     onRegionResized : function(region, newSize){
29265         this.fireEvent("regionresized", region, newSize);
29266         this.layout();
29267     },
29268     
29269     onRegionCollapsed : function(region){
29270         this.fireEvent("regioncollapsed", region);
29271     },
29272     
29273     onRegionExpanded : function(region){
29274         this.fireEvent("regionexpanded", region);
29275     },
29276         
29277     /**
29278      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29279      * performs box-model adjustments.
29280      * @return {Object} The size as an object {width: (the width), height: (the height)}
29281      */
29282     getViewSize : function(){
29283         var size;
29284         if(this.el.dom != document.body){
29285             size = this.el.getSize();
29286         }else{
29287             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29288         }
29289         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29290         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29291         return size;
29292     },
29293     
29294     /**
29295      * Returns the Element this layout is bound to.
29296      * @return {Roo.Element}
29297      */
29298     getEl : function(){
29299         return this.el;
29300     },
29301     
29302     /**
29303      * Returns the specified region.
29304      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29305      * @return {Roo.LayoutRegion}
29306      */
29307     getRegion : function(target){
29308         return this.regions[target.toLowerCase()];
29309     },
29310     
29311     onWindowResize : function(){
29312         if(this.monitorWindowResize){
29313             this.layout();
29314         }
29315     }
29316 });/*
29317  * Based on:
29318  * Ext JS Library 1.1.1
29319  * Copyright(c) 2006-2007, Ext JS, LLC.
29320  *
29321  * Originally Released Under LGPL - original licence link has changed is not relivant.
29322  *
29323  * Fork - LGPL
29324  * <script type="text/javascript">
29325  */
29326 /**
29327  * @class Roo.BorderLayout
29328  * @extends Roo.LayoutManager
29329  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29330  * please see: <br><br>
29331  * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
29332  * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
29333  * Example:
29334  <pre><code>
29335  var layout = new Roo.BorderLayout(document.body, {
29336     north: {
29337         initialSize: 25,
29338         titlebar: false
29339     },
29340     west: {
29341         split:true,
29342         initialSize: 200,
29343         minSize: 175,
29344         maxSize: 400,
29345         titlebar: true,
29346         collapsible: true
29347     },
29348     east: {
29349         split:true,
29350         initialSize: 202,
29351         minSize: 175,
29352         maxSize: 400,
29353         titlebar: true,
29354         collapsible: true
29355     },
29356     south: {
29357         split:true,
29358         initialSize: 100,
29359         minSize: 100,
29360         maxSize: 200,
29361         titlebar: true,
29362         collapsible: true
29363     },
29364     center: {
29365         titlebar: true,
29366         autoScroll:true,
29367         resizeTabs: true,
29368         minTabWidth: 50,
29369         preferredTabWidth: 150
29370     }
29371 });
29372
29373 // shorthand
29374 var CP = Roo.ContentPanel;
29375
29376 layout.beginUpdate();
29377 layout.add("north", new CP("north", "North"));
29378 layout.add("south", new CP("south", {title: "South", closable: true}));
29379 layout.add("west", new CP("west", {title: "West"}));
29380 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29381 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29382 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29383 layout.getRegion("center").showPanel("center1");
29384 layout.endUpdate();
29385 </code></pre>
29386
29387 <b>The container the layout is rendered into can be either the body element or any other element.
29388 If it is not the body element, the container needs to either be an absolute positioned element,
29389 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29390 the container size if it is not the body element.</b>
29391
29392 * @constructor
29393 * Create a new BorderLayout
29394 * @param {String/HTMLElement/Element} container The container this layout is bound to
29395 * @param {Object} config Configuration options
29396  */
29397 Roo.BorderLayout = function(container, config){
29398     config = config || {};
29399     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29400     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29401     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29402         var target = this.factory.validRegions[i];
29403         if(config[target]){
29404             this.addRegion(target, config[target]);
29405         }
29406     }
29407 };
29408
29409 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29410     /**
29411      * Creates and adds a new region if it doesn't already exist.
29412      * @param {String} target The target region key (north, south, east, west or center).
29413      * @param {Object} config The regions config object
29414      * @return {BorderLayoutRegion} The new region
29415      */
29416     addRegion : function(target, config){
29417         if(!this.regions[target]){
29418             var r = this.factory.create(target, this, config);
29419             this.bindRegion(target, r);
29420         }
29421         return this.regions[target];
29422     },
29423
29424     // private (kinda)
29425     bindRegion : function(name, r){
29426         this.regions[name] = r;
29427         r.on("visibilitychange", this.layout, this);
29428         r.on("paneladded", this.layout, this);
29429         r.on("panelremoved", this.layout, this);
29430         r.on("invalidated", this.layout, this);
29431         r.on("resized", this.onRegionResized, this);
29432         r.on("collapsed", this.onRegionCollapsed, this);
29433         r.on("expanded", this.onRegionExpanded, this);
29434     },
29435
29436     /**
29437      * Performs a layout update.
29438      */
29439     layout : function(){
29440         if(this.updating) {
29441             return;
29442         }
29443         var size = this.getViewSize();
29444         var w = size.width;
29445         var h = size.height;
29446         var centerW = w;
29447         var centerH = h;
29448         var centerY = 0;
29449         var centerX = 0;
29450         //var x = 0, y = 0;
29451
29452         var rs = this.regions;
29453         var north = rs["north"];
29454         var south = rs["south"]; 
29455         var west = rs["west"];
29456         var east = rs["east"];
29457         var center = rs["center"];
29458         //if(this.hideOnLayout){ // not supported anymore
29459             //c.el.setStyle("display", "none");
29460         //}
29461         if(north && north.isVisible()){
29462             var b = north.getBox();
29463             var m = north.getMargins();
29464             b.width = w - (m.left+m.right);
29465             b.x = m.left;
29466             b.y = m.top;
29467             centerY = b.height + b.y + m.bottom;
29468             centerH -= centerY;
29469             north.updateBox(this.safeBox(b));
29470         }
29471         if(south && south.isVisible()){
29472             var b = south.getBox();
29473             var m = south.getMargins();
29474             b.width = w - (m.left+m.right);
29475             b.x = m.left;
29476             var totalHeight = (b.height + m.top + m.bottom);
29477             b.y = h - totalHeight + m.top;
29478             centerH -= totalHeight;
29479             south.updateBox(this.safeBox(b));
29480         }
29481         if(west && west.isVisible()){
29482             var b = west.getBox();
29483             var m = west.getMargins();
29484             b.height = centerH - (m.top+m.bottom);
29485             b.x = m.left;
29486             b.y = centerY + m.top;
29487             var totalWidth = (b.width + m.left + m.right);
29488             centerX += totalWidth;
29489             centerW -= totalWidth;
29490             west.updateBox(this.safeBox(b));
29491         }
29492         if(east && east.isVisible()){
29493             var b = east.getBox();
29494             var m = east.getMargins();
29495             b.height = centerH - (m.top+m.bottom);
29496             var totalWidth = (b.width + m.left + m.right);
29497             b.x = w - totalWidth + m.left;
29498             b.y = centerY + m.top;
29499             centerW -= totalWidth;
29500             east.updateBox(this.safeBox(b));
29501         }
29502         if(center){
29503             var m = center.getMargins();
29504             var centerBox = {
29505                 x: centerX + m.left,
29506                 y: centerY + m.top,
29507                 width: centerW - (m.left+m.right),
29508                 height: centerH - (m.top+m.bottom)
29509             };
29510             //if(this.hideOnLayout){
29511                 //center.el.setStyle("display", "block");
29512             //}
29513             center.updateBox(this.safeBox(centerBox));
29514         }
29515         this.el.repaint();
29516         this.fireEvent("layout", this);
29517     },
29518
29519     // private
29520     safeBox : function(box){
29521         box.width = Math.max(0, box.width);
29522         box.height = Math.max(0, box.height);
29523         return box;
29524     },
29525
29526     /**
29527      * Adds a ContentPanel (or subclass) to this layout.
29528      * @param {String} target The target region key (north, south, east, west or center).
29529      * @param {Roo.ContentPanel} panel The panel to add
29530      * @return {Roo.ContentPanel} The added panel
29531      */
29532     add : function(target, panel){
29533          
29534         target = target.toLowerCase();
29535         return this.regions[target].add(panel);
29536     },
29537
29538     /**
29539      * Remove a ContentPanel (or subclass) to this layout.
29540      * @param {String} target The target region key (north, south, east, west or center).
29541      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29542      * @return {Roo.ContentPanel} The removed panel
29543      */
29544     remove : function(target, panel){
29545         target = target.toLowerCase();
29546         return this.regions[target].remove(panel);
29547     },
29548
29549     /**
29550      * Searches all regions for a panel with the specified id
29551      * @param {String} panelId
29552      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29553      */
29554     findPanel : function(panelId){
29555         var rs = this.regions;
29556         for(var target in rs){
29557             if(typeof rs[target] != "function"){
29558                 var p = rs[target].getPanel(panelId);
29559                 if(p){
29560                     return p;
29561                 }
29562             }
29563         }
29564         return null;
29565     },
29566
29567     /**
29568      * Searches all regions for a panel with the specified id and activates (shows) it.
29569      * @param {String/ContentPanel} panelId The panels id or the panel itself
29570      * @return {Roo.ContentPanel} The shown panel or null
29571      */
29572     showPanel : function(panelId) {
29573       var rs = this.regions;
29574       for(var target in rs){
29575          var r = rs[target];
29576          if(typeof r != "function"){
29577             if(r.hasPanel(panelId)){
29578                return r.showPanel(panelId);
29579             }
29580          }
29581       }
29582       return null;
29583    },
29584
29585    /**
29586      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29587      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29588      */
29589     restoreState : function(provider){
29590         if(!provider){
29591             provider = Roo.state.Manager;
29592         }
29593         var sm = new Roo.LayoutStateManager();
29594         sm.init(this, provider);
29595     },
29596
29597     /**
29598      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29599      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29600      * a valid ContentPanel config object.  Example:
29601      * <pre><code>
29602 // Create the main layout
29603 var layout = new Roo.BorderLayout('main-ct', {
29604     west: {
29605         split:true,
29606         minSize: 175,
29607         titlebar: true
29608     },
29609     center: {
29610         title:'Components'
29611     }
29612 }, 'main-ct');
29613
29614 // Create and add multiple ContentPanels at once via configs
29615 layout.batchAdd({
29616    west: {
29617        id: 'source-files',
29618        autoCreate:true,
29619        title:'Ext Source Files',
29620        autoScroll:true,
29621        fitToFrame:true
29622    },
29623    center : {
29624        el: cview,
29625        autoScroll:true,
29626        fitToFrame:true,
29627        toolbar: tb,
29628        resizeEl:'cbody'
29629    }
29630 });
29631 </code></pre>
29632      * @param {Object} regions An object containing ContentPanel configs by region name
29633      */
29634     batchAdd : function(regions){
29635         this.beginUpdate();
29636         for(var rname in regions){
29637             var lr = this.regions[rname];
29638             if(lr){
29639                 this.addTypedPanels(lr, regions[rname]);
29640             }
29641         }
29642         this.endUpdate();
29643     },
29644
29645     // private
29646     addTypedPanels : function(lr, ps){
29647         if(typeof ps == 'string'){
29648             lr.add(new Roo.ContentPanel(ps));
29649         }
29650         else if(ps instanceof Array){
29651             for(var i =0, len = ps.length; i < len; i++){
29652                 this.addTypedPanels(lr, ps[i]);
29653             }
29654         }
29655         else if(!ps.events){ // raw config?
29656             var el = ps.el;
29657             delete ps.el; // prevent conflict
29658             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29659         }
29660         else {  // panel object assumed!
29661             lr.add(ps);
29662         }
29663     },
29664     /**
29665      * Adds a xtype elements to the layout.
29666      * <pre><code>
29667
29668 layout.addxtype({
29669        xtype : 'ContentPanel',
29670        region: 'west',
29671        items: [ .... ]
29672    }
29673 );
29674
29675 layout.addxtype({
29676         xtype : 'NestedLayoutPanel',
29677         region: 'west',
29678         layout: {
29679            center: { },
29680            west: { }   
29681         },
29682         items : [ ... list of content panels or nested layout panels.. ]
29683    }
29684 );
29685 </code></pre>
29686      * @param {Object} cfg Xtype definition of item to add.
29687      */
29688     addxtype : function(cfg)
29689     {
29690         // basically accepts a pannel...
29691         // can accept a layout region..!?!?
29692         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29693         
29694         if (!cfg.xtype.match(/Panel$/)) {
29695             return false;
29696         }
29697         var ret = false;
29698         
29699         if (typeof(cfg.region) == 'undefined') {
29700             Roo.log("Failed to add Panel, region was not set");
29701             Roo.log(cfg);
29702             return false;
29703         }
29704         var region = cfg.region;
29705         delete cfg.region;
29706         
29707           
29708         var xitems = [];
29709         if (cfg.items) {
29710             xitems = cfg.items;
29711             delete cfg.items;
29712         }
29713         var nb = false;
29714         
29715         switch(cfg.xtype) 
29716         {
29717             case 'ContentPanel':  // ContentPanel (el, cfg)
29718             case 'ScrollPanel':  // ContentPanel (el, cfg)
29719             case 'ViewPanel': 
29720                 if(cfg.autoCreate) {
29721                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29722                 } else {
29723                     var el = this.el.createChild();
29724                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29725                 }
29726                 
29727                 this.add(region, ret);
29728                 break;
29729             
29730             
29731             case 'TreePanel': // our new panel!
29732                 cfg.el = this.el.createChild();
29733                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29734                 this.add(region, ret);
29735                 break;
29736             
29737             case 'NestedLayoutPanel': 
29738                 // create a new Layout (which is  a Border Layout...
29739                 var el = this.el.createChild();
29740                 var clayout = cfg.layout;
29741                 delete cfg.layout;
29742                 clayout.items   = clayout.items  || [];
29743                 // replace this exitems with the clayout ones..
29744                 xitems = clayout.items;
29745                  
29746                 
29747                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29748                     cfg.background = false;
29749                 }
29750                 var layout = new Roo.BorderLayout(el, clayout);
29751                 
29752                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29753                 //console.log('adding nested layout panel '  + cfg.toSource());
29754                 this.add(region, ret);
29755                 nb = {}; /// find first...
29756                 break;
29757                 
29758             case 'GridPanel': 
29759             
29760                 // needs grid and region
29761                 
29762                 //var el = this.getRegion(region).el.createChild();
29763                 var el = this.el.createChild();
29764                 // create the grid first...
29765                 
29766                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29767                 delete cfg.grid;
29768                 if (region == 'center' && this.active ) {
29769                     cfg.background = false;
29770                 }
29771                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29772                 
29773                 this.add(region, ret);
29774                 if (cfg.background) {
29775                     ret.on('activate', function(gp) {
29776                         if (!gp.grid.rendered) {
29777                             gp.grid.render();
29778                         }
29779                     });
29780                 } else {
29781                     grid.render();
29782                 }
29783                 break;
29784            
29785            
29786            
29787                 
29788                 
29789                 
29790             default:
29791                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29792                     
29793                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29794                     this.add(region, ret);
29795                 } else {
29796                 
29797                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29798                     return null;
29799                 }
29800                 
29801              // GridPanel (grid, cfg)
29802             
29803         }
29804         this.beginUpdate();
29805         // add children..
29806         var region = '';
29807         var abn = {};
29808         Roo.each(xitems, function(i)  {
29809             region = nb && i.region ? i.region : false;
29810             
29811             var add = ret.addxtype(i);
29812            
29813             if (region) {
29814                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29815                 if (!i.background) {
29816                     abn[region] = nb[region] ;
29817                 }
29818             }
29819             
29820         });
29821         this.endUpdate();
29822
29823         // make the last non-background panel active..
29824         //if (nb) { Roo.log(abn); }
29825         if (nb) {
29826             
29827             for(var r in abn) {
29828                 region = this.getRegion(r);
29829                 if (region) {
29830                     // tried using nb[r], but it does not work..
29831                      
29832                     region.showPanel(abn[r]);
29833                    
29834                 }
29835             }
29836         }
29837         return ret;
29838         
29839     }
29840 });
29841
29842 /**
29843  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29844  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29845  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29846  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29847  * <pre><code>
29848 // shorthand
29849 var CP = Roo.ContentPanel;
29850
29851 var layout = Roo.BorderLayout.create({
29852     north: {
29853         initialSize: 25,
29854         titlebar: false,
29855         panels: [new CP("north", "North")]
29856     },
29857     west: {
29858         split:true,
29859         initialSize: 200,
29860         minSize: 175,
29861         maxSize: 400,
29862         titlebar: true,
29863         collapsible: true,
29864         panels: [new CP("west", {title: "West"})]
29865     },
29866     east: {
29867         split:true,
29868         initialSize: 202,
29869         minSize: 175,
29870         maxSize: 400,
29871         titlebar: true,
29872         collapsible: true,
29873         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29874     },
29875     south: {
29876         split:true,
29877         initialSize: 100,
29878         minSize: 100,
29879         maxSize: 200,
29880         titlebar: true,
29881         collapsible: true,
29882         panels: [new CP("south", {title: "South", closable: true})]
29883     },
29884     center: {
29885         titlebar: true,
29886         autoScroll:true,
29887         resizeTabs: true,
29888         minTabWidth: 50,
29889         preferredTabWidth: 150,
29890         panels: [
29891             new CP("center1", {title: "Close Me", closable: true}),
29892             new CP("center2", {title: "Center Panel", closable: false})
29893         ]
29894     }
29895 }, document.body);
29896
29897 layout.getRegion("center").showPanel("center1");
29898 </code></pre>
29899  * @param config
29900  * @param targetEl
29901  */
29902 Roo.BorderLayout.create = function(config, targetEl){
29903     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29904     layout.beginUpdate();
29905     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29906     for(var j = 0, jlen = regions.length; j < jlen; j++){
29907         var lr = regions[j];
29908         if(layout.regions[lr] && config[lr].panels){
29909             var r = layout.regions[lr];
29910             var ps = config[lr].panels;
29911             layout.addTypedPanels(r, ps);
29912         }
29913     }
29914     layout.endUpdate();
29915     return layout;
29916 };
29917
29918 // private
29919 Roo.BorderLayout.RegionFactory = {
29920     // private
29921     validRegions : ["north","south","east","west","center"],
29922
29923     // private
29924     create : function(target, mgr, config){
29925         target = target.toLowerCase();
29926         if(config.lightweight || config.basic){
29927             return new Roo.BasicLayoutRegion(mgr, config, target);
29928         }
29929         switch(target){
29930             case "north":
29931                 return new Roo.NorthLayoutRegion(mgr, config);
29932             case "south":
29933                 return new Roo.SouthLayoutRegion(mgr, config);
29934             case "east":
29935                 return new Roo.EastLayoutRegion(mgr, config);
29936             case "west":
29937                 return new Roo.WestLayoutRegion(mgr, config);
29938             case "center":
29939                 return new Roo.CenterLayoutRegion(mgr, config);
29940         }
29941         throw 'Layout region "'+target+'" not supported.';
29942     }
29943 };/*
29944  * Based on:
29945  * Ext JS Library 1.1.1
29946  * Copyright(c) 2006-2007, Ext JS, LLC.
29947  *
29948  * Originally Released Under LGPL - original licence link has changed is not relivant.
29949  *
29950  * Fork - LGPL
29951  * <script type="text/javascript">
29952  */
29953  
29954 /**
29955  * @class Roo.BasicLayoutRegion
29956  * @extends Roo.util.Observable
29957  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29958  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29959  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29960  */
29961 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29962     this.mgr = mgr;
29963     this.position  = pos;
29964     this.events = {
29965         /**
29966          * @scope Roo.BasicLayoutRegion
29967          */
29968         
29969         /**
29970          * @event beforeremove
29971          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29972          * @param {Roo.LayoutRegion} this
29973          * @param {Roo.ContentPanel} panel The panel
29974          * @param {Object} e The cancel event object
29975          */
29976         "beforeremove" : true,
29977         /**
29978          * @event invalidated
29979          * Fires when the layout for this region is changed.
29980          * @param {Roo.LayoutRegion} this
29981          */
29982         "invalidated" : true,
29983         /**
29984          * @event visibilitychange
29985          * Fires when this region is shown or hidden 
29986          * @param {Roo.LayoutRegion} this
29987          * @param {Boolean} visibility true or false
29988          */
29989         "visibilitychange" : true,
29990         /**
29991          * @event paneladded
29992          * Fires when a panel is added. 
29993          * @param {Roo.LayoutRegion} this
29994          * @param {Roo.ContentPanel} panel The panel
29995          */
29996         "paneladded" : true,
29997         /**
29998          * @event panelremoved
29999          * Fires when a panel is removed. 
30000          * @param {Roo.LayoutRegion} this
30001          * @param {Roo.ContentPanel} panel The panel
30002          */
30003         "panelremoved" : true,
30004         /**
30005          * @event beforecollapse
30006          * Fires when this region before collapse.
30007          * @param {Roo.LayoutRegion} this
30008          */
30009         "beforecollapse" : true,
30010         /**
30011          * @event collapsed
30012          * Fires when this region is collapsed.
30013          * @param {Roo.LayoutRegion} this
30014          */
30015         "collapsed" : true,
30016         /**
30017          * @event expanded
30018          * Fires when this region is expanded.
30019          * @param {Roo.LayoutRegion} this
30020          */
30021         "expanded" : true,
30022         /**
30023          * @event slideshow
30024          * Fires when this region is slid into view.
30025          * @param {Roo.LayoutRegion} this
30026          */
30027         "slideshow" : true,
30028         /**
30029          * @event slidehide
30030          * Fires when this region slides out of view. 
30031          * @param {Roo.LayoutRegion} this
30032          */
30033         "slidehide" : true,
30034         /**
30035          * @event panelactivated
30036          * Fires when a panel is activated. 
30037          * @param {Roo.LayoutRegion} this
30038          * @param {Roo.ContentPanel} panel The activated panel
30039          */
30040         "panelactivated" : true,
30041         /**
30042          * @event resized
30043          * Fires when the user resizes this region. 
30044          * @param {Roo.LayoutRegion} this
30045          * @param {Number} newSize The new size (width for east/west, height for north/south)
30046          */
30047         "resized" : true
30048     };
30049     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30050     this.panels = new Roo.util.MixedCollection();
30051     this.panels.getKey = this.getPanelId.createDelegate(this);
30052     this.box = null;
30053     this.activePanel = null;
30054     // ensure listeners are added...
30055     
30056     if (config.listeners || config.events) {
30057         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30058             listeners : config.listeners || {},
30059             events : config.events || {}
30060         });
30061     }
30062     
30063     if(skipConfig !== true){
30064         this.applyConfig(config);
30065     }
30066 };
30067
30068 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30069     getPanelId : function(p){
30070         return p.getId();
30071     },
30072     
30073     applyConfig : function(config){
30074         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30075         this.config = config;
30076         
30077     },
30078     
30079     /**
30080      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30081      * the width, for horizontal (north, south) the height.
30082      * @param {Number} newSize The new width or height
30083      */
30084     resizeTo : function(newSize){
30085         var el = this.el ? this.el :
30086                  (this.activePanel ? this.activePanel.getEl() : null);
30087         if(el){
30088             switch(this.position){
30089                 case "east":
30090                 case "west":
30091                     el.setWidth(newSize);
30092                     this.fireEvent("resized", this, newSize);
30093                 break;
30094                 case "north":
30095                 case "south":
30096                     el.setHeight(newSize);
30097                     this.fireEvent("resized", this, newSize);
30098                 break;                
30099             }
30100         }
30101     },
30102     
30103     getBox : function(){
30104         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30105     },
30106     
30107     getMargins : function(){
30108         return this.margins;
30109     },
30110     
30111     updateBox : function(box){
30112         this.box = box;
30113         var el = this.activePanel.getEl();
30114         el.dom.style.left = box.x + "px";
30115         el.dom.style.top = box.y + "px";
30116         this.activePanel.setSize(box.width, box.height);
30117     },
30118     
30119     /**
30120      * Returns the container element for this region.
30121      * @return {Roo.Element}
30122      */
30123     getEl : function(){
30124         return this.activePanel;
30125     },
30126     
30127     /**
30128      * Returns true if this region is currently visible.
30129      * @return {Boolean}
30130      */
30131     isVisible : function(){
30132         return this.activePanel ? true : false;
30133     },
30134     
30135     setActivePanel : function(panel){
30136         panel = this.getPanel(panel);
30137         if(this.activePanel && this.activePanel != panel){
30138             this.activePanel.setActiveState(false);
30139             this.activePanel.getEl().setLeftTop(-10000,-10000);
30140         }
30141         this.activePanel = panel;
30142         panel.setActiveState(true);
30143         if(this.box){
30144             panel.setSize(this.box.width, this.box.height);
30145         }
30146         this.fireEvent("panelactivated", this, panel);
30147         this.fireEvent("invalidated");
30148     },
30149     
30150     /**
30151      * Show the specified panel.
30152      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30153      * @return {Roo.ContentPanel} The shown panel or null
30154      */
30155     showPanel : function(panel){
30156         if(panel = this.getPanel(panel)){
30157             this.setActivePanel(panel);
30158         }
30159         return panel;
30160     },
30161     
30162     /**
30163      * Get the active panel for this region.
30164      * @return {Roo.ContentPanel} The active panel or null
30165      */
30166     getActivePanel : function(){
30167         return this.activePanel;
30168     },
30169     
30170     /**
30171      * Add the passed ContentPanel(s)
30172      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30173      * @return {Roo.ContentPanel} The panel added (if only one was added)
30174      */
30175     add : function(panel){
30176         if(arguments.length > 1){
30177             for(var i = 0, len = arguments.length; i < len; i++) {
30178                 this.add(arguments[i]);
30179             }
30180             return null;
30181         }
30182         if(this.hasPanel(panel)){
30183             this.showPanel(panel);
30184             return panel;
30185         }
30186         var el = panel.getEl();
30187         if(el.dom.parentNode != this.mgr.el.dom){
30188             this.mgr.el.dom.appendChild(el.dom);
30189         }
30190         if(panel.setRegion){
30191             panel.setRegion(this);
30192         }
30193         this.panels.add(panel);
30194         el.setStyle("position", "absolute");
30195         if(!panel.background){
30196             this.setActivePanel(panel);
30197             if(this.config.initialSize && this.panels.getCount()==1){
30198                 this.resizeTo(this.config.initialSize);
30199             }
30200         }
30201         this.fireEvent("paneladded", this, panel);
30202         return panel;
30203     },
30204     
30205     /**
30206      * Returns true if the panel is in this region.
30207      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30208      * @return {Boolean}
30209      */
30210     hasPanel : function(panel){
30211         if(typeof panel == "object"){ // must be panel obj
30212             panel = panel.getId();
30213         }
30214         return this.getPanel(panel) ? true : false;
30215     },
30216     
30217     /**
30218      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30219      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30220      * @param {Boolean} preservePanel Overrides the config preservePanel option
30221      * @return {Roo.ContentPanel} The panel that was removed
30222      */
30223     remove : function(panel, preservePanel){
30224         panel = this.getPanel(panel);
30225         if(!panel){
30226             return null;
30227         }
30228         var e = {};
30229         this.fireEvent("beforeremove", this, panel, e);
30230         if(e.cancel === true){
30231             return null;
30232         }
30233         var panelId = panel.getId();
30234         this.panels.removeKey(panelId);
30235         return panel;
30236     },
30237     
30238     /**
30239      * Returns the panel specified or null if it's not in this region.
30240      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30241      * @return {Roo.ContentPanel}
30242      */
30243     getPanel : function(id){
30244         if(typeof id == "object"){ // must be panel obj
30245             return id;
30246         }
30247         return this.panels.get(id);
30248     },
30249     
30250     /**
30251      * Returns this regions position (north/south/east/west/center).
30252      * @return {String} 
30253      */
30254     getPosition: function(){
30255         return this.position;    
30256     }
30257 });/*
30258  * Based on:
30259  * Ext JS Library 1.1.1
30260  * Copyright(c) 2006-2007, Ext JS, LLC.
30261  *
30262  * Originally Released Under LGPL - original licence link has changed is not relivant.
30263  *
30264  * Fork - LGPL
30265  * <script type="text/javascript">
30266  */
30267  
30268 /**
30269  * @class Roo.LayoutRegion
30270  * @extends Roo.BasicLayoutRegion
30271  * This class represents a region in a layout manager.
30272  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30273  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30274  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30275  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30276  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
30277  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30278  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30279  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30280  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30281  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30282  * @cfg {String}    title           The title for the region (overrides panel titles)
30283  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30284  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30285  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30286  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30287  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30288  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30289  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30290  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30291  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30292  * @cfg {Boolean}   showPin         True to show a pin button
30293  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30294  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30295  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30296  * @cfg {Number}    width           For East/West panels
30297  * @cfg {Number}    height          For North/South panels
30298  * @cfg {Boolean}   split           To show the splitter
30299  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30300  */
30301 Roo.LayoutRegion = function(mgr, config, pos){
30302     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30303     var dh = Roo.DomHelper;
30304     /** This region's container element 
30305     * @type Roo.Element */
30306     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30307     /** This region's title element 
30308     * @type Roo.Element */
30309
30310     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30311         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30312         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30313     ]}, true);
30314     this.titleEl.enableDisplayMode();
30315     /** This region's title text element 
30316     * @type HTMLElement */
30317     this.titleTextEl = this.titleEl.dom.firstChild;
30318     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30319     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30320     this.closeBtn.enableDisplayMode();
30321     this.closeBtn.on("click", this.closeClicked, this);
30322     this.closeBtn.hide();
30323
30324     this.createBody(config);
30325     this.visible = true;
30326     this.collapsed = false;
30327
30328     if(config.hideWhenEmpty){
30329         this.hide();
30330         this.on("paneladded", this.validateVisibility, this);
30331         this.on("panelremoved", this.validateVisibility, this);
30332     }
30333     this.applyConfig(config);
30334 };
30335
30336 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30337
30338     createBody : function(){
30339         /** This region's body element 
30340         * @type Roo.Element */
30341         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30342     },
30343
30344     applyConfig : function(c){
30345         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30346             var dh = Roo.DomHelper;
30347             if(c.titlebar !== false){
30348                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30349                 this.collapseBtn.on("click", this.collapse, this);
30350                 this.collapseBtn.enableDisplayMode();
30351
30352                 if(c.showPin === true || this.showPin){
30353                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30354                     this.stickBtn.enableDisplayMode();
30355                     this.stickBtn.on("click", this.expand, this);
30356                     this.stickBtn.hide();
30357                 }
30358             }
30359             /** This region's collapsed element
30360             * @type Roo.Element */
30361             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30362                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30363             ]}, true);
30364             if(c.floatable !== false){
30365                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30366                this.collapsedEl.on("click", this.collapseClick, this);
30367             }
30368
30369             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30370                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30371                    id: "message", unselectable: "on", style:{"float":"left"}});
30372                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30373              }
30374             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30375             this.expandBtn.on("click", this.expand, this);
30376         }
30377         if(this.collapseBtn){
30378             this.collapseBtn.setVisible(c.collapsible == true);
30379         }
30380         this.cmargins = c.cmargins || this.cmargins ||
30381                          (this.position == "west" || this.position == "east" ?
30382                              {top: 0, left: 2, right:2, bottom: 0} :
30383                              {top: 2, left: 0, right:0, bottom: 2});
30384         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30385         this.bottomTabs = c.tabPosition != "top";
30386         this.autoScroll = c.autoScroll || false;
30387         if(this.autoScroll){
30388             this.bodyEl.setStyle("overflow", "auto");
30389         }else{
30390             this.bodyEl.setStyle("overflow", "hidden");
30391         }
30392         //if(c.titlebar !== false){
30393             if((!c.titlebar && !c.title) || c.titlebar === false){
30394                 this.titleEl.hide();
30395             }else{
30396                 this.titleEl.show();
30397                 if(c.title){
30398                     this.titleTextEl.innerHTML = c.title;
30399                 }
30400             }
30401         //}
30402         this.duration = c.duration || .30;
30403         this.slideDuration = c.slideDuration || .45;
30404         this.config = c;
30405         if(c.collapsed){
30406             this.collapse(true);
30407         }
30408         if(c.hidden){
30409             this.hide();
30410         }
30411     },
30412     /**
30413      * Returns true if this region is currently visible.
30414      * @return {Boolean}
30415      */
30416     isVisible : function(){
30417         return this.visible;
30418     },
30419
30420     /**
30421      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30422      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30423      */
30424     setCollapsedTitle : function(title){
30425         title = title || "&#160;";
30426         if(this.collapsedTitleTextEl){
30427             this.collapsedTitleTextEl.innerHTML = title;
30428         }
30429     },
30430
30431     getBox : function(){
30432         var b;
30433         if(!this.collapsed){
30434             b = this.el.getBox(false, true);
30435         }else{
30436             b = this.collapsedEl.getBox(false, true);
30437         }
30438         return b;
30439     },
30440
30441     getMargins : function(){
30442         return this.collapsed ? this.cmargins : this.margins;
30443     },
30444
30445     highlight : function(){
30446         this.el.addClass("x-layout-panel-dragover");
30447     },
30448
30449     unhighlight : function(){
30450         this.el.removeClass("x-layout-panel-dragover");
30451     },
30452
30453     updateBox : function(box){
30454         this.box = box;
30455         if(!this.collapsed){
30456             this.el.dom.style.left = box.x + "px";
30457             this.el.dom.style.top = box.y + "px";
30458             this.updateBody(box.width, box.height);
30459         }else{
30460             this.collapsedEl.dom.style.left = box.x + "px";
30461             this.collapsedEl.dom.style.top = box.y + "px";
30462             this.collapsedEl.setSize(box.width, box.height);
30463         }
30464         if(this.tabs){
30465             this.tabs.autoSizeTabs();
30466         }
30467     },
30468
30469     updateBody : function(w, h){
30470         if(w !== null){
30471             this.el.setWidth(w);
30472             w -= this.el.getBorderWidth("rl");
30473             if(this.config.adjustments){
30474                 w += this.config.adjustments[0];
30475             }
30476         }
30477         if(h !== null){
30478             this.el.setHeight(h);
30479             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30480             h -= this.el.getBorderWidth("tb");
30481             if(this.config.adjustments){
30482                 h += this.config.adjustments[1];
30483             }
30484             this.bodyEl.setHeight(h);
30485             if(this.tabs){
30486                 h = this.tabs.syncHeight(h);
30487             }
30488         }
30489         if(this.panelSize){
30490             w = w !== null ? w : this.panelSize.width;
30491             h = h !== null ? h : this.panelSize.height;
30492         }
30493         if(this.activePanel){
30494             var el = this.activePanel.getEl();
30495             w = w !== null ? w : el.getWidth();
30496             h = h !== null ? h : el.getHeight();
30497             this.panelSize = {width: w, height: h};
30498             this.activePanel.setSize(w, h);
30499         }
30500         if(Roo.isIE && this.tabs){
30501             this.tabs.el.repaint();
30502         }
30503     },
30504
30505     /**
30506      * Returns the container element for this region.
30507      * @return {Roo.Element}
30508      */
30509     getEl : function(){
30510         return this.el;
30511     },
30512
30513     /**
30514      * Hides this region.
30515      */
30516     hide : function(){
30517         if(!this.collapsed){
30518             this.el.dom.style.left = "-2000px";
30519             this.el.hide();
30520         }else{
30521             this.collapsedEl.dom.style.left = "-2000px";
30522             this.collapsedEl.hide();
30523         }
30524         this.visible = false;
30525         this.fireEvent("visibilitychange", this, false);
30526     },
30527
30528     /**
30529      * Shows this region if it was previously hidden.
30530      */
30531     show : function(){
30532         if(!this.collapsed){
30533             this.el.show();
30534         }else{
30535             this.collapsedEl.show();
30536         }
30537         this.visible = true;
30538         this.fireEvent("visibilitychange", this, true);
30539     },
30540
30541     closeClicked : function(){
30542         if(this.activePanel){
30543             this.remove(this.activePanel);
30544         }
30545     },
30546
30547     collapseClick : function(e){
30548         if(this.isSlid){
30549            e.stopPropagation();
30550            this.slideIn();
30551         }else{
30552            e.stopPropagation();
30553            this.slideOut();
30554         }
30555     },
30556
30557     /**
30558      * Collapses this region.
30559      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30560      */
30561     collapse : function(skipAnim, skipCheck){
30562         if(this.collapsed) {
30563             return;
30564         }
30565         
30566         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30567             
30568             this.collapsed = true;
30569             if(this.split){
30570                 this.split.el.hide();
30571             }
30572             if(this.config.animate && skipAnim !== true){
30573                 this.fireEvent("invalidated", this);
30574                 this.animateCollapse();
30575             }else{
30576                 this.el.setLocation(-20000,-20000);
30577                 this.el.hide();
30578                 this.collapsedEl.show();
30579                 this.fireEvent("collapsed", this);
30580                 this.fireEvent("invalidated", this);
30581             }
30582         }
30583         
30584     },
30585
30586     animateCollapse : function(){
30587         // overridden
30588     },
30589
30590     /**
30591      * Expands this region if it was previously collapsed.
30592      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30593      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30594      */
30595     expand : function(e, skipAnim){
30596         if(e) {
30597             e.stopPropagation();
30598         }
30599         if(!this.collapsed || this.el.hasActiveFx()) {
30600             return;
30601         }
30602         if(this.isSlid){
30603             this.afterSlideIn();
30604             skipAnim = true;
30605         }
30606         this.collapsed = false;
30607         if(this.config.animate && skipAnim !== true){
30608             this.animateExpand();
30609         }else{
30610             this.el.show();
30611             if(this.split){
30612                 this.split.el.show();
30613             }
30614             this.collapsedEl.setLocation(-2000,-2000);
30615             this.collapsedEl.hide();
30616             this.fireEvent("invalidated", this);
30617             this.fireEvent("expanded", this);
30618         }
30619     },
30620
30621     animateExpand : function(){
30622         // overridden
30623     },
30624
30625     initTabs : function()
30626     {
30627         this.bodyEl.setStyle("overflow", "hidden");
30628         var ts = new Roo.TabPanel(
30629                 this.bodyEl.dom,
30630                 {
30631                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30632                     disableTooltips: this.config.disableTabTips,
30633                     toolbar : this.config.toolbar
30634                 }
30635         );
30636         if(this.config.hideTabs){
30637             ts.stripWrap.setDisplayed(false);
30638         }
30639         this.tabs = ts;
30640         ts.resizeTabs = this.config.resizeTabs === true;
30641         ts.minTabWidth = this.config.minTabWidth || 40;
30642         ts.maxTabWidth = this.config.maxTabWidth || 250;
30643         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30644         ts.monitorResize = false;
30645         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30646         ts.bodyEl.addClass('x-layout-tabs-body');
30647         this.panels.each(this.initPanelAsTab, this);
30648     },
30649
30650     initPanelAsTab : function(panel){
30651         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30652                     this.config.closeOnTab && panel.isClosable());
30653         if(panel.tabTip !== undefined){
30654             ti.setTooltip(panel.tabTip);
30655         }
30656         ti.on("activate", function(){
30657               this.setActivePanel(panel);
30658         }, this);
30659         if(this.config.closeOnTab){
30660             ti.on("beforeclose", function(t, e){
30661                 e.cancel = true;
30662                 this.remove(panel);
30663             }, this);
30664         }
30665         return ti;
30666     },
30667
30668     updatePanelTitle : function(panel, title){
30669         if(this.activePanel == panel){
30670             this.updateTitle(title);
30671         }
30672         if(this.tabs){
30673             var ti = this.tabs.getTab(panel.getEl().id);
30674             ti.setText(title);
30675             if(panel.tabTip !== undefined){
30676                 ti.setTooltip(panel.tabTip);
30677             }
30678         }
30679     },
30680
30681     updateTitle : function(title){
30682         if(this.titleTextEl && !this.config.title){
30683             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30684         }
30685     },
30686
30687     setActivePanel : function(panel){
30688         panel = this.getPanel(panel);
30689         if(this.activePanel && this.activePanel != panel){
30690             this.activePanel.setActiveState(false);
30691         }
30692         this.activePanel = panel;
30693         panel.setActiveState(true);
30694         if(this.panelSize){
30695             panel.setSize(this.panelSize.width, this.panelSize.height);
30696         }
30697         if(this.closeBtn){
30698             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30699         }
30700         this.updateTitle(panel.getTitle());
30701         if(this.tabs){
30702             this.fireEvent("invalidated", this);
30703         }
30704         this.fireEvent("panelactivated", this, panel);
30705     },
30706
30707     /**
30708      * Shows the specified panel.
30709      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30710      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30711      */
30712     showPanel : function(panel)
30713     {
30714         panel = this.getPanel(panel);
30715         if(panel){
30716             if(this.tabs){
30717                 var tab = this.tabs.getTab(panel.getEl().id);
30718                 if(tab.isHidden()){
30719                     this.tabs.unhideTab(tab.id);
30720                 }
30721                 tab.activate();
30722             }else{
30723                 this.setActivePanel(panel);
30724             }
30725         }
30726         return panel;
30727     },
30728
30729     /**
30730      * Get the active panel for this region.
30731      * @return {Roo.ContentPanel} The active panel or null
30732      */
30733     getActivePanel : function(){
30734         return this.activePanel;
30735     },
30736
30737     validateVisibility : function(){
30738         if(this.panels.getCount() < 1){
30739             this.updateTitle("&#160;");
30740             this.closeBtn.hide();
30741             this.hide();
30742         }else{
30743             if(!this.isVisible()){
30744                 this.show();
30745             }
30746         }
30747     },
30748
30749     /**
30750      * Adds the passed ContentPanel(s) to this region.
30751      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30752      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30753      */
30754     add : function(panel){
30755         if(arguments.length > 1){
30756             for(var i = 0, len = arguments.length; i < len; i++) {
30757                 this.add(arguments[i]);
30758             }
30759             return null;
30760         }
30761         if(this.hasPanel(panel)){
30762             this.showPanel(panel);
30763             return panel;
30764         }
30765         panel.setRegion(this);
30766         this.panels.add(panel);
30767         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30768             this.bodyEl.dom.appendChild(panel.getEl().dom);
30769             if(panel.background !== true){
30770                 this.setActivePanel(panel);
30771             }
30772             this.fireEvent("paneladded", this, panel);
30773             return panel;
30774         }
30775         if(!this.tabs){
30776             this.initTabs();
30777         }else{
30778             this.initPanelAsTab(panel);
30779         }
30780         if(panel.background !== true){
30781             this.tabs.activate(panel.getEl().id);
30782         }
30783         this.fireEvent("paneladded", this, panel);
30784         return panel;
30785     },
30786
30787     /**
30788      * Hides the tab for the specified panel.
30789      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30790      */
30791     hidePanel : function(panel){
30792         if(this.tabs && (panel = this.getPanel(panel))){
30793             this.tabs.hideTab(panel.getEl().id);
30794         }
30795     },
30796
30797     /**
30798      * Unhides the tab for a previously hidden panel.
30799      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30800      */
30801     unhidePanel : function(panel){
30802         if(this.tabs && (panel = this.getPanel(panel))){
30803             this.tabs.unhideTab(panel.getEl().id);
30804         }
30805     },
30806
30807     clearPanels : function(){
30808         while(this.panels.getCount() > 0){
30809              this.remove(this.panels.first());
30810         }
30811     },
30812
30813     /**
30814      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30815      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30816      * @param {Boolean} preservePanel Overrides the config preservePanel option
30817      * @return {Roo.ContentPanel} The panel that was removed
30818      */
30819     remove : function(panel, preservePanel){
30820         panel = this.getPanel(panel);
30821         if(!panel){
30822             return null;
30823         }
30824         var e = {};
30825         this.fireEvent("beforeremove", this, panel, e);
30826         if(e.cancel === true){
30827             return null;
30828         }
30829         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30830         var panelId = panel.getId();
30831         this.panels.removeKey(panelId);
30832         if(preservePanel){
30833             document.body.appendChild(panel.getEl().dom);
30834         }
30835         if(this.tabs){
30836             this.tabs.removeTab(panel.getEl().id);
30837         }else if (!preservePanel){
30838             this.bodyEl.dom.removeChild(panel.getEl().dom);
30839         }
30840         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30841             var p = this.panels.first();
30842             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30843             tempEl.appendChild(p.getEl().dom);
30844             this.bodyEl.update("");
30845             this.bodyEl.dom.appendChild(p.getEl().dom);
30846             tempEl = null;
30847             this.updateTitle(p.getTitle());
30848             this.tabs = null;
30849             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30850             this.setActivePanel(p);
30851         }
30852         panel.setRegion(null);
30853         if(this.activePanel == panel){
30854             this.activePanel = null;
30855         }
30856         if(this.config.autoDestroy !== false && preservePanel !== true){
30857             try{panel.destroy();}catch(e){}
30858         }
30859         this.fireEvent("panelremoved", this, panel);
30860         return panel;
30861     },
30862
30863     /**
30864      * Returns the TabPanel component used by this region
30865      * @return {Roo.TabPanel}
30866      */
30867     getTabs : function(){
30868         return this.tabs;
30869     },
30870
30871     createTool : function(parentEl, className){
30872         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30873             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30874         btn.addClassOnOver("x-layout-tools-button-over");
30875         return btn;
30876     }
30877 });/*
30878  * Based on:
30879  * Ext JS Library 1.1.1
30880  * Copyright(c) 2006-2007, Ext JS, LLC.
30881  *
30882  * Originally Released Under LGPL - original licence link has changed is not relivant.
30883  *
30884  * Fork - LGPL
30885  * <script type="text/javascript">
30886  */
30887  
30888
30889
30890 /**
30891  * @class Roo.SplitLayoutRegion
30892  * @extends Roo.LayoutRegion
30893  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30894  */
30895 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30896     this.cursor = cursor;
30897     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30898 };
30899
30900 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30901     splitTip : "Drag to resize.",
30902     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30903     useSplitTips : false,
30904
30905     applyConfig : function(config){
30906         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30907         if(config.split){
30908             if(!this.split){
30909                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30910                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30911                 /** The SplitBar for this region 
30912                 * @type Roo.SplitBar */
30913                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30914                 this.split.on("moved", this.onSplitMove, this);
30915                 this.split.useShim = config.useShim === true;
30916                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30917                 if(this.useSplitTips){
30918                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30919                 }
30920                 if(config.collapsible){
30921                     this.split.el.on("dblclick", this.collapse,  this);
30922                 }
30923             }
30924             if(typeof config.minSize != "undefined"){
30925                 this.split.minSize = config.minSize;
30926             }
30927             if(typeof config.maxSize != "undefined"){
30928                 this.split.maxSize = config.maxSize;
30929             }
30930             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30931                 this.hideSplitter();
30932             }
30933         }
30934     },
30935
30936     getHMaxSize : function(){
30937          var cmax = this.config.maxSize || 10000;
30938          var center = this.mgr.getRegion("center");
30939          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30940     },
30941
30942     getVMaxSize : function(){
30943          var cmax = this.config.maxSize || 10000;
30944          var center = this.mgr.getRegion("center");
30945          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30946     },
30947
30948     onSplitMove : function(split, newSize){
30949         this.fireEvent("resized", this, newSize);
30950     },
30951     
30952     /** 
30953      * Returns the {@link Roo.SplitBar} for this region.
30954      * @return {Roo.SplitBar}
30955      */
30956     getSplitBar : function(){
30957         return this.split;
30958     },
30959     
30960     hide : function(){
30961         this.hideSplitter();
30962         Roo.SplitLayoutRegion.superclass.hide.call(this);
30963     },
30964
30965     hideSplitter : function(){
30966         if(this.split){
30967             this.split.el.setLocation(-2000,-2000);
30968             this.split.el.hide();
30969         }
30970     },
30971
30972     show : function(){
30973         if(this.split){
30974             this.split.el.show();
30975         }
30976         Roo.SplitLayoutRegion.superclass.show.call(this);
30977     },
30978     
30979     beforeSlide: function(){
30980         if(Roo.isGecko){// firefox overflow auto bug workaround
30981             this.bodyEl.clip();
30982             if(this.tabs) {
30983                 this.tabs.bodyEl.clip();
30984             }
30985             if(this.activePanel){
30986                 this.activePanel.getEl().clip();
30987                 
30988                 if(this.activePanel.beforeSlide){
30989                     this.activePanel.beforeSlide();
30990                 }
30991             }
30992         }
30993     },
30994     
30995     afterSlide : function(){
30996         if(Roo.isGecko){// firefox overflow auto bug workaround
30997             this.bodyEl.unclip();
30998             if(this.tabs) {
30999                 this.tabs.bodyEl.unclip();
31000             }
31001             if(this.activePanel){
31002                 this.activePanel.getEl().unclip();
31003                 if(this.activePanel.afterSlide){
31004                     this.activePanel.afterSlide();
31005                 }
31006             }
31007         }
31008     },
31009
31010     initAutoHide : function(){
31011         if(this.autoHide !== false){
31012             if(!this.autoHideHd){
31013                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31014                 this.autoHideHd = {
31015                     "mouseout": function(e){
31016                         if(!e.within(this.el, true)){
31017                             st.delay(500);
31018                         }
31019                     },
31020                     "mouseover" : function(e){
31021                         st.cancel();
31022                     },
31023                     scope : this
31024                 };
31025             }
31026             this.el.on(this.autoHideHd);
31027         }
31028     },
31029
31030     clearAutoHide : function(){
31031         if(this.autoHide !== false){
31032             this.el.un("mouseout", this.autoHideHd.mouseout);
31033             this.el.un("mouseover", this.autoHideHd.mouseover);
31034         }
31035     },
31036
31037     clearMonitor : function(){
31038         Roo.get(document).un("click", this.slideInIf, this);
31039     },
31040
31041     // these names are backwards but not changed for compat
31042     slideOut : function(){
31043         if(this.isSlid || this.el.hasActiveFx()){
31044             return;
31045         }
31046         this.isSlid = true;
31047         if(this.collapseBtn){
31048             this.collapseBtn.hide();
31049         }
31050         this.closeBtnState = this.closeBtn.getStyle('display');
31051         this.closeBtn.hide();
31052         if(this.stickBtn){
31053             this.stickBtn.show();
31054         }
31055         this.el.show();
31056         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31057         this.beforeSlide();
31058         this.el.setStyle("z-index", 10001);
31059         this.el.slideIn(this.getSlideAnchor(), {
31060             callback: function(){
31061                 this.afterSlide();
31062                 this.initAutoHide();
31063                 Roo.get(document).on("click", this.slideInIf, this);
31064                 this.fireEvent("slideshow", this);
31065             },
31066             scope: this,
31067             block: true
31068         });
31069     },
31070
31071     afterSlideIn : function(){
31072         this.clearAutoHide();
31073         this.isSlid = false;
31074         this.clearMonitor();
31075         this.el.setStyle("z-index", "");
31076         if(this.collapseBtn){
31077             this.collapseBtn.show();
31078         }
31079         this.closeBtn.setStyle('display', this.closeBtnState);
31080         if(this.stickBtn){
31081             this.stickBtn.hide();
31082         }
31083         this.fireEvent("slidehide", this);
31084     },
31085
31086     slideIn : function(cb){
31087         if(!this.isSlid || this.el.hasActiveFx()){
31088             Roo.callback(cb);
31089             return;
31090         }
31091         this.isSlid = false;
31092         this.beforeSlide();
31093         this.el.slideOut(this.getSlideAnchor(), {
31094             callback: function(){
31095                 this.el.setLeftTop(-10000, -10000);
31096                 this.afterSlide();
31097                 this.afterSlideIn();
31098                 Roo.callback(cb);
31099             },
31100             scope: this,
31101             block: true
31102         });
31103     },
31104     
31105     slideInIf : function(e){
31106         if(!e.within(this.el)){
31107             this.slideIn();
31108         }
31109     },
31110
31111     animateCollapse : function(){
31112         this.beforeSlide();
31113         this.el.setStyle("z-index", 20000);
31114         var anchor = this.getSlideAnchor();
31115         this.el.slideOut(anchor, {
31116             callback : function(){
31117                 this.el.setStyle("z-index", "");
31118                 this.collapsedEl.slideIn(anchor, {duration:.3});
31119                 this.afterSlide();
31120                 this.el.setLocation(-10000,-10000);
31121                 this.el.hide();
31122                 this.fireEvent("collapsed", this);
31123             },
31124             scope: this,
31125             block: true
31126         });
31127     },
31128
31129     animateExpand : function(){
31130         this.beforeSlide();
31131         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31132         this.el.setStyle("z-index", 20000);
31133         this.collapsedEl.hide({
31134             duration:.1
31135         });
31136         this.el.slideIn(this.getSlideAnchor(), {
31137             callback : function(){
31138                 this.el.setStyle("z-index", "");
31139                 this.afterSlide();
31140                 if(this.split){
31141                     this.split.el.show();
31142                 }
31143                 this.fireEvent("invalidated", this);
31144                 this.fireEvent("expanded", this);
31145             },
31146             scope: this,
31147             block: true
31148         });
31149     },
31150
31151     anchors : {
31152         "west" : "left",
31153         "east" : "right",
31154         "north" : "top",
31155         "south" : "bottom"
31156     },
31157
31158     sanchors : {
31159         "west" : "l",
31160         "east" : "r",
31161         "north" : "t",
31162         "south" : "b"
31163     },
31164
31165     canchors : {
31166         "west" : "tl-tr",
31167         "east" : "tr-tl",
31168         "north" : "tl-bl",
31169         "south" : "bl-tl"
31170     },
31171
31172     getAnchor : function(){
31173         return this.anchors[this.position];
31174     },
31175
31176     getCollapseAnchor : function(){
31177         return this.canchors[this.position];
31178     },
31179
31180     getSlideAnchor : function(){
31181         return this.sanchors[this.position];
31182     },
31183
31184     getAlignAdj : function(){
31185         var cm = this.cmargins;
31186         switch(this.position){
31187             case "west":
31188                 return [0, 0];
31189             break;
31190             case "east":
31191                 return [0, 0];
31192             break;
31193             case "north":
31194                 return [0, 0];
31195             break;
31196             case "south":
31197                 return [0, 0];
31198             break;
31199         }
31200     },
31201
31202     getExpandAdj : function(){
31203         var c = this.collapsedEl, cm = this.cmargins;
31204         switch(this.position){
31205             case "west":
31206                 return [-(cm.right+c.getWidth()+cm.left), 0];
31207             break;
31208             case "east":
31209                 return [cm.right+c.getWidth()+cm.left, 0];
31210             break;
31211             case "north":
31212                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31213             break;
31214             case "south":
31215                 return [0, cm.top+cm.bottom+c.getHeight()];
31216             break;
31217         }
31218     }
31219 });/*
31220  * Based on:
31221  * Ext JS Library 1.1.1
31222  * Copyright(c) 2006-2007, Ext JS, LLC.
31223  *
31224  * Originally Released Under LGPL - original licence link has changed is not relivant.
31225  *
31226  * Fork - LGPL
31227  * <script type="text/javascript">
31228  */
31229 /*
31230  * These classes are private internal classes
31231  */
31232 Roo.CenterLayoutRegion = function(mgr, config){
31233     Roo.LayoutRegion.call(this, mgr, config, "center");
31234     this.visible = true;
31235     this.minWidth = config.minWidth || 20;
31236     this.minHeight = config.minHeight || 20;
31237 };
31238
31239 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31240     hide : function(){
31241         // center panel can't be hidden
31242     },
31243     
31244     show : function(){
31245         // center panel can't be hidden
31246     },
31247     
31248     getMinWidth: function(){
31249         return this.minWidth;
31250     },
31251     
31252     getMinHeight: function(){
31253         return this.minHeight;
31254     }
31255 });
31256
31257
31258 Roo.NorthLayoutRegion = function(mgr, config){
31259     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31260     if(this.split){
31261         this.split.placement = Roo.SplitBar.TOP;
31262         this.split.orientation = Roo.SplitBar.VERTICAL;
31263         this.split.el.addClass("x-layout-split-v");
31264     }
31265     var size = config.initialSize || config.height;
31266     if(typeof size != "undefined"){
31267         this.el.setHeight(size);
31268     }
31269 };
31270 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31271     orientation: Roo.SplitBar.VERTICAL,
31272     getBox : function(){
31273         if(this.collapsed){
31274             return this.collapsedEl.getBox();
31275         }
31276         var box = this.el.getBox();
31277         if(this.split){
31278             box.height += this.split.el.getHeight();
31279         }
31280         return box;
31281     },
31282     
31283     updateBox : function(box){
31284         if(this.split && !this.collapsed){
31285             box.height -= this.split.el.getHeight();
31286             this.split.el.setLeft(box.x);
31287             this.split.el.setTop(box.y+box.height);
31288             this.split.el.setWidth(box.width);
31289         }
31290         if(this.collapsed){
31291             this.updateBody(box.width, null);
31292         }
31293         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31294     }
31295 });
31296
31297 Roo.SouthLayoutRegion = function(mgr, config){
31298     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31299     if(this.split){
31300         this.split.placement = Roo.SplitBar.BOTTOM;
31301         this.split.orientation = Roo.SplitBar.VERTICAL;
31302         this.split.el.addClass("x-layout-split-v");
31303     }
31304     var size = config.initialSize || config.height;
31305     if(typeof size != "undefined"){
31306         this.el.setHeight(size);
31307     }
31308 };
31309 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31310     orientation: Roo.SplitBar.VERTICAL,
31311     getBox : function(){
31312         if(this.collapsed){
31313             return this.collapsedEl.getBox();
31314         }
31315         var box = this.el.getBox();
31316         if(this.split){
31317             var sh = this.split.el.getHeight();
31318             box.height += sh;
31319             box.y -= sh;
31320         }
31321         return box;
31322     },
31323     
31324     updateBox : function(box){
31325         if(this.split && !this.collapsed){
31326             var sh = this.split.el.getHeight();
31327             box.height -= sh;
31328             box.y += sh;
31329             this.split.el.setLeft(box.x);
31330             this.split.el.setTop(box.y-sh);
31331             this.split.el.setWidth(box.width);
31332         }
31333         if(this.collapsed){
31334             this.updateBody(box.width, null);
31335         }
31336         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31337     }
31338 });
31339
31340 Roo.EastLayoutRegion = function(mgr, config){
31341     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31342     if(this.split){
31343         this.split.placement = Roo.SplitBar.RIGHT;
31344         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31345         this.split.el.addClass("x-layout-split-h");
31346     }
31347     var size = config.initialSize || config.width;
31348     if(typeof size != "undefined"){
31349         this.el.setWidth(size);
31350     }
31351 };
31352 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31353     orientation: Roo.SplitBar.HORIZONTAL,
31354     getBox : function(){
31355         if(this.collapsed){
31356             return this.collapsedEl.getBox();
31357         }
31358         var box = this.el.getBox();
31359         if(this.split){
31360             var sw = this.split.el.getWidth();
31361             box.width += sw;
31362             box.x -= sw;
31363         }
31364         return box;
31365     },
31366
31367     updateBox : function(box){
31368         if(this.split && !this.collapsed){
31369             var sw = this.split.el.getWidth();
31370             box.width -= sw;
31371             this.split.el.setLeft(box.x);
31372             this.split.el.setTop(box.y);
31373             this.split.el.setHeight(box.height);
31374             box.x += sw;
31375         }
31376         if(this.collapsed){
31377             this.updateBody(null, box.height);
31378         }
31379         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31380     }
31381 });
31382
31383 Roo.WestLayoutRegion = function(mgr, config){
31384     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31385     if(this.split){
31386         this.split.placement = Roo.SplitBar.LEFT;
31387         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31388         this.split.el.addClass("x-layout-split-h");
31389     }
31390     var size = config.initialSize || config.width;
31391     if(typeof size != "undefined"){
31392         this.el.setWidth(size);
31393     }
31394 };
31395 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31396     orientation: Roo.SplitBar.HORIZONTAL,
31397     getBox : function(){
31398         if(this.collapsed){
31399             return this.collapsedEl.getBox();
31400         }
31401         var box = this.el.getBox();
31402         if(this.split){
31403             box.width += this.split.el.getWidth();
31404         }
31405         return box;
31406     },
31407     
31408     updateBox : function(box){
31409         if(this.split && !this.collapsed){
31410             var sw = this.split.el.getWidth();
31411             box.width -= sw;
31412             this.split.el.setLeft(box.x+box.width);
31413             this.split.el.setTop(box.y);
31414             this.split.el.setHeight(box.height);
31415         }
31416         if(this.collapsed){
31417             this.updateBody(null, box.height);
31418         }
31419         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31420     }
31421 });
31422 /*
31423  * Based on:
31424  * Ext JS Library 1.1.1
31425  * Copyright(c) 2006-2007, Ext JS, LLC.
31426  *
31427  * Originally Released Under LGPL - original licence link has changed is not relivant.
31428  *
31429  * Fork - LGPL
31430  * <script type="text/javascript">
31431  */
31432  
31433  
31434 /*
31435  * Private internal class for reading and applying state
31436  */
31437 Roo.LayoutStateManager = function(layout){
31438      // default empty state
31439      this.state = {
31440         north: {},
31441         south: {},
31442         east: {},
31443         west: {}       
31444     };
31445 };
31446
31447 Roo.LayoutStateManager.prototype = {
31448     init : function(layout, provider){
31449         this.provider = provider;
31450         var state = provider.get(layout.id+"-layout-state");
31451         if(state){
31452             var wasUpdating = layout.isUpdating();
31453             if(!wasUpdating){
31454                 layout.beginUpdate();
31455             }
31456             for(var key in state){
31457                 if(typeof state[key] != "function"){
31458                     var rstate = state[key];
31459                     var r = layout.getRegion(key);
31460                     if(r && rstate){
31461                         if(rstate.size){
31462                             r.resizeTo(rstate.size);
31463                         }
31464                         if(rstate.collapsed == true){
31465                             r.collapse(true);
31466                         }else{
31467                             r.expand(null, true);
31468                         }
31469                     }
31470                 }
31471             }
31472             if(!wasUpdating){
31473                 layout.endUpdate();
31474             }
31475             this.state = state; 
31476         }
31477         this.layout = layout;
31478         layout.on("regionresized", this.onRegionResized, this);
31479         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31480         layout.on("regionexpanded", this.onRegionExpanded, this);
31481     },
31482     
31483     storeState : function(){
31484         this.provider.set(this.layout.id+"-layout-state", this.state);
31485     },
31486     
31487     onRegionResized : function(region, newSize){
31488         this.state[region.getPosition()].size = newSize;
31489         this.storeState();
31490     },
31491     
31492     onRegionCollapsed : function(region){
31493         this.state[region.getPosition()].collapsed = true;
31494         this.storeState();
31495     },
31496     
31497     onRegionExpanded : function(region){
31498         this.state[region.getPosition()].collapsed = false;
31499         this.storeState();
31500     }
31501 };/*
31502  * Based on:
31503  * Ext JS Library 1.1.1
31504  * Copyright(c) 2006-2007, Ext JS, LLC.
31505  *
31506  * Originally Released Under LGPL - original licence link has changed is not relivant.
31507  *
31508  * Fork - LGPL
31509  * <script type="text/javascript">
31510  */
31511 /**
31512  * @class Roo.ContentPanel
31513  * @extends Roo.util.Observable
31514  * A basic ContentPanel element.
31515  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31516  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31517  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
31518  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31519  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31520  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31521  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31522  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31523  * @cfg {String} title          The title for this panel
31524  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31525  * @cfg {String} url            Calls {@link #setUrl} with this value
31526  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31527  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31528  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31529  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31530
31531  * @constructor
31532  * Create a new ContentPanel.
31533  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31534  * @param {String/Object} config A string to set only the title or a config object
31535  * @param {String} content (optional) Set the HTML content for this panel
31536  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31537  */
31538 Roo.ContentPanel = function(el, config, content){
31539     
31540      
31541     /*
31542     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31543         config = el;
31544         el = Roo.id();
31545     }
31546     if (config && config.parentLayout) { 
31547         el = config.parentLayout.el.createChild(); 
31548     }
31549     */
31550     if(el.autoCreate){ // xtype is available if this is called from factory
31551         config = el;
31552         el = Roo.id();
31553     }
31554     this.el = Roo.get(el);
31555     if(!this.el && config && config.autoCreate){
31556         if(typeof config.autoCreate == "object"){
31557             if(!config.autoCreate.id){
31558                 config.autoCreate.id = config.id||el;
31559             }
31560             this.el = Roo.DomHelper.append(document.body,
31561                         config.autoCreate, true);
31562         }else{
31563             this.el = Roo.DomHelper.append(document.body,
31564                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31565         }
31566     }
31567     this.closable = false;
31568     this.loaded = false;
31569     this.active = false;
31570     if(typeof config == "string"){
31571         this.title = config;
31572     }else{
31573         Roo.apply(this, config);
31574     }
31575     
31576     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31577         this.wrapEl = this.el.wrap();
31578         this.toolbar.container = this.el.insertSibling(false, 'before');
31579         this.toolbar = new Roo.Toolbar(this.toolbar);
31580     }
31581     
31582     // xtype created footer. - not sure if will work as we normally have to render first..
31583     if (this.footer && !this.footer.el && this.footer.xtype) {
31584         if (!this.wrapEl) {
31585             this.wrapEl = this.el.wrap();
31586         }
31587     
31588         this.footer.container = this.wrapEl.createChild();
31589          
31590         this.footer = Roo.factory(this.footer, Roo);
31591         
31592     }
31593     
31594     if(this.resizeEl){
31595         this.resizeEl = Roo.get(this.resizeEl, true);
31596     }else{
31597         this.resizeEl = this.el;
31598     }
31599     // handle view.xtype
31600     
31601  
31602     
31603     
31604     this.addEvents({
31605         /**
31606          * @event activate
31607          * Fires when this panel is activated. 
31608          * @param {Roo.ContentPanel} this
31609          */
31610         "activate" : true,
31611         /**
31612          * @event deactivate
31613          * Fires when this panel is activated. 
31614          * @param {Roo.ContentPanel} this
31615          */
31616         "deactivate" : true,
31617
31618         /**
31619          * @event resize
31620          * Fires when this panel is resized if fitToFrame is true.
31621          * @param {Roo.ContentPanel} this
31622          * @param {Number} width The width after any component adjustments
31623          * @param {Number} height The height after any component adjustments
31624          */
31625         "resize" : true,
31626         
31627          /**
31628          * @event render
31629          * Fires when this tab is created
31630          * @param {Roo.ContentPanel} this
31631          */
31632         "render" : true
31633          
31634         
31635     });
31636     
31637
31638     
31639     
31640     if(this.autoScroll){
31641         this.resizeEl.setStyle("overflow", "auto");
31642     } else {
31643         // fix randome scrolling
31644         this.el.on('scroll', function() {
31645             Roo.log('fix random scolling');
31646             this.scrollTo('top',0); 
31647         });
31648     }
31649     content = content || this.content;
31650     if(content){
31651         this.setContent(content);
31652     }
31653     if(config && config.url){
31654         this.setUrl(this.url, this.params, this.loadOnce);
31655     }
31656     
31657     
31658     
31659     Roo.ContentPanel.superclass.constructor.call(this);
31660     
31661     if (this.view && typeof(this.view.xtype) != 'undefined') {
31662         this.view.el = this.el.appendChild(document.createElement("div"));
31663         this.view = Roo.factory(this.view); 
31664         this.view.render  &&  this.view.render(false, '');  
31665     }
31666     
31667     
31668     this.fireEvent('render', this);
31669 };
31670
31671 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31672     tabTip:'',
31673     setRegion : function(region){
31674         this.region = region;
31675         if(region){
31676            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31677         }else{
31678            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31679         } 
31680     },
31681     
31682     /**
31683      * Returns the toolbar for this Panel if one was configured. 
31684      * @return {Roo.Toolbar} 
31685      */
31686     getToolbar : function(){
31687         return this.toolbar;
31688     },
31689     
31690     setActiveState : function(active){
31691         this.active = active;
31692         if(!active){
31693             this.fireEvent("deactivate", this);
31694         }else{
31695             this.fireEvent("activate", this);
31696         }
31697     },
31698     /**
31699      * Updates this panel's element
31700      * @param {String} content The new content
31701      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31702     */
31703     setContent : function(content, loadScripts){
31704         this.el.update(content, loadScripts);
31705     },
31706
31707     ignoreResize : function(w, h){
31708         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31709             return true;
31710         }else{
31711             this.lastSize = {width: w, height: h};
31712             return false;
31713         }
31714     },
31715     /**
31716      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31717      * @return {Roo.UpdateManager} The UpdateManager
31718      */
31719     getUpdateManager : function(){
31720         return this.el.getUpdateManager();
31721     },
31722      /**
31723      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31724      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
31725 <pre><code>
31726 panel.load({
31727     url: "your-url.php",
31728     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31729     callback: yourFunction,
31730     scope: yourObject, //(optional scope)
31731     discardUrl: false,
31732     nocache: false,
31733     text: "Loading...",
31734     timeout: 30,
31735     scripts: false
31736 });
31737 </code></pre>
31738      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31739      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
31740      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
31741      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31742      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
31743      * @return {Roo.ContentPanel} this
31744      */
31745     load : function(){
31746         var um = this.el.getUpdateManager();
31747         um.update.apply(um, arguments);
31748         return this;
31749     },
31750
31751
31752     /**
31753      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
31754      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31755      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
31756      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
31757      * @return {Roo.UpdateManager} The UpdateManager
31758      */
31759     setUrl : function(url, params, loadOnce){
31760         if(this.refreshDelegate){
31761             this.removeListener("activate", this.refreshDelegate);
31762         }
31763         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31764         this.on("activate", this.refreshDelegate);
31765         return this.el.getUpdateManager();
31766     },
31767     
31768     _handleRefresh : function(url, params, loadOnce){
31769         if(!loadOnce || !this.loaded){
31770             var updater = this.el.getUpdateManager();
31771             updater.update(url, params, this._setLoaded.createDelegate(this));
31772         }
31773     },
31774     
31775     _setLoaded : function(){
31776         this.loaded = true;
31777     }, 
31778     
31779     /**
31780      * Returns this panel's id
31781      * @return {String} 
31782      */
31783     getId : function(){
31784         return this.el.id;
31785     },
31786     
31787     /** 
31788      * Returns this panel's element - used by regiosn to add.
31789      * @return {Roo.Element} 
31790      */
31791     getEl : function(){
31792         return this.wrapEl || this.el;
31793     },
31794     
31795     adjustForComponents : function(width, height)
31796     {
31797         //Roo.log('adjustForComponents ');
31798         if(this.resizeEl != this.el){
31799             width -= this.el.getFrameWidth('lr');
31800             height -= this.el.getFrameWidth('tb');
31801         }
31802         if(this.toolbar){
31803             var te = this.toolbar.getEl();
31804             height -= te.getHeight();
31805             te.setWidth(width);
31806         }
31807         if(this.footer){
31808             var te = this.footer.getEl();
31809             //Roo.log("footer:" + te.getHeight());
31810             
31811             height -= te.getHeight();
31812             te.setWidth(width);
31813         }
31814         
31815         
31816         if(this.adjustments){
31817             width += this.adjustments[0];
31818             height += this.adjustments[1];
31819         }
31820         return {"width": width, "height": height};
31821     },
31822     
31823     setSize : function(width, height){
31824         if(this.fitToFrame && !this.ignoreResize(width, height)){
31825             if(this.fitContainer && this.resizeEl != this.el){
31826                 this.el.setSize(width, height);
31827             }
31828             var size = this.adjustForComponents(width, height);
31829             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31830             this.fireEvent('resize', this, size.width, size.height);
31831         }
31832     },
31833     
31834     /**
31835      * Returns this panel's title
31836      * @return {String} 
31837      */
31838     getTitle : function(){
31839         return this.title;
31840     },
31841     
31842     /**
31843      * Set this panel's title
31844      * @param {String} title
31845      */
31846     setTitle : function(title){
31847         this.title = title;
31848         if(this.region){
31849             this.region.updatePanelTitle(this, title);
31850         }
31851     },
31852     
31853     /**
31854      * Returns true is this panel was configured to be closable
31855      * @return {Boolean} 
31856      */
31857     isClosable : function(){
31858         return this.closable;
31859     },
31860     
31861     beforeSlide : function(){
31862         this.el.clip();
31863         this.resizeEl.clip();
31864     },
31865     
31866     afterSlide : function(){
31867         this.el.unclip();
31868         this.resizeEl.unclip();
31869     },
31870     
31871     /**
31872      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31873      *   Will fail silently if the {@link #setUrl} method has not been called.
31874      *   This does not activate the panel, just updates its content.
31875      */
31876     refresh : function(){
31877         if(this.refreshDelegate){
31878            this.loaded = false;
31879            this.refreshDelegate();
31880         }
31881     },
31882     
31883     /**
31884      * Destroys this panel
31885      */
31886     destroy : function(){
31887         this.el.removeAllListeners();
31888         var tempEl = document.createElement("span");
31889         tempEl.appendChild(this.el.dom);
31890         tempEl.innerHTML = "";
31891         this.el.remove();
31892         this.el = null;
31893     },
31894     
31895     /**
31896      * form - if the content panel contains a form - this is a reference to it.
31897      * @type {Roo.form.Form}
31898      */
31899     form : false,
31900     /**
31901      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31902      *    This contains a reference to it.
31903      * @type {Roo.View}
31904      */
31905     view : false,
31906     
31907       /**
31908      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31909      * <pre><code>
31910
31911 layout.addxtype({
31912        xtype : 'Form',
31913        items: [ .... ]
31914    }
31915 );
31916
31917 </code></pre>
31918      * @param {Object} cfg Xtype definition of item to add.
31919      */
31920     
31921     addxtype : function(cfg) {
31922         // add form..
31923         if (cfg.xtype.match(/^Form$/)) {
31924             
31925             var el;
31926             //if (this.footer) {
31927             //    el = this.footer.container.insertSibling(false, 'before');
31928             //} else {
31929                 el = this.el.createChild();
31930             //}
31931
31932             this.form = new  Roo.form.Form(cfg);
31933             
31934             
31935             if ( this.form.allItems.length) {
31936                 this.form.render(el.dom);
31937             }
31938             return this.form;
31939         }
31940         // should only have one of theses..
31941         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31942             // views.. should not be just added - used named prop 'view''
31943             
31944             cfg.el = this.el.appendChild(document.createElement("div"));
31945             // factory?
31946             
31947             var ret = new Roo.factory(cfg);
31948              
31949              ret.render && ret.render(false, ''); // render blank..
31950             this.view = ret;
31951             return ret;
31952         }
31953         return false;
31954     }
31955 });
31956
31957 /**
31958  * @class Roo.GridPanel
31959  * @extends Roo.ContentPanel
31960  * @constructor
31961  * Create a new GridPanel.
31962  * @param {Roo.grid.Grid} grid The grid for this panel
31963  * @param {String/Object} config A string to set only the panel's title, or a config object
31964  */
31965 Roo.GridPanel = function(grid, config){
31966     
31967   
31968     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31969         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31970         
31971     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31972     
31973     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31974     
31975     if(this.toolbar){
31976         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31977     }
31978     // xtype created footer. - not sure if will work as we normally have to render first..
31979     if (this.footer && !this.footer.el && this.footer.xtype) {
31980         
31981         this.footer.container = this.grid.getView().getFooterPanel(true);
31982         this.footer.dataSource = this.grid.dataSource;
31983         this.footer = Roo.factory(this.footer, Roo);
31984         
31985     }
31986     
31987     grid.monitorWindowResize = false; // turn off autosizing
31988     grid.autoHeight = false;
31989     grid.autoWidth = false;
31990     this.grid = grid;
31991     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31992 };
31993
31994 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31995     getId : function(){
31996         return this.grid.id;
31997     },
31998     
31999     /**
32000      * Returns the grid for this panel
32001      * @return {Roo.grid.Grid} 
32002      */
32003     getGrid : function(){
32004         return this.grid;    
32005     },
32006     
32007     setSize : function(width, height){
32008         if(!this.ignoreResize(width, height)){
32009             var grid = this.grid;
32010             var size = this.adjustForComponents(width, height);
32011             grid.getGridEl().setSize(size.width, size.height);
32012             grid.autoSize();
32013         }
32014     },
32015     
32016     beforeSlide : function(){
32017         this.grid.getView().scroller.clip();
32018     },
32019     
32020     afterSlide : function(){
32021         this.grid.getView().scroller.unclip();
32022     },
32023     
32024     destroy : function(){
32025         this.grid.destroy();
32026         delete this.grid;
32027         Roo.GridPanel.superclass.destroy.call(this); 
32028     }
32029 });
32030
32031
32032 /**
32033  * @class Roo.NestedLayoutPanel
32034  * @extends Roo.ContentPanel
32035  * @constructor
32036  * Create a new NestedLayoutPanel.
32037  * 
32038  * 
32039  * @param {Roo.BorderLayout} layout The layout for this panel
32040  * @param {String/Object} config A string to set only the title or a config object
32041  */
32042 Roo.NestedLayoutPanel = function(layout, config)
32043 {
32044     // construct with only one argument..
32045     /* FIXME - implement nicer consturctors
32046     if (layout.layout) {
32047         config = layout;
32048         layout = config.layout;
32049         delete config.layout;
32050     }
32051     if (layout.xtype && !layout.getEl) {
32052         // then layout needs constructing..
32053         layout = Roo.factory(layout, Roo);
32054     }
32055     */
32056     
32057     
32058     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32059     
32060     layout.monitorWindowResize = false; // turn off autosizing
32061     this.layout = layout;
32062     this.layout.getEl().addClass("x-layout-nested-layout");
32063     
32064     
32065     
32066     
32067 };
32068
32069 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32070
32071     setSize : function(width, height){
32072         if(!this.ignoreResize(width, height)){
32073             var size = this.adjustForComponents(width, height);
32074             var el = this.layout.getEl();
32075             el.setSize(size.width, size.height);
32076             var touch = el.dom.offsetWidth;
32077             this.layout.layout();
32078             // ie requires a double layout on the first pass
32079             if(Roo.isIE && !this.initialized){
32080                 this.initialized = true;
32081                 this.layout.layout();
32082             }
32083         }
32084     },
32085     
32086     // activate all subpanels if not currently active..
32087     
32088     setActiveState : function(active){
32089         this.active = active;
32090         if(!active){
32091             this.fireEvent("deactivate", this);
32092             return;
32093         }
32094         
32095         this.fireEvent("activate", this);
32096         // not sure if this should happen before or after..
32097         if (!this.layout) {
32098             return; // should not happen..
32099         }
32100         var reg = false;
32101         for (var r in this.layout.regions) {
32102             reg = this.layout.getRegion(r);
32103             if (reg.getActivePanel()) {
32104                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32105                 reg.setActivePanel(reg.getActivePanel());
32106                 continue;
32107             }
32108             if (!reg.panels.length) {
32109                 continue;
32110             }
32111             reg.showPanel(reg.getPanel(0));
32112         }
32113         
32114         
32115         
32116         
32117     },
32118     
32119     /**
32120      * Returns the nested BorderLayout for this panel
32121      * @return {Roo.BorderLayout} 
32122      */
32123     getLayout : function(){
32124         return this.layout;
32125     },
32126     
32127      /**
32128      * Adds a xtype elements to the layout of the nested panel
32129      * <pre><code>
32130
32131 panel.addxtype({
32132        xtype : 'ContentPanel',
32133        region: 'west',
32134        items: [ .... ]
32135    }
32136 );
32137
32138 panel.addxtype({
32139         xtype : 'NestedLayoutPanel',
32140         region: 'west',
32141         layout: {
32142            center: { },
32143            west: { }   
32144         },
32145         items : [ ... list of content panels or nested layout panels.. ]
32146    }
32147 );
32148 </code></pre>
32149      * @param {Object} cfg Xtype definition of item to add.
32150      */
32151     addxtype : function(cfg) {
32152         return this.layout.addxtype(cfg);
32153     
32154     }
32155 });
32156
32157 Roo.ScrollPanel = function(el, config, content){
32158     config = config || {};
32159     config.fitToFrame = true;
32160     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32161     
32162     this.el.dom.style.overflow = "hidden";
32163     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32164     this.el.removeClass("x-layout-inactive-content");
32165     this.el.on("mousewheel", this.onWheel, this);
32166
32167     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32168     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32169     up.unselectable(); down.unselectable();
32170     up.on("click", this.scrollUp, this);
32171     down.on("click", this.scrollDown, this);
32172     up.addClassOnOver("x-scroller-btn-over");
32173     down.addClassOnOver("x-scroller-btn-over");
32174     up.addClassOnClick("x-scroller-btn-click");
32175     down.addClassOnClick("x-scroller-btn-click");
32176     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32177
32178     this.resizeEl = this.el;
32179     this.el = wrap; this.up = up; this.down = down;
32180 };
32181
32182 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32183     increment : 100,
32184     wheelIncrement : 5,
32185     scrollUp : function(){
32186         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32187     },
32188
32189     scrollDown : function(){
32190         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32191     },
32192
32193     afterScroll : function(){
32194         var el = this.resizeEl;
32195         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32196         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32197         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32198     },
32199
32200     setSize : function(){
32201         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32202         this.afterScroll();
32203     },
32204
32205     onWheel : function(e){
32206         var d = e.getWheelDelta();
32207         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32208         this.afterScroll();
32209         e.stopEvent();
32210     },
32211
32212     setContent : function(content, loadScripts){
32213         this.resizeEl.update(content, loadScripts);
32214     }
32215
32216 });
32217
32218
32219
32220
32221
32222
32223
32224
32225
32226 /**
32227  * @class Roo.TreePanel
32228  * @extends Roo.ContentPanel
32229  * @constructor
32230  * Create a new TreePanel. - defaults to fit/scoll contents.
32231  * @param {String/Object} config A string to set only the panel's title, or a config object
32232  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32233  */
32234 Roo.TreePanel = function(config){
32235     var el = config.el;
32236     var tree = config.tree;
32237     delete config.tree; 
32238     delete config.el; // hopefull!
32239     
32240     // wrapper for IE7 strict & safari scroll issue
32241     
32242     var treeEl = el.createChild();
32243     config.resizeEl = treeEl;
32244     
32245     
32246     
32247     Roo.TreePanel.superclass.constructor.call(this, el, config);
32248  
32249  
32250     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32251     //console.log(tree);
32252     this.on('activate', function()
32253     {
32254         if (this.tree.rendered) {
32255             return;
32256         }
32257         //console.log('render tree');
32258         this.tree.render();
32259     });
32260     // this should not be needed.. - it's actually the 'el' that resizes?
32261     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32262     
32263     //this.on('resize',  function (cp, w, h) {
32264     //        this.tree.innerCt.setWidth(w);
32265     //        this.tree.innerCt.setHeight(h);
32266     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32267     //});
32268
32269         
32270     
32271 };
32272
32273 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32274     fitToFrame : true,
32275     autoScroll : true
32276 });
32277
32278
32279
32280
32281
32282
32283
32284
32285
32286
32287
32288 /*
32289  * Based on:
32290  * Ext JS Library 1.1.1
32291  * Copyright(c) 2006-2007, Ext JS, LLC.
32292  *
32293  * Originally Released Under LGPL - original licence link has changed is not relivant.
32294  *
32295  * Fork - LGPL
32296  * <script type="text/javascript">
32297  */
32298  
32299
32300 /**
32301  * @class Roo.ReaderLayout
32302  * @extends Roo.BorderLayout
32303  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32304  * center region containing two nested regions (a top one for a list view and one for item preview below),
32305  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32306  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32307  * expedites the setup of the overall layout and regions for this common application style.
32308  * Example:
32309  <pre><code>
32310 var reader = new Roo.ReaderLayout();
32311 var CP = Roo.ContentPanel;  // shortcut for adding
32312
32313 reader.beginUpdate();
32314 reader.add("north", new CP("north", "North"));
32315 reader.add("west", new CP("west", {title: "West"}));
32316 reader.add("east", new CP("east", {title: "East"}));
32317
32318 reader.regions.listView.add(new CP("listView", "List"));
32319 reader.regions.preview.add(new CP("preview", "Preview"));
32320 reader.endUpdate();
32321 </code></pre>
32322 * @constructor
32323 * Create a new ReaderLayout
32324 * @param {Object} config Configuration options
32325 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32326 * document.body if omitted)
32327 */
32328 Roo.ReaderLayout = function(config, renderTo){
32329     var c = config || {size:{}};
32330     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32331         north: c.north !== false ? Roo.apply({
32332             split:false,
32333             initialSize: 32,
32334             titlebar: false
32335         }, c.north) : false,
32336         west: c.west !== false ? Roo.apply({
32337             split:true,
32338             initialSize: 200,
32339             minSize: 175,
32340             maxSize: 400,
32341             titlebar: true,
32342             collapsible: true,
32343             animate: true,
32344             margins:{left:5,right:0,bottom:5,top:5},
32345             cmargins:{left:5,right:5,bottom:5,top:5}
32346         }, c.west) : false,
32347         east: c.east !== false ? Roo.apply({
32348             split:true,
32349             initialSize: 200,
32350             minSize: 175,
32351             maxSize: 400,
32352             titlebar: true,
32353             collapsible: true,
32354             animate: true,
32355             margins:{left:0,right:5,bottom:5,top:5},
32356             cmargins:{left:5,right:5,bottom:5,top:5}
32357         }, c.east) : false,
32358         center: Roo.apply({
32359             tabPosition: 'top',
32360             autoScroll:false,
32361             closeOnTab: true,
32362             titlebar:false,
32363             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32364         }, c.center)
32365     });
32366
32367     this.el.addClass('x-reader');
32368
32369     this.beginUpdate();
32370
32371     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32372         south: c.preview !== false ? Roo.apply({
32373             split:true,
32374             initialSize: 200,
32375             minSize: 100,
32376             autoScroll:true,
32377             collapsible:true,
32378             titlebar: true,
32379             cmargins:{top:5,left:0, right:0, bottom:0}
32380         }, c.preview) : false,
32381         center: Roo.apply({
32382             autoScroll:false,
32383             titlebar:false,
32384             minHeight:200
32385         }, c.listView)
32386     });
32387     this.add('center', new Roo.NestedLayoutPanel(inner,
32388             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32389
32390     this.endUpdate();
32391
32392     this.regions.preview = inner.getRegion('south');
32393     this.regions.listView = inner.getRegion('center');
32394 };
32395
32396 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32397  * Based on:
32398  * Ext JS Library 1.1.1
32399  * Copyright(c) 2006-2007, Ext JS, LLC.
32400  *
32401  * Originally Released Under LGPL - original licence link has changed is not relivant.
32402  *
32403  * Fork - LGPL
32404  * <script type="text/javascript">
32405  */
32406  
32407 /**
32408  * @class Roo.grid.Grid
32409  * @extends Roo.util.Observable
32410  * This class represents the primary interface of a component based grid control.
32411  * <br><br>Usage:<pre><code>
32412  var grid = new Roo.grid.Grid("my-container-id", {
32413      ds: myDataStore,
32414      cm: myColModel,
32415      selModel: mySelectionModel,
32416      autoSizeColumns: true,
32417      monitorWindowResize: false,
32418      trackMouseOver: true
32419  });
32420  // set any options
32421  grid.render();
32422  * </code></pre>
32423  * <b>Common Problems:</b><br/>
32424  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32425  * element will correct this<br/>
32426  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32427  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32428  * are unpredictable.<br/>
32429  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32430  * grid to calculate dimensions/offsets.<br/>
32431   * @constructor
32432  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32433  * The container MUST have some type of size defined for the grid to fill. The container will be
32434  * automatically set to position relative if it isn't already.
32435  * @param {Object} config A config object that sets properties on this grid.
32436  */
32437 Roo.grid.Grid = function(container, config){
32438         // initialize the container
32439         this.container = Roo.get(container);
32440         this.container.update("");
32441         this.container.setStyle("overflow", "hidden");
32442     this.container.addClass('x-grid-container');
32443
32444     this.id = this.container.id;
32445
32446     Roo.apply(this, config);
32447     // check and correct shorthanded configs
32448     if(this.ds){
32449         this.dataSource = this.ds;
32450         delete this.ds;
32451     }
32452     if(this.cm){
32453         this.colModel = this.cm;
32454         delete this.cm;
32455     }
32456     if(this.sm){
32457         this.selModel = this.sm;
32458         delete this.sm;
32459     }
32460
32461     if (this.selModel) {
32462         this.selModel = Roo.factory(this.selModel, Roo.grid);
32463         this.sm = this.selModel;
32464         this.sm.xmodule = this.xmodule || false;
32465     }
32466     if (typeof(this.colModel.config) == 'undefined') {
32467         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32468         this.cm = this.colModel;
32469         this.cm.xmodule = this.xmodule || false;
32470     }
32471     if (this.dataSource) {
32472         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32473         this.ds = this.dataSource;
32474         this.ds.xmodule = this.xmodule || false;
32475          
32476     }
32477     
32478     
32479     
32480     if(this.width){
32481         this.container.setWidth(this.width);
32482     }
32483
32484     if(this.height){
32485         this.container.setHeight(this.height);
32486     }
32487     /** @private */
32488         this.addEvents({
32489         // raw events
32490         /**
32491          * @event click
32492          * The raw click event for the entire grid.
32493          * @param {Roo.EventObject} e
32494          */
32495         "click" : true,
32496         /**
32497          * @event dblclick
32498          * The raw dblclick event for the entire grid.
32499          * @param {Roo.EventObject} e
32500          */
32501         "dblclick" : true,
32502         /**
32503          * @event contextmenu
32504          * The raw contextmenu event for the entire grid.
32505          * @param {Roo.EventObject} e
32506          */
32507         "contextmenu" : true,
32508         /**
32509          * @event mousedown
32510          * The raw mousedown event for the entire grid.
32511          * @param {Roo.EventObject} e
32512          */
32513         "mousedown" : true,
32514         /**
32515          * @event mouseup
32516          * The raw mouseup event for the entire grid.
32517          * @param {Roo.EventObject} e
32518          */
32519         "mouseup" : true,
32520         /**
32521          * @event mouseover
32522          * The raw mouseover event for the entire grid.
32523          * @param {Roo.EventObject} e
32524          */
32525         "mouseover" : true,
32526         /**
32527          * @event mouseout
32528          * The raw mouseout event for the entire grid.
32529          * @param {Roo.EventObject} e
32530          */
32531         "mouseout" : true,
32532         /**
32533          * @event keypress
32534          * The raw keypress event for the entire grid.
32535          * @param {Roo.EventObject} e
32536          */
32537         "keypress" : true,
32538         /**
32539          * @event keydown
32540          * The raw keydown event for the entire grid.
32541          * @param {Roo.EventObject} e
32542          */
32543         "keydown" : true,
32544
32545         // custom events
32546
32547         /**
32548          * @event cellclick
32549          * Fires when a cell is clicked
32550          * @param {Grid} this
32551          * @param {Number} rowIndex
32552          * @param {Number} columnIndex
32553          * @param {Roo.EventObject} e
32554          */
32555         "cellclick" : true,
32556         /**
32557          * @event celldblclick
32558          * Fires when a cell is double clicked
32559          * @param {Grid} this
32560          * @param {Number} rowIndex
32561          * @param {Number} columnIndex
32562          * @param {Roo.EventObject} e
32563          */
32564         "celldblclick" : true,
32565         /**
32566          * @event rowclick
32567          * Fires when a row is clicked
32568          * @param {Grid} this
32569          * @param {Number} rowIndex
32570          * @param {Roo.EventObject} e
32571          */
32572         "rowclick" : true,
32573         /**
32574          * @event rowdblclick
32575          * Fires when a row is double clicked
32576          * @param {Grid} this
32577          * @param {Number} rowIndex
32578          * @param {Roo.EventObject} e
32579          */
32580         "rowdblclick" : true,
32581         /**
32582          * @event headerclick
32583          * Fires when a header is clicked
32584          * @param {Grid} this
32585          * @param {Number} columnIndex
32586          * @param {Roo.EventObject} e
32587          */
32588         "headerclick" : true,
32589         /**
32590          * @event headerdblclick
32591          * Fires when a header cell is double clicked
32592          * @param {Grid} this
32593          * @param {Number} columnIndex
32594          * @param {Roo.EventObject} e
32595          */
32596         "headerdblclick" : true,
32597         /**
32598          * @event rowcontextmenu
32599          * Fires when a row is right clicked
32600          * @param {Grid} this
32601          * @param {Number} rowIndex
32602          * @param {Roo.EventObject} e
32603          */
32604         "rowcontextmenu" : true,
32605         /**
32606          * @event cellcontextmenu
32607          * Fires when a cell is right clicked
32608          * @param {Grid} this
32609          * @param {Number} rowIndex
32610          * @param {Number} cellIndex
32611          * @param {Roo.EventObject} e
32612          */
32613          "cellcontextmenu" : true,
32614         /**
32615          * @event headercontextmenu
32616          * Fires when a header is right clicked
32617          * @param {Grid} this
32618          * @param {Number} columnIndex
32619          * @param {Roo.EventObject} e
32620          */
32621         "headercontextmenu" : true,
32622         /**
32623          * @event bodyscroll
32624          * Fires when the body element is scrolled
32625          * @param {Number} scrollLeft
32626          * @param {Number} scrollTop
32627          */
32628         "bodyscroll" : true,
32629         /**
32630          * @event columnresize
32631          * Fires when the user resizes a column
32632          * @param {Number} columnIndex
32633          * @param {Number} newSize
32634          */
32635         "columnresize" : true,
32636         /**
32637          * @event columnmove
32638          * Fires when the user moves a column
32639          * @param {Number} oldIndex
32640          * @param {Number} newIndex
32641          */
32642         "columnmove" : true,
32643         /**
32644          * @event startdrag
32645          * Fires when row(s) start being dragged
32646          * @param {Grid} this
32647          * @param {Roo.GridDD} dd The drag drop object
32648          * @param {event} e The raw browser event
32649          */
32650         "startdrag" : true,
32651         /**
32652          * @event enddrag
32653          * Fires when a drag operation is complete
32654          * @param {Grid} this
32655          * @param {Roo.GridDD} dd The drag drop object
32656          * @param {event} e The raw browser event
32657          */
32658         "enddrag" : true,
32659         /**
32660          * @event dragdrop
32661          * Fires when dragged row(s) are dropped on a valid DD target
32662          * @param {Grid} this
32663          * @param {Roo.GridDD} dd The drag drop object
32664          * @param {String} targetId The target drag drop object
32665          * @param {event} e The raw browser event
32666          */
32667         "dragdrop" : true,
32668         /**
32669          * @event dragover
32670          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32671          * @param {Grid} this
32672          * @param {Roo.GridDD} dd The drag drop object
32673          * @param {String} targetId The target drag drop object
32674          * @param {event} e The raw browser event
32675          */
32676         "dragover" : true,
32677         /**
32678          * @event dragenter
32679          *  Fires when the dragged row(s) first cross another DD target while being dragged
32680          * @param {Grid} this
32681          * @param {Roo.GridDD} dd The drag drop object
32682          * @param {String} targetId The target drag drop object
32683          * @param {event} e The raw browser event
32684          */
32685         "dragenter" : true,
32686         /**
32687          * @event dragout
32688          * Fires when the dragged row(s) leave another DD target while being dragged
32689          * @param {Grid} this
32690          * @param {Roo.GridDD} dd The drag drop object
32691          * @param {String} targetId The target drag drop object
32692          * @param {event} e The raw browser event
32693          */
32694         "dragout" : true,
32695         /**
32696          * @event rowclass
32697          * Fires when a row is rendered, so you can change add a style to it.
32698          * @param {GridView} gridview   The grid view
32699          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32700          */
32701         'rowclass' : true,
32702
32703         /**
32704          * @event render
32705          * Fires when the grid is rendered
32706          * @param {Grid} grid
32707          */
32708         'render' : true
32709     });
32710
32711     Roo.grid.Grid.superclass.constructor.call(this);
32712 };
32713 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32714     
32715     /**
32716      * @cfg {String} ddGroup - drag drop group.
32717      */
32718
32719     /**
32720      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32721      */
32722     minColumnWidth : 25,
32723
32724     /**
32725      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32726      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32727      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32728      */
32729     autoSizeColumns : false,
32730
32731     /**
32732      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32733      */
32734     autoSizeHeaders : true,
32735
32736     /**
32737      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32738      */
32739     monitorWindowResize : true,
32740
32741     /**
32742      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32743      * rows measured to get a columns size. Default is 0 (all rows).
32744      */
32745     maxRowsToMeasure : 0,
32746
32747     /**
32748      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32749      */
32750     trackMouseOver : true,
32751
32752     /**
32753     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32754     */
32755     
32756     /**
32757     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32758     */
32759     enableDragDrop : false,
32760     
32761     /**
32762     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32763     */
32764     enableColumnMove : true,
32765     
32766     /**
32767     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32768     */
32769     enableColumnHide : true,
32770     
32771     /**
32772     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32773     */
32774     enableRowHeightSync : false,
32775     
32776     /**
32777     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32778     */
32779     stripeRows : true,
32780     
32781     /**
32782     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32783     */
32784     autoHeight : false,
32785
32786     /**
32787      * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
32788      */
32789     autoExpandColumn : false,
32790
32791     /**
32792     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32793     * Default is 50.
32794     */
32795     autoExpandMin : 50,
32796
32797     /**
32798     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32799     */
32800     autoExpandMax : 1000,
32801
32802     /**
32803     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32804     */
32805     view : null,
32806
32807     /**
32808     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32809     */
32810     loadMask : false,
32811     /**
32812     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32813     */
32814     dropTarget: false,
32815     
32816    
32817     
32818     // private
32819     rendered : false,
32820
32821     /**
32822     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32823     * of a fixed width. Default is false.
32824     */
32825     /**
32826     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32827     */
32828     /**
32829      * Called once after all setup has been completed and the grid is ready to be rendered.
32830      * @return {Roo.grid.Grid} this
32831      */
32832     render : function()
32833     {
32834         var c = this.container;
32835         // try to detect autoHeight/width mode
32836         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32837             this.autoHeight = true;
32838         }
32839         var view = this.getView();
32840         view.init(this);
32841
32842         c.on("click", this.onClick, this);
32843         c.on("dblclick", this.onDblClick, this);
32844         c.on("contextmenu", this.onContextMenu, this);
32845         c.on("keydown", this.onKeyDown, this);
32846         if (Roo.isTouch) {
32847             c.on("touchstart", this.onTouchStart, this);
32848         }
32849
32850         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32851
32852         this.getSelectionModel().init(this);
32853
32854         view.render();
32855
32856         if(this.loadMask){
32857             this.loadMask = new Roo.LoadMask(this.container,
32858                     Roo.apply({store:this.dataSource}, this.loadMask));
32859         }
32860         
32861         
32862         if (this.toolbar && this.toolbar.xtype) {
32863             this.toolbar.container = this.getView().getHeaderPanel(true);
32864             this.toolbar = new Roo.Toolbar(this.toolbar);
32865         }
32866         if (this.footer && this.footer.xtype) {
32867             this.footer.dataSource = this.getDataSource();
32868             this.footer.container = this.getView().getFooterPanel(true);
32869             this.footer = Roo.factory(this.footer, Roo);
32870         }
32871         if (this.dropTarget && this.dropTarget.xtype) {
32872             delete this.dropTarget.xtype;
32873             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32874         }
32875         
32876         
32877         this.rendered = true;
32878         this.fireEvent('render', this);
32879         return this;
32880     },
32881
32882         /**
32883          * Reconfigures the grid to use a different Store and Column Model.
32884          * The View will be bound to the new objects and refreshed.
32885          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32886          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32887          */
32888     reconfigure : function(dataSource, colModel){
32889         if(this.loadMask){
32890             this.loadMask.destroy();
32891             this.loadMask = new Roo.LoadMask(this.container,
32892                     Roo.apply({store:dataSource}, this.loadMask));
32893         }
32894         this.view.bind(dataSource, colModel);
32895         this.dataSource = dataSource;
32896         this.colModel = colModel;
32897         this.view.refresh(true);
32898     },
32899
32900     // private
32901     onKeyDown : function(e){
32902         this.fireEvent("keydown", e);
32903     },
32904
32905     /**
32906      * Destroy this grid.
32907      * @param {Boolean} removeEl True to remove the element
32908      */
32909     destroy : function(removeEl, keepListeners){
32910         if(this.loadMask){
32911             this.loadMask.destroy();
32912         }
32913         var c = this.container;
32914         c.removeAllListeners();
32915         this.view.destroy();
32916         this.colModel.purgeListeners();
32917         if(!keepListeners){
32918             this.purgeListeners();
32919         }
32920         c.update("");
32921         if(removeEl === true){
32922             c.remove();
32923         }
32924     },
32925
32926     // private
32927     processEvent : function(name, e){
32928         // does this fire select???
32929         //Roo.log('grid:processEvent '  + name);
32930         
32931         if (name != 'touchstart' ) {
32932             this.fireEvent(name, e);    
32933         }
32934         
32935         var t = e.getTarget();
32936         var v = this.view;
32937         var header = v.findHeaderIndex(t);
32938         if(header !== false){
32939             var ename = name == 'touchstart' ? 'click' : name;
32940              
32941             this.fireEvent("header" + ename, this, header, e);
32942         }else{
32943             var row = v.findRowIndex(t);
32944             var cell = v.findCellIndex(t);
32945             if (name == 'touchstart') {
32946                 // first touch is always a click.
32947                 // hopefull this happens after selection is updated.?
32948                 name = false;
32949                 
32950                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32951                     var cs = this.selModel.getSelectedCell();
32952                     if (row == cs[0] && cell == cs[1]){
32953                         name = 'dblclick';
32954                     }
32955                 }
32956                 if (typeof(this.selModel.getSelections) != 'undefined') {
32957                     var cs = this.selModel.getSelections();
32958                     var ds = this.dataSource;
32959                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32960                         name = 'dblclick';
32961                     }
32962                 }
32963                 if (!name) {
32964                     return;
32965                 }
32966             }
32967             
32968             
32969             if(row !== false){
32970                 this.fireEvent("row" + name, this, row, e);
32971                 if(cell !== false){
32972                     this.fireEvent("cell" + name, this, row, cell, e);
32973                 }
32974             }
32975         }
32976     },
32977
32978     // private
32979     onClick : function(e){
32980         this.processEvent("click", e);
32981     },
32982    // private
32983     onTouchStart : function(e){
32984         this.processEvent("touchstart", e);
32985     },
32986
32987     // private
32988     onContextMenu : function(e, t){
32989         this.processEvent("contextmenu", e);
32990     },
32991
32992     // private
32993     onDblClick : function(e){
32994         this.processEvent("dblclick", e);
32995     },
32996
32997     // private
32998     walkCells : function(row, col, step, fn, scope){
32999         var cm = this.colModel, clen = cm.getColumnCount();
33000         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33001         if(step < 0){
33002             if(col < 0){
33003                 row--;
33004                 first = false;
33005             }
33006             while(row >= 0){
33007                 if(!first){
33008                     col = clen-1;
33009                 }
33010                 first = false;
33011                 while(col >= 0){
33012                     if(fn.call(scope || this, row, col, cm) === true){
33013                         return [row, col];
33014                     }
33015                     col--;
33016                 }
33017                 row--;
33018             }
33019         } else {
33020             if(col >= clen){
33021                 row++;
33022                 first = false;
33023             }
33024             while(row < rlen){
33025                 if(!first){
33026                     col = 0;
33027                 }
33028                 first = false;
33029                 while(col < clen){
33030                     if(fn.call(scope || this, row, col, cm) === true){
33031                         return [row, col];
33032                     }
33033                     col++;
33034                 }
33035                 row++;
33036             }
33037         }
33038         return null;
33039     },
33040
33041     // private
33042     getSelections : function(){
33043         return this.selModel.getSelections();
33044     },
33045
33046     /**
33047      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33048      * but if manual update is required this method will initiate it.
33049      */
33050     autoSize : function(){
33051         if(this.rendered){
33052             this.view.layout();
33053             if(this.view.adjustForScroll){
33054                 this.view.adjustForScroll();
33055             }
33056         }
33057     },
33058
33059     /**
33060      * Returns the grid's underlying element.
33061      * @return {Element} The element
33062      */
33063     getGridEl : function(){
33064         return this.container;
33065     },
33066
33067     // private for compatibility, overridden by editor grid
33068     stopEditing : function(){},
33069
33070     /**
33071      * Returns the grid's SelectionModel.
33072      * @return {SelectionModel}
33073      */
33074     getSelectionModel : function(){
33075         if(!this.selModel){
33076             this.selModel = new Roo.grid.RowSelectionModel();
33077         }
33078         return this.selModel;
33079     },
33080
33081     /**
33082      * Returns the grid's DataSource.
33083      * @return {DataSource}
33084      */
33085     getDataSource : function(){
33086         return this.dataSource;
33087     },
33088
33089     /**
33090      * Returns the grid's ColumnModel.
33091      * @return {ColumnModel}
33092      */
33093     getColumnModel : function(){
33094         return this.colModel;
33095     },
33096
33097     /**
33098      * Returns the grid's GridView object.
33099      * @return {GridView}
33100      */
33101     getView : function(){
33102         if(!this.view){
33103             this.view = new Roo.grid.GridView(this.viewConfig);
33104         }
33105         return this.view;
33106     },
33107     /**
33108      * Called to get grid's drag proxy text, by default returns this.ddText.
33109      * @return {String}
33110      */
33111     getDragDropText : function(){
33112         var count = this.selModel.getCount();
33113         return String.format(this.ddText, count, count == 1 ? '' : 's');
33114     }
33115 });
33116 /**
33117  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33118  * %0 is replaced with the number of selected rows.
33119  * @type String
33120  */
33121 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33122  * Based on:
33123  * Ext JS Library 1.1.1
33124  * Copyright(c) 2006-2007, Ext JS, LLC.
33125  *
33126  * Originally Released Under LGPL - original licence link has changed is not relivant.
33127  *
33128  * Fork - LGPL
33129  * <script type="text/javascript">
33130  */
33131  
33132 Roo.grid.AbstractGridView = function(){
33133         this.grid = null;
33134         
33135         this.events = {
33136             "beforerowremoved" : true,
33137             "beforerowsinserted" : true,
33138             "beforerefresh" : true,
33139             "rowremoved" : true,
33140             "rowsinserted" : true,
33141             "rowupdated" : true,
33142             "refresh" : true
33143         };
33144     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33145 };
33146
33147 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33148     rowClass : "x-grid-row",
33149     cellClass : "x-grid-cell",
33150     tdClass : "x-grid-td",
33151     hdClass : "x-grid-hd",
33152     splitClass : "x-grid-hd-split",
33153     
33154     init: function(grid){
33155         this.grid = grid;
33156                 var cid = this.grid.getGridEl().id;
33157         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33158         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33159         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33160         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33161         },
33162         
33163     getColumnRenderers : function(){
33164         var renderers = [];
33165         var cm = this.grid.colModel;
33166         var colCount = cm.getColumnCount();
33167         for(var i = 0; i < colCount; i++){
33168             renderers[i] = cm.getRenderer(i);
33169         }
33170         return renderers;
33171     },
33172     
33173     getColumnIds : function(){
33174         var ids = [];
33175         var cm = this.grid.colModel;
33176         var colCount = cm.getColumnCount();
33177         for(var i = 0; i < colCount; i++){
33178             ids[i] = cm.getColumnId(i);
33179         }
33180         return ids;
33181     },
33182     
33183     getDataIndexes : function(){
33184         if(!this.indexMap){
33185             this.indexMap = this.buildIndexMap();
33186         }
33187         return this.indexMap.colToData;
33188     },
33189     
33190     getColumnIndexByDataIndex : function(dataIndex){
33191         if(!this.indexMap){
33192             this.indexMap = this.buildIndexMap();
33193         }
33194         return this.indexMap.dataToCol[dataIndex];
33195     },
33196     
33197     /**
33198      * Set a css style for a column dynamically. 
33199      * @param {Number} colIndex The index of the column
33200      * @param {String} name The css property name
33201      * @param {String} value The css value
33202      */
33203     setCSSStyle : function(colIndex, name, value){
33204         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33205         Roo.util.CSS.updateRule(selector, name, value);
33206     },
33207     
33208     generateRules : function(cm){
33209         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33210         Roo.util.CSS.removeStyleSheet(rulesId);
33211         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33212             var cid = cm.getColumnId(i);
33213             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33214                          this.tdSelector, cid, " {\n}\n",
33215                          this.hdSelector, cid, " {\n}\n",
33216                          this.splitSelector, cid, " {\n}\n");
33217         }
33218         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33219     }
33220 });/*
33221  * Based on:
33222  * Ext JS Library 1.1.1
33223  * Copyright(c) 2006-2007, Ext JS, LLC.
33224  *
33225  * Originally Released Under LGPL - original licence link has changed is not relivant.
33226  *
33227  * Fork - LGPL
33228  * <script type="text/javascript">
33229  */
33230
33231 // private
33232 // This is a support class used internally by the Grid components
33233 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33234     this.grid = grid;
33235     this.view = grid.getView();
33236     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33237     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33238     if(hd2){
33239         this.setHandleElId(Roo.id(hd));
33240         this.setOuterHandleElId(Roo.id(hd2));
33241     }
33242     this.scroll = false;
33243 };
33244 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33245     maxDragWidth: 120,
33246     getDragData : function(e){
33247         var t = Roo.lib.Event.getTarget(e);
33248         var h = this.view.findHeaderCell(t);
33249         if(h){
33250             return {ddel: h.firstChild, header:h};
33251         }
33252         return false;
33253     },
33254
33255     onInitDrag : function(e){
33256         this.view.headersDisabled = true;
33257         var clone = this.dragData.ddel.cloneNode(true);
33258         clone.id = Roo.id();
33259         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33260         this.proxy.update(clone);
33261         return true;
33262     },
33263
33264     afterValidDrop : function(){
33265         var v = this.view;
33266         setTimeout(function(){
33267             v.headersDisabled = false;
33268         }, 50);
33269     },
33270
33271     afterInvalidDrop : function(){
33272         var v = this.view;
33273         setTimeout(function(){
33274             v.headersDisabled = false;
33275         }, 50);
33276     }
33277 });
33278 /*
33279  * Based on:
33280  * Ext JS Library 1.1.1
33281  * Copyright(c) 2006-2007, Ext JS, LLC.
33282  *
33283  * Originally Released Under LGPL - original licence link has changed is not relivant.
33284  *
33285  * Fork - LGPL
33286  * <script type="text/javascript">
33287  */
33288 // private
33289 // This is a support class used internally by the Grid components
33290 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33291     this.grid = grid;
33292     this.view = grid.getView();
33293     // split the proxies so they don't interfere with mouse events
33294     this.proxyTop = Roo.DomHelper.append(document.body, {
33295         cls:"col-move-top", html:"&#160;"
33296     }, true);
33297     this.proxyBottom = Roo.DomHelper.append(document.body, {
33298         cls:"col-move-bottom", html:"&#160;"
33299     }, true);
33300     this.proxyTop.hide = this.proxyBottom.hide = function(){
33301         this.setLeftTop(-100,-100);
33302         this.setStyle("visibility", "hidden");
33303     };
33304     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33305     // temporarily disabled
33306     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33307     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33308 };
33309 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33310     proxyOffsets : [-4, -9],
33311     fly: Roo.Element.fly,
33312
33313     getTargetFromEvent : function(e){
33314         var t = Roo.lib.Event.getTarget(e);
33315         var cindex = this.view.findCellIndex(t);
33316         if(cindex !== false){
33317             return this.view.getHeaderCell(cindex);
33318         }
33319         return null;
33320     },
33321
33322     nextVisible : function(h){
33323         var v = this.view, cm = this.grid.colModel;
33324         h = h.nextSibling;
33325         while(h){
33326             if(!cm.isHidden(v.getCellIndex(h))){
33327                 return h;
33328             }
33329             h = h.nextSibling;
33330         }
33331         return null;
33332     },
33333
33334     prevVisible : function(h){
33335         var v = this.view, cm = this.grid.colModel;
33336         h = h.prevSibling;
33337         while(h){
33338             if(!cm.isHidden(v.getCellIndex(h))){
33339                 return h;
33340             }
33341             h = h.prevSibling;
33342         }
33343         return null;
33344     },
33345
33346     positionIndicator : function(h, n, e){
33347         var x = Roo.lib.Event.getPageX(e);
33348         var r = Roo.lib.Dom.getRegion(n.firstChild);
33349         var px, pt, py = r.top + this.proxyOffsets[1];
33350         if((r.right - x) <= (r.right-r.left)/2){
33351             px = r.right+this.view.borderWidth;
33352             pt = "after";
33353         }else{
33354             px = r.left;
33355             pt = "before";
33356         }
33357         var oldIndex = this.view.getCellIndex(h);
33358         var newIndex = this.view.getCellIndex(n);
33359
33360         if(this.grid.colModel.isFixed(newIndex)){
33361             return false;
33362         }
33363
33364         var locked = this.grid.colModel.isLocked(newIndex);
33365
33366         if(pt == "after"){
33367             newIndex++;
33368         }
33369         if(oldIndex < newIndex){
33370             newIndex--;
33371         }
33372         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33373             return false;
33374         }
33375         px +=  this.proxyOffsets[0];
33376         this.proxyTop.setLeftTop(px, py);
33377         this.proxyTop.show();
33378         if(!this.bottomOffset){
33379             this.bottomOffset = this.view.mainHd.getHeight();
33380         }
33381         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33382         this.proxyBottom.show();
33383         return pt;
33384     },
33385
33386     onNodeEnter : function(n, dd, e, data){
33387         if(data.header != n){
33388             this.positionIndicator(data.header, n, e);
33389         }
33390     },
33391
33392     onNodeOver : function(n, dd, e, data){
33393         var result = false;
33394         if(data.header != n){
33395             result = this.positionIndicator(data.header, n, e);
33396         }
33397         if(!result){
33398             this.proxyTop.hide();
33399             this.proxyBottom.hide();
33400         }
33401         return result ? this.dropAllowed : this.dropNotAllowed;
33402     },
33403
33404     onNodeOut : function(n, dd, e, data){
33405         this.proxyTop.hide();
33406         this.proxyBottom.hide();
33407     },
33408
33409     onNodeDrop : function(n, dd, e, data){
33410         var h = data.header;
33411         if(h != n){
33412             var cm = this.grid.colModel;
33413             var x = Roo.lib.Event.getPageX(e);
33414             var r = Roo.lib.Dom.getRegion(n.firstChild);
33415             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33416             var oldIndex = this.view.getCellIndex(h);
33417             var newIndex = this.view.getCellIndex(n);
33418             var locked = cm.isLocked(newIndex);
33419             if(pt == "after"){
33420                 newIndex++;
33421             }
33422             if(oldIndex < newIndex){
33423                 newIndex--;
33424             }
33425             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33426                 return false;
33427             }
33428             cm.setLocked(oldIndex, locked, true);
33429             cm.moveColumn(oldIndex, newIndex);
33430             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33431             return true;
33432         }
33433         return false;
33434     }
33435 });
33436 /*
33437  * Based on:
33438  * Ext JS Library 1.1.1
33439  * Copyright(c) 2006-2007, Ext JS, LLC.
33440  *
33441  * Originally Released Under LGPL - original licence link has changed is not relivant.
33442  *
33443  * Fork - LGPL
33444  * <script type="text/javascript">
33445  */
33446   
33447 /**
33448  * @class Roo.grid.GridView
33449  * @extends Roo.util.Observable
33450  *
33451  * @constructor
33452  * @param {Object} config
33453  */
33454 Roo.grid.GridView = function(config){
33455     Roo.grid.GridView.superclass.constructor.call(this);
33456     this.el = null;
33457
33458     Roo.apply(this, config);
33459 };
33460
33461 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33462
33463     unselectable :  'unselectable="on"',
33464     unselectableCls :  'x-unselectable',
33465     
33466     
33467     rowClass : "x-grid-row",
33468
33469     cellClass : "x-grid-col",
33470
33471     tdClass : "x-grid-td",
33472
33473     hdClass : "x-grid-hd",
33474
33475     splitClass : "x-grid-split",
33476
33477     sortClasses : ["sort-asc", "sort-desc"],
33478
33479     enableMoveAnim : false,
33480
33481     hlColor: "C3DAF9",
33482
33483     dh : Roo.DomHelper,
33484
33485     fly : Roo.Element.fly,
33486
33487     css : Roo.util.CSS,
33488
33489     borderWidth: 1,
33490
33491     splitOffset: 3,
33492
33493     scrollIncrement : 22,
33494
33495     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33496
33497     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33498
33499     bind : function(ds, cm){
33500         if(this.ds){
33501             this.ds.un("load", this.onLoad, this);
33502             this.ds.un("datachanged", this.onDataChange, this);
33503             this.ds.un("add", this.onAdd, this);
33504             this.ds.un("remove", this.onRemove, this);
33505             this.ds.un("update", this.onUpdate, this);
33506             this.ds.un("clear", this.onClear, this);
33507         }
33508         if(ds){
33509             ds.on("load", this.onLoad, this);
33510             ds.on("datachanged", this.onDataChange, this);
33511             ds.on("add", this.onAdd, this);
33512             ds.on("remove", this.onRemove, this);
33513             ds.on("update", this.onUpdate, this);
33514             ds.on("clear", this.onClear, this);
33515         }
33516         this.ds = ds;
33517
33518         if(this.cm){
33519             this.cm.un("widthchange", this.onColWidthChange, this);
33520             this.cm.un("headerchange", this.onHeaderChange, this);
33521             this.cm.un("hiddenchange", this.onHiddenChange, this);
33522             this.cm.un("columnmoved", this.onColumnMove, this);
33523             this.cm.un("columnlockchange", this.onColumnLock, this);
33524         }
33525         if(cm){
33526             this.generateRules(cm);
33527             cm.on("widthchange", this.onColWidthChange, this);
33528             cm.on("headerchange", this.onHeaderChange, this);
33529             cm.on("hiddenchange", this.onHiddenChange, this);
33530             cm.on("columnmoved", this.onColumnMove, this);
33531             cm.on("columnlockchange", this.onColumnLock, this);
33532         }
33533         this.cm = cm;
33534     },
33535
33536     init: function(grid){
33537         Roo.grid.GridView.superclass.init.call(this, grid);
33538
33539         this.bind(grid.dataSource, grid.colModel);
33540
33541         grid.on("headerclick", this.handleHeaderClick, this);
33542
33543         if(grid.trackMouseOver){
33544             grid.on("mouseover", this.onRowOver, this);
33545             grid.on("mouseout", this.onRowOut, this);
33546         }
33547         grid.cancelTextSelection = function(){};
33548         this.gridId = grid.id;
33549
33550         var tpls = this.templates || {};
33551
33552         if(!tpls.master){
33553             tpls.master = new Roo.Template(
33554                '<div class="x-grid" hidefocus="true">',
33555                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33556                   '<div class="x-grid-topbar"></div>',
33557                   '<div class="x-grid-scroller"><div></div></div>',
33558                   '<div class="x-grid-locked">',
33559                       '<div class="x-grid-header">{lockedHeader}</div>',
33560                       '<div class="x-grid-body">{lockedBody}</div>',
33561                   "</div>",
33562                   '<div class="x-grid-viewport">',
33563                       '<div class="x-grid-header">{header}</div>',
33564                       '<div class="x-grid-body">{body}</div>',
33565                   "</div>",
33566                   '<div class="x-grid-bottombar"></div>',
33567                  
33568                   '<div class="x-grid-resize-proxy">&#160;</div>',
33569                "</div>"
33570             );
33571             tpls.master.disableformats = true;
33572         }
33573
33574         if(!tpls.header){
33575             tpls.header = new Roo.Template(
33576                '<table border="0" cellspacing="0" cellpadding="0">',
33577                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33578                "</table>{splits}"
33579             );
33580             tpls.header.disableformats = true;
33581         }
33582         tpls.header.compile();
33583
33584         if(!tpls.hcell){
33585             tpls.hcell = new Roo.Template(
33586                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33587                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33588                 "</div></td>"
33589              );
33590              tpls.hcell.disableFormats = true;
33591         }
33592         tpls.hcell.compile();
33593
33594         if(!tpls.hsplit){
33595             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33596                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33597             tpls.hsplit.disableFormats = true;
33598         }
33599         tpls.hsplit.compile();
33600
33601         if(!tpls.body){
33602             tpls.body = new Roo.Template(
33603                '<table border="0" cellspacing="0" cellpadding="0">',
33604                "<tbody>{rows}</tbody>",
33605                "</table>"
33606             );
33607             tpls.body.disableFormats = true;
33608         }
33609         tpls.body.compile();
33610
33611         if(!tpls.row){
33612             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33613             tpls.row.disableFormats = true;
33614         }
33615         tpls.row.compile();
33616
33617         if(!tpls.cell){
33618             tpls.cell = new Roo.Template(
33619                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33620                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33621                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33622                 "</td>"
33623             );
33624             tpls.cell.disableFormats = true;
33625         }
33626         tpls.cell.compile();
33627
33628         this.templates = tpls;
33629     },
33630
33631     // remap these for backwards compat
33632     onColWidthChange : function(){
33633         this.updateColumns.apply(this, arguments);
33634     },
33635     onHeaderChange : function(){
33636         this.updateHeaders.apply(this, arguments);
33637     }, 
33638     onHiddenChange : function(){
33639         this.handleHiddenChange.apply(this, arguments);
33640     },
33641     onColumnMove : function(){
33642         this.handleColumnMove.apply(this, arguments);
33643     },
33644     onColumnLock : function(){
33645         this.handleLockChange.apply(this, arguments);
33646     },
33647
33648     onDataChange : function(){
33649         this.refresh();
33650         this.updateHeaderSortState();
33651     },
33652
33653     onClear : function(){
33654         this.refresh();
33655     },
33656
33657     onUpdate : function(ds, record){
33658         this.refreshRow(record);
33659     },
33660
33661     refreshRow : function(record){
33662         var ds = this.ds, index;
33663         if(typeof record == 'number'){
33664             index = record;
33665             record = ds.getAt(index);
33666         }else{
33667             index = ds.indexOf(record);
33668         }
33669         this.insertRows(ds, index, index, true);
33670         this.onRemove(ds, record, index+1, true);
33671         this.syncRowHeights(index, index);
33672         this.layout();
33673         this.fireEvent("rowupdated", this, index, record);
33674     },
33675
33676     onAdd : function(ds, records, index){
33677         this.insertRows(ds, index, index + (records.length-1));
33678     },
33679
33680     onRemove : function(ds, record, index, isUpdate){
33681         if(isUpdate !== true){
33682             this.fireEvent("beforerowremoved", this, index, record);
33683         }
33684         var bt = this.getBodyTable(), lt = this.getLockedTable();
33685         if(bt.rows[index]){
33686             bt.firstChild.removeChild(bt.rows[index]);
33687         }
33688         if(lt.rows[index]){
33689             lt.firstChild.removeChild(lt.rows[index]);
33690         }
33691         if(isUpdate !== true){
33692             this.stripeRows(index);
33693             this.syncRowHeights(index, index);
33694             this.layout();
33695             this.fireEvent("rowremoved", this, index, record);
33696         }
33697     },
33698
33699     onLoad : function(){
33700         this.scrollToTop();
33701     },
33702
33703     /**
33704      * Scrolls the grid to the top
33705      */
33706     scrollToTop : function(){
33707         if(this.scroller){
33708             this.scroller.dom.scrollTop = 0;
33709             this.syncScroll();
33710         }
33711     },
33712
33713     /**
33714      * Gets a panel in the header of the grid that can be used for toolbars etc.
33715      * After modifying the contents of this panel a call to grid.autoSize() may be
33716      * required to register any changes in size.
33717      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33718      * @return Roo.Element
33719      */
33720     getHeaderPanel : function(doShow){
33721         if(doShow){
33722             this.headerPanel.show();
33723         }
33724         return this.headerPanel;
33725     },
33726
33727     /**
33728      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33729      * After modifying the contents of this panel a call to grid.autoSize() may be
33730      * required to register any changes in size.
33731      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33732      * @return Roo.Element
33733      */
33734     getFooterPanel : function(doShow){
33735         if(doShow){
33736             this.footerPanel.show();
33737         }
33738         return this.footerPanel;
33739     },
33740
33741     initElements : function(){
33742         var E = Roo.Element;
33743         var el = this.grid.getGridEl().dom.firstChild;
33744         var cs = el.childNodes;
33745
33746         this.el = new E(el);
33747         
33748          this.focusEl = new E(el.firstChild);
33749         this.focusEl.swallowEvent("click", true);
33750         
33751         this.headerPanel = new E(cs[1]);
33752         this.headerPanel.enableDisplayMode("block");
33753
33754         this.scroller = new E(cs[2]);
33755         this.scrollSizer = new E(this.scroller.dom.firstChild);
33756
33757         this.lockedWrap = new E(cs[3]);
33758         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33759         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33760
33761         this.mainWrap = new E(cs[4]);
33762         this.mainHd = new E(this.mainWrap.dom.firstChild);
33763         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33764
33765         this.footerPanel = new E(cs[5]);
33766         this.footerPanel.enableDisplayMode("block");
33767
33768         this.resizeProxy = new E(cs[6]);
33769
33770         this.headerSelector = String.format(
33771            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33772            this.lockedHd.id, this.mainHd.id
33773         );
33774
33775         this.splitterSelector = String.format(
33776            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33777            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33778         );
33779     },
33780     idToCssName : function(s)
33781     {
33782         return s.replace(/[^a-z0-9]+/ig, '-');
33783     },
33784
33785     getHeaderCell : function(index){
33786         return Roo.DomQuery.select(this.headerSelector)[index];
33787     },
33788
33789     getHeaderCellMeasure : function(index){
33790         return this.getHeaderCell(index).firstChild;
33791     },
33792
33793     getHeaderCellText : function(index){
33794         return this.getHeaderCell(index).firstChild.firstChild;
33795     },
33796
33797     getLockedTable : function(){
33798         return this.lockedBody.dom.firstChild;
33799     },
33800
33801     getBodyTable : function(){
33802         return this.mainBody.dom.firstChild;
33803     },
33804
33805     getLockedRow : function(index){
33806         return this.getLockedTable().rows[index];
33807     },
33808
33809     getRow : function(index){
33810         return this.getBodyTable().rows[index];
33811     },
33812
33813     getRowComposite : function(index){
33814         if(!this.rowEl){
33815             this.rowEl = new Roo.CompositeElementLite();
33816         }
33817         var els = [], lrow, mrow;
33818         if(lrow = this.getLockedRow(index)){
33819             els.push(lrow);
33820         }
33821         if(mrow = this.getRow(index)){
33822             els.push(mrow);
33823         }
33824         this.rowEl.elements = els;
33825         return this.rowEl;
33826     },
33827     /**
33828      * Gets the 'td' of the cell
33829      * 
33830      * @param {Integer} rowIndex row to select
33831      * @param {Integer} colIndex column to select
33832      * 
33833      * @return {Object} 
33834      */
33835     getCell : function(rowIndex, colIndex){
33836         var locked = this.cm.getLockedCount();
33837         var source;
33838         if(colIndex < locked){
33839             source = this.lockedBody.dom.firstChild;
33840         }else{
33841             source = this.mainBody.dom.firstChild;
33842             colIndex -= locked;
33843         }
33844         return source.rows[rowIndex].childNodes[colIndex];
33845     },
33846
33847     getCellText : function(rowIndex, colIndex){
33848         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33849     },
33850
33851     getCellBox : function(cell){
33852         var b = this.fly(cell).getBox();
33853         if(Roo.isOpera){ // opera fails to report the Y
33854             b.y = cell.offsetTop + this.mainBody.getY();
33855         }
33856         return b;
33857     },
33858
33859     getCellIndex : function(cell){
33860         var id = String(cell.className).match(this.cellRE);
33861         if(id){
33862             return parseInt(id[1], 10);
33863         }
33864         return 0;
33865     },
33866
33867     findHeaderIndex : function(n){
33868         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33869         return r ? this.getCellIndex(r) : false;
33870     },
33871
33872     findHeaderCell : function(n){
33873         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33874         return r ? r : false;
33875     },
33876
33877     findRowIndex : function(n){
33878         if(!n){
33879             return false;
33880         }
33881         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33882         return r ? r.rowIndex : false;
33883     },
33884
33885     findCellIndex : function(node){
33886         var stop = this.el.dom;
33887         while(node && node != stop){
33888             if(this.findRE.test(node.className)){
33889                 return this.getCellIndex(node);
33890             }
33891             node = node.parentNode;
33892         }
33893         return false;
33894     },
33895
33896     getColumnId : function(index){
33897         return this.cm.getColumnId(index);
33898     },
33899
33900     getSplitters : function()
33901     {
33902         if(this.splitterSelector){
33903            return Roo.DomQuery.select(this.splitterSelector);
33904         }else{
33905             return null;
33906       }
33907     },
33908
33909     getSplitter : function(index){
33910         return this.getSplitters()[index];
33911     },
33912
33913     onRowOver : function(e, t){
33914         var row;
33915         if((row = this.findRowIndex(t)) !== false){
33916             this.getRowComposite(row).addClass("x-grid-row-over");
33917         }
33918     },
33919
33920     onRowOut : function(e, t){
33921         var row;
33922         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33923             this.getRowComposite(row).removeClass("x-grid-row-over");
33924         }
33925     },
33926
33927     renderHeaders : function(){
33928         var cm = this.cm;
33929         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33930         var cb = [], lb = [], sb = [], lsb = [], p = {};
33931         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33932             p.cellId = "x-grid-hd-0-" + i;
33933             p.splitId = "x-grid-csplit-0-" + i;
33934             p.id = cm.getColumnId(i);
33935             p.value = cm.getColumnHeader(i) || "";
33936             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33937             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33938             if(!cm.isLocked(i)){
33939                 cb[cb.length] = ct.apply(p);
33940                 sb[sb.length] = st.apply(p);
33941             }else{
33942                 lb[lb.length] = ct.apply(p);
33943                 lsb[lsb.length] = st.apply(p);
33944             }
33945         }
33946         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33947                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33948     },
33949
33950     updateHeaders : function(){
33951         var html = this.renderHeaders();
33952         this.lockedHd.update(html[0]);
33953         this.mainHd.update(html[1]);
33954     },
33955
33956     /**
33957      * Focuses the specified row.
33958      * @param {Number} row The row index
33959      */
33960     focusRow : function(row)
33961     {
33962         //Roo.log('GridView.focusRow');
33963         var x = this.scroller.dom.scrollLeft;
33964         this.focusCell(row, 0, false);
33965         this.scroller.dom.scrollLeft = x;
33966     },
33967
33968     /**
33969      * Focuses the specified cell.
33970      * @param {Number} row The row index
33971      * @param {Number} col The column index
33972      * @param {Boolean} hscroll false to disable horizontal scrolling
33973      */
33974     focusCell : function(row, col, hscroll)
33975     {
33976         //Roo.log('GridView.focusCell');
33977         var el = this.ensureVisible(row, col, hscroll);
33978         this.focusEl.alignTo(el, "tl-tl");
33979         if(Roo.isGecko){
33980             this.focusEl.focus();
33981         }else{
33982             this.focusEl.focus.defer(1, this.focusEl);
33983         }
33984     },
33985
33986     /**
33987      * Scrolls the specified cell into view
33988      * @param {Number} row The row index
33989      * @param {Number} col The column index
33990      * @param {Boolean} hscroll false to disable horizontal scrolling
33991      */
33992     ensureVisible : function(row, col, hscroll)
33993     {
33994         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33995         //return null; //disable for testing.
33996         if(typeof row != "number"){
33997             row = row.rowIndex;
33998         }
33999         if(row < 0 && row >= this.ds.getCount()){
34000             return  null;
34001         }
34002         col = (col !== undefined ? col : 0);
34003         var cm = this.grid.colModel;
34004         while(cm.isHidden(col)){
34005             col++;
34006         }
34007
34008         var el = this.getCell(row, col);
34009         if(!el){
34010             return null;
34011         }
34012         var c = this.scroller.dom;
34013
34014         var ctop = parseInt(el.offsetTop, 10);
34015         var cleft = parseInt(el.offsetLeft, 10);
34016         var cbot = ctop + el.offsetHeight;
34017         var cright = cleft + el.offsetWidth;
34018         
34019         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34020         var stop = parseInt(c.scrollTop, 10);
34021         var sleft = parseInt(c.scrollLeft, 10);
34022         var sbot = stop + ch;
34023         var sright = sleft + c.clientWidth;
34024         /*
34025         Roo.log('GridView.ensureVisible:' +
34026                 ' ctop:' + ctop +
34027                 ' c.clientHeight:' + c.clientHeight +
34028                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34029                 ' stop:' + stop +
34030                 ' cbot:' + cbot +
34031                 ' sbot:' + sbot +
34032                 ' ch:' + ch  
34033                 );
34034         */
34035         if(ctop < stop){
34036              c.scrollTop = ctop;
34037             //Roo.log("set scrolltop to ctop DISABLE?");
34038         }else if(cbot > sbot){
34039             //Roo.log("set scrolltop to cbot-ch");
34040             c.scrollTop = cbot-ch;
34041         }
34042         
34043         if(hscroll !== false){
34044             if(cleft < sleft){
34045                 c.scrollLeft = cleft;
34046             }else if(cright > sright){
34047                 c.scrollLeft = cright-c.clientWidth;
34048             }
34049         }
34050          
34051         return el;
34052     },
34053
34054     updateColumns : function(){
34055         this.grid.stopEditing();
34056         var cm = this.grid.colModel, colIds = this.getColumnIds();
34057         //var totalWidth = cm.getTotalWidth();
34058         var pos = 0;
34059         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34060             //if(cm.isHidden(i)) continue;
34061             var w = cm.getColumnWidth(i);
34062             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34063             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34064         }
34065         this.updateSplitters();
34066     },
34067
34068     generateRules : function(cm){
34069         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34070         Roo.util.CSS.removeStyleSheet(rulesId);
34071         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34072             var cid = cm.getColumnId(i);
34073             var align = '';
34074             if(cm.config[i].align){
34075                 align = 'text-align:'+cm.config[i].align+';';
34076             }
34077             var hidden = '';
34078             if(cm.isHidden(i)){
34079                 hidden = 'display:none;';
34080             }
34081             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34082             ruleBuf.push(
34083                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34084                     this.hdSelector, cid, " {\n", align, width, "}\n",
34085                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34086                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34087         }
34088         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34089     },
34090
34091     updateSplitters : function(){
34092         var cm = this.cm, s = this.getSplitters();
34093         if(s){ // splitters not created yet
34094             var pos = 0, locked = true;
34095             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34096                 if(cm.isHidden(i)) {
34097                     continue;
34098                 }
34099                 var w = cm.getColumnWidth(i); // make sure it's a number
34100                 if(!cm.isLocked(i) && locked){
34101                     pos = 0;
34102                     locked = false;
34103                 }
34104                 pos += w;
34105                 s[i].style.left = (pos-this.splitOffset) + "px";
34106             }
34107         }
34108     },
34109
34110     handleHiddenChange : function(colModel, colIndex, hidden){
34111         if(hidden){
34112             this.hideColumn(colIndex);
34113         }else{
34114             this.unhideColumn(colIndex);
34115         }
34116     },
34117
34118     hideColumn : function(colIndex){
34119         var cid = this.getColumnId(colIndex);
34120         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34121         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34122         if(Roo.isSafari){
34123             this.updateHeaders();
34124         }
34125         this.updateSplitters();
34126         this.layout();
34127     },
34128
34129     unhideColumn : function(colIndex){
34130         var cid = this.getColumnId(colIndex);
34131         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34132         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34133
34134         if(Roo.isSafari){
34135             this.updateHeaders();
34136         }
34137         this.updateSplitters();
34138         this.layout();
34139     },
34140
34141     insertRows : function(dm, firstRow, lastRow, isUpdate){
34142         if(firstRow == 0 && lastRow == dm.getCount()-1){
34143             this.refresh();
34144         }else{
34145             if(!isUpdate){
34146                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34147             }
34148             var s = this.getScrollState();
34149             var markup = this.renderRows(firstRow, lastRow);
34150             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34151             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34152             this.restoreScroll(s);
34153             if(!isUpdate){
34154                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34155                 this.syncRowHeights(firstRow, lastRow);
34156                 this.stripeRows(firstRow);
34157                 this.layout();
34158             }
34159         }
34160     },
34161
34162     bufferRows : function(markup, target, index){
34163         var before = null, trows = target.rows, tbody = target.tBodies[0];
34164         if(index < trows.length){
34165             before = trows[index];
34166         }
34167         var b = document.createElement("div");
34168         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34169         var rows = b.firstChild.rows;
34170         for(var i = 0, len = rows.length; i < len; i++){
34171             if(before){
34172                 tbody.insertBefore(rows[0], before);
34173             }else{
34174                 tbody.appendChild(rows[0]);
34175             }
34176         }
34177         b.innerHTML = "";
34178         b = null;
34179     },
34180
34181     deleteRows : function(dm, firstRow, lastRow){
34182         if(dm.getRowCount()<1){
34183             this.fireEvent("beforerefresh", this);
34184             this.mainBody.update("");
34185             this.lockedBody.update("");
34186             this.fireEvent("refresh", this);
34187         }else{
34188             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34189             var bt = this.getBodyTable();
34190             var tbody = bt.firstChild;
34191             var rows = bt.rows;
34192             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34193                 tbody.removeChild(rows[firstRow]);
34194             }
34195             this.stripeRows(firstRow);
34196             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34197         }
34198     },
34199
34200     updateRows : function(dataSource, firstRow, lastRow){
34201         var s = this.getScrollState();
34202         this.refresh();
34203         this.restoreScroll(s);
34204     },
34205
34206     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34207         if(!noRefresh){
34208            this.refresh();
34209         }
34210         this.updateHeaderSortState();
34211     },
34212
34213     getScrollState : function(){
34214         
34215         var sb = this.scroller.dom;
34216         return {left: sb.scrollLeft, top: sb.scrollTop};
34217     },
34218
34219     stripeRows : function(startRow){
34220         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34221             return;
34222         }
34223         startRow = startRow || 0;
34224         var rows = this.getBodyTable().rows;
34225         var lrows = this.getLockedTable().rows;
34226         var cls = ' x-grid-row-alt ';
34227         for(var i = startRow, len = rows.length; i < len; i++){
34228             var row = rows[i], lrow = lrows[i];
34229             var isAlt = ((i+1) % 2 == 0);
34230             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34231             if(isAlt == hasAlt){
34232                 continue;
34233             }
34234             if(isAlt){
34235                 row.className += " x-grid-row-alt";
34236             }else{
34237                 row.className = row.className.replace("x-grid-row-alt", "");
34238             }
34239             if(lrow){
34240                 lrow.className = row.className;
34241             }
34242         }
34243     },
34244
34245     restoreScroll : function(state){
34246         //Roo.log('GridView.restoreScroll');
34247         var sb = this.scroller.dom;
34248         sb.scrollLeft = state.left;
34249         sb.scrollTop = state.top;
34250         this.syncScroll();
34251     },
34252
34253     syncScroll : function(){
34254         //Roo.log('GridView.syncScroll');
34255         var sb = this.scroller.dom;
34256         var sh = this.mainHd.dom;
34257         var bs = this.mainBody.dom;
34258         var lv = this.lockedBody.dom;
34259         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34260         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34261     },
34262
34263     handleScroll : function(e){
34264         this.syncScroll();
34265         var sb = this.scroller.dom;
34266         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34267         e.stopEvent();
34268     },
34269
34270     handleWheel : function(e){
34271         var d = e.getWheelDelta();
34272         this.scroller.dom.scrollTop -= d*22;
34273         // set this here to prevent jumpy scrolling on large tables
34274         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34275         e.stopEvent();
34276     },
34277
34278     renderRows : function(startRow, endRow){
34279         // pull in all the crap needed to render rows
34280         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34281         var colCount = cm.getColumnCount();
34282
34283         if(ds.getCount() < 1){
34284             return ["", ""];
34285         }
34286
34287         // build a map for all the columns
34288         var cs = [];
34289         for(var i = 0; i < colCount; i++){
34290             var name = cm.getDataIndex(i);
34291             cs[i] = {
34292                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34293                 renderer : cm.getRenderer(i),
34294                 id : cm.getColumnId(i),
34295                 locked : cm.isLocked(i),
34296                 has_editor : cm.isCellEditable(i)
34297             };
34298         }
34299
34300         startRow = startRow || 0;
34301         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34302
34303         // records to render
34304         var rs = ds.getRange(startRow, endRow);
34305
34306         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34307     },
34308
34309     // As much as I hate to duplicate code, this was branched because FireFox really hates
34310     // [].join("") on strings. The performance difference was substantial enough to
34311     // branch this function
34312     doRender : Roo.isGecko ?
34313             function(cs, rs, ds, startRow, colCount, stripe){
34314                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34315                 // buffers
34316                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34317                 
34318                 var hasListener = this.grid.hasListener('rowclass');
34319                 var rowcfg = {};
34320                 for(var j = 0, len = rs.length; j < len; j++){
34321                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34322                     for(var i = 0; i < colCount; i++){
34323                         c = cs[i];
34324                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34325                         p.id = c.id;
34326                         p.css = p.attr = "";
34327                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34328                         if(p.value == undefined || p.value === "") {
34329                             p.value = "&#160;";
34330                         }
34331                         if(c.has_editor){
34332                             p.css += ' x-grid-editable-cell';
34333                         }
34334                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34335                             p.css +=  ' x-grid-dirty-cell';
34336                         }
34337                         var markup = ct.apply(p);
34338                         if(!c.locked){
34339                             cb+= markup;
34340                         }else{
34341                             lcb+= markup;
34342                         }
34343                     }
34344                     var alt = [];
34345                     if(stripe && ((rowIndex+1) % 2 == 0)){
34346                         alt.push("x-grid-row-alt")
34347                     }
34348                     if(r.dirty){
34349                         alt.push(  " x-grid-dirty-row");
34350                     }
34351                     rp.cells = lcb;
34352                     if(this.getRowClass){
34353                         alt.push(this.getRowClass(r, rowIndex));
34354                     }
34355                     if (hasListener) {
34356                         rowcfg = {
34357                              
34358                             record: r,
34359                             rowIndex : rowIndex,
34360                             rowClass : ''
34361                         };
34362                         this.grid.fireEvent('rowclass', this, rowcfg);
34363                         alt.push(rowcfg.rowClass);
34364                     }
34365                     rp.alt = alt.join(" ");
34366                     lbuf+= rt.apply(rp);
34367                     rp.cells = cb;
34368                     buf+=  rt.apply(rp);
34369                 }
34370                 return [lbuf, buf];
34371             } :
34372             function(cs, rs, ds, startRow, colCount, stripe){
34373                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34374                 // buffers
34375                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34376                 var hasListener = this.grid.hasListener('rowclass');
34377  
34378                 var rowcfg = {};
34379                 for(var j = 0, len = rs.length; j < len; j++){
34380                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34381                     for(var i = 0; i < colCount; i++){
34382                         c = cs[i];
34383                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34384                         p.id = c.id;
34385                         p.css = p.attr = "";
34386                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34387                         if(p.value == undefined || p.value === "") {
34388                             p.value = "&#160;";
34389                         }
34390                         //Roo.log(c);
34391                          if(c.has_editor){
34392                             p.css += ' x-grid-editable-cell';
34393                         }
34394                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34395                             p.css += ' x-grid-dirty-cell' 
34396                         }
34397                         
34398                         var markup = ct.apply(p);
34399                         if(!c.locked){
34400                             cb[cb.length] = markup;
34401                         }else{
34402                             lcb[lcb.length] = markup;
34403                         }
34404                     }
34405                     var alt = [];
34406                     if(stripe && ((rowIndex+1) % 2 == 0)){
34407                         alt.push( "x-grid-row-alt");
34408                     }
34409                     if(r.dirty){
34410                         alt.push(" x-grid-dirty-row");
34411                     }
34412                     rp.cells = lcb;
34413                     if(this.getRowClass){
34414                         alt.push( this.getRowClass(r, rowIndex));
34415                     }
34416                     if (hasListener) {
34417                         rowcfg = {
34418                              
34419                             record: r,
34420                             rowIndex : rowIndex,
34421                             rowClass : ''
34422                         };
34423                         this.grid.fireEvent('rowclass', this, rowcfg);
34424                         alt.push(rowcfg.rowClass);
34425                     }
34426                     
34427                     rp.alt = alt.join(" ");
34428                     rp.cells = lcb.join("");
34429                     lbuf[lbuf.length] = rt.apply(rp);
34430                     rp.cells = cb.join("");
34431                     buf[buf.length] =  rt.apply(rp);
34432                 }
34433                 return [lbuf.join(""), buf.join("")];
34434             },
34435
34436     renderBody : function(){
34437         var markup = this.renderRows();
34438         var bt = this.templates.body;
34439         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34440     },
34441
34442     /**
34443      * Refreshes the grid
34444      * @param {Boolean} headersToo
34445      */
34446     refresh : function(headersToo){
34447         this.fireEvent("beforerefresh", this);
34448         this.grid.stopEditing();
34449         var result = this.renderBody();
34450         this.lockedBody.update(result[0]);
34451         this.mainBody.update(result[1]);
34452         if(headersToo === true){
34453             this.updateHeaders();
34454             this.updateColumns();
34455             this.updateSplitters();
34456             this.updateHeaderSortState();
34457         }
34458         this.syncRowHeights();
34459         this.layout();
34460         this.fireEvent("refresh", this);
34461     },
34462
34463     handleColumnMove : function(cm, oldIndex, newIndex){
34464         this.indexMap = null;
34465         var s = this.getScrollState();
34466         this.refresh(true);
34467         this.restoreScroll(s);
34468         this.afterMove(newIndex);
34469     },
34470
34471     afterMove : function(colIndex){
34472         if(this.enableMoveAnim && Roo.enableFx){
34473             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34474         }
34475         // if multisort - fix sortOrder, and reload..
34476         if (this.grid.dataSource.multiSort) {
34477             // the we can call sort again..
34478             var dm = this.grid.dataSource;
34479             var cm = this.grid.colModel;
34480             var so = [];
34481             for(var i = 0; i < cm.config.length; i++ ) {
34482                 
34483                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34484                     continue; // dont' bother, it's not in sort list or being set.
34485                 }
34486                 
34487                 so.push(cm.config[i].dataIndex);
34488             };
34489             dm.sortOrder = so;
34490             dm.load(dm.lastOptions);
34491             
34492             
34493         }
34494         
34495     },
34496
34497     updateCell : function(dm, rowIndex, dataIndex){
34498         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34499         if(typeof colIndex == "undefined"){ // not present in grid
34500             return;
34501         }
34502         var cm = this.grid.colModel;
34503         var cell = this.getCell(rowIndex, colIndex);
34504         var cellText = this.getCellText(rowIndex, colIndex);
34505
34506         var p = {
34507             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34508             id : cm.getColumnId(colIndex),
34509             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34510         };
34511         var renderer = cm.getRenderer(colIndex);
34512         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34513         if(typeof val == "undefined" || val === "") {
34514             val = "&#160;";
34515         }
34516         cellText.innerHTML = val;
34517         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34518         this.syncRowHeights(rowIndex, rowIndex);
34519     },
34520
34521     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34522         var maxWidth = 0;
34523         if(this.grid.autoSizeHeaders){
34524             var h = this.getHeaderCellMeasure(colIndex);
34525             maxWidth = Math.max(maxWidth, h.scrollWidth);
34526         }
34527         var tb, index;
34528         if(this.cm.isLocked(colIndex)){
34529             tb = this.getLockedTable();
34530             index = colIndex;
34531         }else{
34532             tb = this.getBodyTable();
34533             index = colIndex - this.cm.getLockedCount();
34534         }
34535         if(tb && tb.rows){
34536             var rows = tb.rows;
34537             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34538             for(var i = 0; i < stopIndex; i++){
34539                 var cell = rows[i].childNodes[index].firstChild;
34540                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34541             }
34542         }
34543         return maxWidth + /*margin for error in IE*/ 5;
34544     },
34545     /**
34546      * Autofit a column to its content.
34547      * @param {Number} colIndex
34548      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34549      */
34550      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34551          if(this.cm.isHidden(colIndex)){
34552              return; // can't calc a hidden column
34553          }
34554         if(forceMinSize){
34555             var cid = this.cm.getColumnId(colIndex);
34556             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34557            if(this.grid.autoSizeHeaders){
34558                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34559            }
34560         }
34561         var newWidth = this.calcColumnWidth(colIndex);
34562         this.cm.setColumnWidth(colIndex,
34563             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34564         if(!suppressEvent){
34565             this.grid.fireEvent("columnresize", colIndex, newWidth);
34566         }
34567     },
34568
34569     /**
34570      * Autofits all columns to their content and then expands to fit any extra space in the grid
34571      */
34572      autoSizeColumns : function(){
34573         var cm = this.grid.colModel;
34574         var colCount = cm.getColumnCount();
34575         for(var i = 0; i < colCount; i++){
34576             this.autoSizeColumn(i, true, true);
34577         }
34578         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34579             this.fitColumns();
34580         }else{
34581             this.updateColumns();
34582             this.layout();
34583         }
34584     },
34585
34586     /**
34587      * Autofits all columns to the grid's width proportionate with their current size
34588      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34589      */
34590     fitColumns : function(reserveScrollSpace){
34591         var cm = this.grid.colModel;
34592         var colCount = cm.getColumnCount();
34593         var cols = [];
34594         var width = 0;
34595         var i, w;
34596         for (i = 0; i < colCount; i++){
34597             if(!cm.isHidden(i) && !cm.isFixed(i)){
34598                 w = cm.getColumnWidth(i);
34599                 cols.push(i);
34600                 cols.push(w);
34601                 width += w;
34602             }
34603         }
34604         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34605         if(reserveScrollSpace){
34606             avail -= 17;
34607         }
34608         var frac = (avail - cm.getTotalWidth())/width;
34609         while (cols.length){
34610             w = cols.pop();
34611             i = cols.pop();
34612             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34613         }
34614         this.updateColumns();
34615         this.layout();
34616     },
34617
34618     onRowSelect : function(rowIndex){
34619         var row = this.getRowComposite(rowIndex);
34620         row.addClass("x-grid-row-selected");
34621     },
34622
34623     onRowDeselect : function(rowIndex){
34624         var row = this.getRowComposite(rowIndex);
34625         row.removeClass("x-grid-row-selected");
34626     },
34627
34628     onCellSelect : function(row, col){
34629         var cell = this.getCell(row, col);
34630         if(cell){
34631             Roo.fly(cell).addClass("x-grid-cell-selected");
34632         }
34633     },
34634
34635     onCellDeselect : function(row, col){
34636         var cell = this.getCell(row, col);
34637         if(cell){
34638             Roo.fly(cell).removeClass("x-grid-cell-selected");
34639         }
34640     },
34641
34642     updateHeaderSortState : function(){
34643         
34644         // sort state can be single { field: xxx, direction : yyy}
34645         // or   { xxx=>ASC , yyy : DESC ..... }
34646         
34647         var mstate = {};
34648         if (!this.ds.multiSort) { 
34649             var state = this.ds.getSortState();
34650             if(!state){
34651                 return;
34652             }
34653             mstate[state.field] = state.direction;
34654             // FIXME... - this is not used here.. but might be elsewhere..
34655             this.sortState = state;
34656             
34657         } else {
34658             mstate = this.ds.sortToggle;
34659         }
34660         //remove existing sort classes..
34661         
34662         var sc = this.sortClasses;
34663         var hds = this.el.select(this.headerSelector).removeClass(sc);
34664         
34665         for(var f in mstate) {
34666         
34667             var sortColumn = this.cm.findColumnIndex(f);
34668             
34669             if(sortColumn != -1){
34670                 var sortDir = mstate[f];        
34671                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34672             }
34673         }
34674         
34675          
34676         
34677     },
34678
34679
34680     handleHeaderClick : function(g, index,e){
34681         
34682         Roo.log("header click");
34683         
34684         if (Roo.isTouch) {
34685             // touch events on header are handled by context
34686             this.handleHdCtx(g,index,e);
34687             return;
34688         }
34689         
34690         
34691         if(this.headersDisabled){
34692             return;
34693         }
34694         var dm = g.dataSource, cm = g.colModel;
34695         if(!cm.isSortable(index)){
34696             return;
34697         }
34698         g.stopEditing();
34699         
34700         if (dm.multiSort) {
34701             // update the sortOrder
34702             var so = [];
34703             for(var i = 0; i < cm.config.length; i++ ) {
34704                 
34705                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34706                     continue; // dont' bother, it's not in sort list or being set.
34707                 }
34708                 
34709                 so.push(cm.config[i].dataIndex);
34710             };
34711             dm.sortOrder = so;
34712         }
34713         
34714         
34715         dm.sort(cm.getDataIndex(index));
34716     },
34717
34718
34719     destroy : function(){
34720         if(this.colMenu){
34721             this.colMenu.removeAll();
34722             Roo.menu.MenuMgr.unregister(this.colMenu);
34723             this.colMenu.getEl().remove();
34724             delete this.colMenu;
34725         }
34726         if(this.hmenu){
34727             this.hmenu.removeAll();
34728             Roo.menu.MenuMgr.unregister(this.hmenu);
34729             this.hmenu.getEl().remove();
34730             delete this.hmenu;
34731         }
34732         if(this.grid.enableColumnMove){
34733             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34734             if(dds){
34735                 for(var dd in dds){
34736                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34737                         var elid = dds[dd].dragElId;
34738                         dds[dd].unreg();
34739                         Roo.get(elid).remove();
34740                     } else if(dds[dd].config.isTarget){
34741                         dds[dd].proxyTop.remove();
34742                         dds[dd].proxyBottom.remove();
34743                         dds[dd].unreg();
34744                     }
34745                     if(Roo.dd.DDM.locationCache[dd]){
34746                         delete Roo.dd.DDM.locationCache[dd];
34747                     }
34748                 }
34749                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34750             }
34751         }
34752         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34753         this.bind(null, null);
34754         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34755     },
34756
34757     handleLockChange : function(){
34758         this.refresh(true);
34759     },
34760
34761     onDenyColumnLock : function(){
34762
34763     },
34764
34765     onDenyColumnHide : function(){
34766
34767     },
34768
34769     handleHdMenuClick : function(item){
34770         var index = this.hdCtxIndex;
34771         var cm = this.cm, ds = this.ds;
34772         switch(item.id){
34773             case "asc":
34774                 ds.sort(cm.getDataIndex(index), "ASC");
34775                 break;
34776             case "desc":
34777                 ds.sort(cm.getDataIndex(index), "DESC");
34778                 break;
34779             case "lock":
34780                 var lc = cm.getLockedCount();
34781                 if(cm.getColumnCount(true) <= lc+1){
34782                     this.onDenyColumnLock();
34783                     return;
34784                 }
34785                 if(lc != index){
34786                     cm.setLocked(index, true, true);
34787                     cm.moveColumn(index, lc);
34788                     this.grid.fireEvent("columnmove", index, lc);
34789                 }else{
34790                     cm.setLocked(index, true);
34791                 }
34792             break;
34793             case "unlock":
34794                 var lc = cm.getLockedCount();
34795                 if((lc-1) != index){
34796                     cm.setLocked(index, false, true);
34797                     cm.moveColumn(index, lc-1);
34798                     this.grid.fireEvent("columnmove", index, lc-1);
34799                 }else{
34800                     cm.setLocked(index, false);
34801                 }
34802             break;
34803             case 'wider': // used to expand cols on touch..
34804             case 'narrow':
34805                 var cw = cm.getColumnWidth(index);
34806                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34807                 cw = Math.max(0, cw);
34808                 cw = Math.min(cw,4000);
34809                 cm.setColumnWidth(index, cw);
34810                 break;
34811                 
34812             default:
34813                 index = cm.getIndexById(item.id.substr(4));
34814                 if(index != -1){
34815                     if(item.checked && cm.getColumnCount(true) <= 1){
34816                         this.onDenyColumnHide();
34817                         return false;
34818                     }
34819                     cm.setHidden(index, item.checked);
34820                 }
34821         }
34822         return true;
34823     },
34824
34825     beforeColMenuShow : function(){
34826         var cm = this.cm,  colCount = cm.getColumnCount();
34827         this.colMenu.removeAll();
34828         for(var i = 0; i < colCount; i++){
34829             this.colMenu.add(new Roo.menu.CheckItem({
34830                 id: "col-"+cm.getColumnId(i),
34831                 text: cm.getColumnHeader(i),
34832                 checked: !cm.isHidden(i),
34833                 hideOnClick:false
34834             }));
34835         }
34836     },
34837
34838     handleHdCtx : function(g, index, e){
34839         e.stopEvent();
34840         var hd = this.getHeaderCell(index);
34841         this.hdCtxIndex = index;
34842         var ms = this.hmenu.items, cm = this.cm;
34843         ms.get("asc").setDisabled(!cm.isSortable(index));
34844         ms.get("desc").setDisabled(!cm.isSortable(index));
34845         if(this.grid.enableColLock !== false){
34846             ms.get("lock").setDisabled(cm.isLocked(index));
34847             ms.get("unlock").setDisabled(!cm.isLocked(index));
34848         }
34849         this.hmenu.show(hd, "tl-bl");
34850     },
34851
34852     handleHdOver : function(e){
34853         var hd = this.findHeaderCell(e.getTarget());
34854         if(hd && !this.headersDisabled){
34855             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34856                this.fly(hd).addClass("x-grid-hd-over");
34857             }
34858         }
34859     },
34860
34861     handleHdOut : function(e){
34862         var hd = this.findHeaderCell(e.getTarget());
34863         if(hd){
34864             this.fly(hd).removeClass("x-grid-hd-over");
34865         }
34866     },
34867
34868     handleSplitDblClick : function(e, t){
34869         var i = this.getCellIndex(t);
34870         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34871             this.autoSizeColumn(i, true);
34872             this.layout();
34873         }
34874     },
34875
34876     render : function(){
34877
34878         var cm = this.cm;
34879         var colCount = cm.getColumnCount();
34880
34881         if(this.grid.monitorWindowResize === true){
34882             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34883         }
34884         var header = this.renderHeaders();
34885         var body = this.templates.body.apply({rows:""});
34886         var html = this.templates.master.apply({
34887             lockedBody: body,
34888             body: body,
34889             lockedHeader: header[0],
34890             header: header[1]
34891         });
34892
34893         //this.updateColumns();
34894
34895         this.grid.getGridEl().dom.innerHTML = html;
34896
34897         this.initElements();
34898         
34899         // a kludge to fix the random scolling effect in webkit
34900         this.el.on("scroll", function() {
34901             this.el.dom.scrollTop=0; // hopefully not recursive..
34902         },this);
34903
34904         this.scroller.on("scroll", this.handleScroll, this);
34905         this.lockedBody.on("mousewheel", this.handleWheel, this);
34906         this.mainBody.on("mousewheel", this.handleWheel, this);
34907
34908         this.mainHd.on("mouseover", this.handleHdOver, this);
34909         this.mainHd.on("mouseout", this.handleHdOut, this);
34910         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34911                 {delegate: "."+this.splitClass});
34912
34913         this.lockedHd.on("mouseover", this.handleHdOver, this);
34914         this.lockedHd.on("mouseout", this.handleHdOut, this);
34915         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34916                 {delegate: "."+this.splitClass});
34917
34918         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34919             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34920         }
34921
34922         this.updateSplitters();
34923
34924         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34925             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34926             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34927         }
34928
34929         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34930             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34931             this.hmenu.add(
34932                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34933                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34934             );
34935             if(this.grid.enableColLock !== false){
34936                 this.hmenu.add('-',
34937                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34938                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34939                 );
34940             }
34941             if (Roo.isTouch) {
34942                  this.hmenu.add('-',
34943                     {id:"wider", text: this.columnsWiderText},
34944                     {id:"narrow", text: this.columnsNarrowText }
34945                 );
34946                 
34947                  
34948             }
34949             
34950             if(this.grid.enableColumnHide !== false){
34951
34952                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34953                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34954                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34955
34956                 this.hmenu.add('-',
34957                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34958                 );
34959             }
34960             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34961
34962             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34963         }
34964
34965         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34966             this.dd = new Roo.grid.GridDragZone(this.grid, {
34967                 ddGroup : this.grid.ddGroup || 'GridDD'
34968             });
34969             
34970         }
34971
34972         /*
34973         for(var i = 0; i < colCount; i++){
34974             if(cm.isHidden(i)){
34975                 this.hideColumn(i);
34976             }
34977             if(cm.config[i].align){
34978                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34979                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34980             }
34981         }*/
34982         
34983         this.updateHeaderSortState();
34984
34985         this.beforeInitialResize();
34986         this.layout(true);
34987
34988         // two part rendering gives faster view to the user
34989         this.renderPhase2.defer(1, this);
34990     },
34991
34992     renderPhase2 : function(){
34993         // render the rows now
34994         this.refresh();
34995         if(this.grid.autoSizeColumns){
34996             this.autoSizeColumns();
34997         }
34998     },
34999
35000     beforeInitialResize : function(){
35001
35002     },
35003
35004     onColumnSplitterMoved : function(i, w){
35005         this.userResized = true;
35006         var cm = this.grid.colModel;
35007         cm.setColumnWidth(i, w, true);
35008         var cid = cm.getColumnId(i);
35009         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35010         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35011         this.updateSplitters();
35012         this.layout();
35013         this.grid.fireEvent("columnresize", i, w);
35014     },
35015
35016     syncRowHeights : function(startIndex, endIndex){
35017         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35018             startIndex = startIndex || 0;
35019             var mrows = this.getBodyTable().rows;
35020             var lrows = this.getLockedTable().rows;
35021             var len = mrows.length-1;
35022             endIndex = Math.min(endIndex || len, len);
35023             for(var i = startIndex; i <= endIndex; i++){
35024                 var m = mrows[i], l = lrows[i];
35025                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35026                 m.style.height = l.style.height = h + "px";
35027             }
35028         }
35029     },
35030
35031     layout : function(initialRender, is2ndPass){
35032         var g = this.grid;
35033         var auto = g.autoHeight;
35034         var scrollOffset = 16;
35035         var c = g.getGridEl(), cm = this.cm,
35036                 expandCol = g.autoExpandColumn,
35037                 gv = this;
35038         //c.beginMeasure();
35039
35040         if(!c.dom.offsetWidth){ // display:none?
35041             if(initialRender){
35042                 this.lockedWrap.show();
35043                 this.mainWrap.show();
35044             }
35045             return;
35046         }
35047
35048         var hasLock = this.cm.isLocked(0);
35049
35050         var tbh = this.headerPanel.getHeight();
35051         var bbh = this.footerPanel.getHeight();
35052
35053         if(auto){
35054             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35055             var newHeight = ch + c.getBorderWidth("tb");
35056             if(g.maxHeight){
35057                 newHeight = Math.min(g.maxHeight, newHeight);
35058             }
35059             c.setHeight(newHeight);
35060         }
35061
35062         if(g.autoWidth){
35063             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35064         }
35065
35066         var s = this.scroller;
35067
35068         var csize = c.getSize(true);
35069
35070         this.el.setSize(csize.width, csize.height);
35071
35072         this.headerPanel.setWidth(csize.width);
35073         this.footerPanel.setWidth(csize.width);
35074
35075         var hdHeight = this.mainHd.getHeight();
35076         var vw = csize.width;
35077         var vh = csize.height - (tbh + bbh);
35078
35079         s.setSize(vw, vh);
35080
35081         var bt = this.getBodyTable();
35082         
35083         if(cm.getLockedCount() == cm.config.length){
35084             bt = this.getLockedTable();
35085         }
35086         
35087         var ltWidth = hasLock ?
35088                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35089
35090         var scrollHeight = bt.offsetHeight;
35091         var scrollWidth = ltWidth + bt.offsetWidth;
35092         var vscroll = false, hscroll = false;
35093
35094         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35095
35096         var lw = this.lockedWrap, mw = this.mainWrap;
35097         var lb = this.lockedBody, mb = this.mainBody;
35098
35099         setTimeout(function(){
35100             var t = s.dom.offsetTop;
35101             var w = s.dom.clientWidth,
35102                 h = s.dom.clientHeight;
35103
35104             lw.setTop(t);
35105             lw.setSize(ltWidth, h);
35106
35107             mw.setLeftTop(ltWidth, t);
35108             mw.setSize(w-ltWidth, h);
35109
35110             lb.setHeight(h-hdHeight);
35111             mb.setHeight(h-hdHeight);
35112
35113             if(is2ndPass !== true && !gv.userResized && expandCol){
35114                 // high speed resize without full column calculation
35115                 
35116                 var ci = cm.getIndexById(expandCol);
35117                 if (ci < 0) {
35118                     ci = cm.findColumnIndex(expandCol);
35119                 }
35120                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35121                 var expandId = cm.getColumnId(ci);
35122                 var  tw = cm.getTotalWidth(false);
35123                 var currentWidth = cm.getColumnWidth(ci);
35124                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35125                 if(currentWidth != cw){
35126                     cm.setColumnWidth(ci, cw, true);
35127                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35128                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35129                     gv.updateSplitters();
35130                     gv.layout(false, true);
35131                 }
35132             }
35133
35134             if(initialRender){
35135                 lw.show();
35136                 mw.show();
35137             }
35138             //c.endMeasure();
35139         }, 10);
35140     },
35141
35142     onWindowResize : function(){
35143         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35144             return;
35145         }
35146         this.layout();
35147     },
35148
35149     appendFooter : function(parentEl){
35150         return null;
35151     },
35152
35153     sortAscText : "Sort Ascending",
35154     sortDescText : "Sort Descending",
35155     lockText : "Lock Column",
35156     unlockText : "Unlock Column",
35157     columnsText : "Columns",
35158  
35159     columnsWiderText : "Wider",
35160     columnsNarrowText : "Thinner"
35161 });
35162
35163
35164 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35165     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35166     this.proxy.el.addClass('x-grid3-col-dd');
35167 };
35168
35169 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35170     handleMouseDown : function(e){
35171
35172     },
35173
35174     callHandleMouseDown : function(e){
35175         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35176     }
35177 });
35178 /*
35179  * Based on:
35180  * Ext JS Library 1.1.1
35181  * Copyright(c) 2006-2007, Ext JS, LLC.
35182  *
35183  * Originally Released Under LGPL - original licence link has changed is not relivant.
35184  *
35185  * Fork - LGPL
35186  * <script type="text/javascript">
35187  */
35188  
35189 // private
35190 // This is a support class used internally by the Grid components
35191 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35192     this.grid = grid;
35193     this.view = grid.getView();
35194     this.proxy = this.view.resizeProxy;
35195     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35196         "gridSplitters" + this.grid.getGridEl().id, {
35197         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35198     });
35199     this.setHandleElId(Roo.id(hd));
35200     this.setOuterHandleElId(Roo.id(hd2));
35201     this.scroll = false;
35202 };
35203 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35204     fly: Roo.Element.fly,
35205
35206     b4StartDrag : function(x, y){
35207         this.view.headersDisabled = true;
35208         this.proxy.setHeight(this.view.mainWrap.getHeight());
35209         var w = this.cm.getColumnWidth(this.cellIndex);
35210         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35211         this.resetConstraints();
35212         this.setXConstraint(minw, 1000);
35213         this.setYConstraint(0, 0);
35214         this.minX = x - minw;
35215         this.maxX = x + 1000;
35216         this.startPos = x;
35217         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35218     },
35219
35220
35221     handleMouseDown : function(e){
35222         ev = Roo.EventObject.setEvent(e);
35223         var t = this.fly(ev.getTarget());
35224         if(t.hasClass("x-grid-split")){
35225             this.cellIndex = this.view.getCellIndex(t.dom);
35226             this.split = t.dom;
35227             this.cm = this.grid.colModel;
35228             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35229                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35230             }
35231         }
35232     },
35233
35234     endDrag : function(e){
35235         this.view.headersDisabled = false;
35236         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35237         var diff = endX - this.startPos;
35238         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35239     },
35240
35241     autoOffset : function(){
35242         this.setDelta(0,0);
35243     }
35244 });/*
35245  * Based on:
35246  * Ext JS Library 1.1.1
35247  * Copyright(c) 2006-2007, Ext JS, LLC.
35248  *
35249  * Originally Released Under LGPL - original licence link has changed is not relivant.
35250  *
35251  * Fork - LGPL
35252  * <script type="text/javascript">
35253  */
35254  
35255 // private
35256 // This is a support class used internally by the Grid components
35257 Roo.grid.GridDragZone = function(grid, config){
35258     this.view = grid.getView();
35259     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35260     if(this.view.lockedBody){
35261         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35262         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35263     }
35264     this.scroll = false;
35265     this.grid = grid;
35266     this.ddel = document.createElement('div');
35267     this.ddel.className = 'x-grid-dd-wrap';
35268 };
35269
35270 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35271     ddGroup : "GridDD",
35272
35273     getDragData : function(e){
35274         var t = Roo.lib.Event.getTarget(e);
35275         var rowIndex = this.view.findRowIndex(t);
35276         var sm = this.grid.selModel;
35277             
35278         //Roo.log(rowIndex);
35279         
35280         if (sm.getSelectedCell) {
35281             // cell selection..
35282             if (!sm.getSelectedCell()) {
35283                 return false;
35284             }
35285             if (rowIndex != sm.getSelectedCell()[0]) {
35286                 return false;
35287             }
35288         
35289         }
35290         
35291         if(rowIndex !== false){
35292             
35293             // if editorgrid.. 
35294             
35295             
35296             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35297                
35298             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35299               //  
35300             //}
35301             if (e.hasModifier()){
35302                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35303             }
35304             
35305             Roo.log("getDragData");
35306             
35307             return {
35308                 grid: this.grid,
35309                 ddel: this.ddel,
35310                 rowIndex: rowIndex,
35311                 selections:sm.getSelections ? sm.getSelections() : (
35312                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35313                 )
35314             };
35315         }
35316         return false;
35317     },
35318
35319     onInitDrag : function(e){
35320         var data = this.dragData;
35321         this.ddel.innerHTML = this.grid.getDragDropText();
35322         this.proxy.update(this.ddel);
35323         // fire start drag?
35324     },
35325
35326     afterRepair : function(){
35327         this.dragging = false;
35328     },
35329
35330     getRepairXY : function(e, data){
35331         return false;
35332     },
35333
35334     onEndDrag : function(data, e){
35335         // fire end drag?
35336     },
35337
35338     onValidDrop : function(dd, e, id){
35339         // fire drag drop?
35340         this.hideProxy();
35341     },
35342
35343     beforeInvalidDrop : function(e, id){
35344
35345     }
35346 });/*
35347  * Based on:
35348  * Ext JS Library 1.1.1
35349  * Copyright(c) 2006-2007, Ext JS, LLC.
35350  *
35351  * Originally Released Under LGPL - original licence link has changed is not relivant.
35352  *
35353  * Fork - LGPL
35354  * <script type="text/javascript">
35355  */
35356  
35357
35358 /**
35359  * @class Roo.grid.ColumnModel
35360  * @extends Roo.util.Observable
35361  * This is the default implementation of a ColumnModel used by the Grid. It defines
35362  * the columns in the grid.
35363  * <br>Usage:<br>
35364  <pre><code>
35365  var colModel = new Roo.grid.ColumnModel([
35366         {header: "Ticker", width: 60, sortable: true, locked: true},
35367         {header: "Company Name", width: 150, sortable: true},
35368         {header: "Market Cap.", width: 100, sortable: true},
35369         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35370         {header: "Employees", width: 100, sortable: true, resizable: false}
35371  ]);
35372  </code></pre>
35373  * <p>
35374  
35375  * The config options listed for this class are options which may appear in each
35376  * individual column definition.
35377  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35378  * @constructor
35379  * @param {Object} config An Array of column config objects. See this class's
35380  * config objects for details.
35381 */
35382 Roo.grid.ColumnModel = function(config){
35383         /**
35384      * The config passed into the constructor
35385      */
35386     this.config = config;
35387     this.lookup = {};
35388
35389     // if no id, create one
35390     // if the column does not have a dataIndex mapping,
35391     // map it to the order it is in the config
35392     for(var i = 0, len = config.length; i < len; i++){
35393         var c = config[i];
35394         if(typeof c.dataIndex == "undefined"){
35395             c.dataIndex = i;
35396         }
35397         if(typeof c.renderer == "string"){
35398             c.renderer = Roo.util.Format[c.renderer];
35399         }
35400         if(typeof c.id == "undefined"){
35401             c.id = Roo.id();
35402         }
35403         if(c.editor && c.editor.xtype){
35404             c.editor  = Roo.factory(c.editor, Roo.grid);
35405         }
35406         if(c.editor && c.editor.isFormField){
35407             c.editor = new Roo.grid.GridEditor(c.editor);
35408         }
35409         this.lookup[c.id] = c;
35410     }
35411
35412     /**
35413      * The width of columns which have no width specified (defaults to 100)
35414      * @type Number
35415      */
35416     this.defaultWidth = 100;
35417
35418     /**
35419      * Default sortable of columns which have no sortable specified (defaults to false)
35420      * @type Boolean
35421      */
35422     this.defaultSortable = false;
35423
35424     this.addEvents({
35425         /**
35426              * @event widthchange
35427              * Fires when the width of a column changes.
35428              * @param {ColumnModel} this
35429              * @param {Number} columnIndex The column index
35430              * @param {Number} newWidth The new width
35431              */
35432             "widthchange": true,
35433         /**
35434              * @event headerchange
35435              * Fires when the text of a header changes.
35436              * @param {ColumnModel} this
35437              * @param {Number} columnIndex The column index
35438              * @param {Number} newText The new header text
35439              */
35440             "headerchange": true,
35441         /**
35442              * @event hiddenchange
35443              * Fires when a column is hidden or "unhidden".
35444              * @param {ColumnModel} this
35445              * @param {Number} columnIndex The column index
35446              * @param {Boolean} hidden true if hidden, false otherwise
35447              */
35448             "hiddenchange": true,
35449             /**
35450          * @event columnmoved
35451          * Fires when a column is moved.
35452          * @param {ColumnModel} this
35453          * @param {Number} oldIndex
35454          * @param {Number} newIndex
35455          */
35456         "columnmoved" : true,
35457         /**
35458          * @event columlockchange
35459          * Fires when a column's locked state is changed
35460          * @param {ColumnModel} this
35461          * @param {Number} colIndex
35462          * @param {Boolean} locked true if locked
35463          */
35464         "columnlockchange" : true
35465     });
35466     Roo.grid.ColumnModel.superclass.constructor.call(this);
35467 };
35468 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35469     /**
35470      * @cfg {String} header The header text to display in the Grid view.
35471      */
35472     /**
35473      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35474      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35475      * specified, the column's index is used as an index into the Record's data Array.
35476      */
35477     /**
35478      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35479      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35480      */
35481     /**
35482      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35483      * Defaults to the value of the {@link #defaultSortable} property.
35484      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35485      */
35486     /**
35487      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35488      */
35489     /**
35490      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35491      */
35492     /**
35493      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35494      */
35495     /**
35496      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35497      */
35498     /**
35499      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35500      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35501      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35502      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35503      */
35504        /**
35505      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35506      */
35507     /**
35508      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35509      */
35510     /**
35511      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35512      */
35513     /**
35514      * @cfg {String} cursor (Optional)
35515      */
35516     /**
35517      * @cfg {String} tooltip (Optional)
35518      */
35519     /**
35520      * @cfg {Number} xs (Optional)
35521      */
35522     /**
35523      * @cfg {Number} sm (Optional)
35524      */
35525     /**
35526      * @cfg {Number} md (Optional)
35527      */
35528     /**
35529      * @cfg {Number} lg (Optional)
35530      */
35531     /**
35532      * Returns the id of the column at the specified index.
35533      * @param {Number} index The column index
35534      * @return {String} the id
35535      */
35536     getColumnId : function(index){
35537         return this.config[index].id;
35538     },
35539
35540     /**
35541      * Returns the column for a specified id.
35542      * @param {String} id The column id
35543      * @return {Object} the column
35544      */
35545     getColumnById : function(id){
35546         return this.lookup[id];
35547     },
35548
35549     
35550     /**
35551      * Returns the column for a specified dataIndex.
35552      * @param {String} dataIndex The column dataIndex
35553      * @return {Object|Boolean} the column or false if not found
35554      */
35555     getColumnByDataIndex: function(dataIndex){
35556         var index = this.findColumnIndex(dataIndex);
35557         return index > -1 ? this.config[index] : false;
35558     },
35559     
35560     /**
35561      * Returns the index for a specified column id.
35562      * @param {String} id The column id
35563      * @return {Number} the index, or -1 if not found
35564      */
35565     getIndexById : function(id){
35566         for(var i = 0, len = this.config.length; i < len; i++){
35567             if(this.config[i].id == id){
35568                 return i;
35569             }
35570         }
35571         return -1;
35572     },
35573     
35574     /**
35575      * Returns the index for a specified column dataIndex.
35576      * @param {String} dataIndex The column dataIndex
35577      * @return {Number} the index, or -1 if not found
35578      */
35579     
35580     findColumnIndex : function(dataIndex){
35581         for(var i = 0, len = this.config.length; i < len; i++){
35582             if(this.config[i].dataIndex == dataIndex){
35583                 return i;
35584             }
35585         }
35586         return -1;
35587     },
35588     
35589     
35590     moveColumn : function(oldIndex, newIndex){
35591         var c = this.config[oldIndex];
35592         this.config.splice(oldIndex, 1);
35593         this.config.splice(newIndex, 0, c);
35594         this.dataMap = null;
35595         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35596     },
35597
35598     isLocked : function(colIndex){
35599         return this.config[colIndex].locked === true;
35600     },
35601
35602     setLocked : function(colIndex, value, suppressEvent){
35603         if(this.isLocked(colIndex) == value){
35604             return;
35605         }
35606         this.config[colIndex].locked = value;
35607         if(!suppressEvent){
35608             this.fireEvent("columnlockchange", this, colIndex, value);
35609         }
35610     },
35611
35612     getTotalLockedWidth : function(){
35613         var totalWidth = 0;
35614         for(var i = 0; i < this.config.length; i++){
35615             if(this.isLocked(i) && !this.isHidden(i)){
35616                 this.totalWidth += this.getColumnWidth(i);
35617             }
35618         }
35619         return totalWidth;
35620     },
35621
35622     getLockedCount : function(){
35623         for(var i = 0, len = this.config.length; i < len; i++){
35624             if(!this.isLocked(i)){
35625                 return i;
35626             }
35627         }
35628         
35629         return this.config.length;
35630     },
35631
35632     /**
35633      * Returns the number of columns.
35634      * @return {Number}
35635      */
35636     getColumnCount : function(visibleOnly){
35637         if(visibleOnly === true){
35638             var c = 0;
35639             for(var i = 0, len = this.config.length; i < len; i++){
35640                 if(!this.isHidden(i)){
35641                     c++;
35642                 }
35643             }
35644             return c;
35645         }
35646         return this.config.length;
35647     },
35648
35649     /**
35650      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35651      * @param {Function} fn
35652      * @param {Object} scope (optional)
35653      * @return {Array} result
35654      */
35655     getColumnsBy : function(fn, scope){
35656         var r = [];
35657         for(var i = 0, len = this.config.length; i < len; i++){
35658             var c = this.config[i];
35659             if(fn.call(scope||this, c, i) === true){
35660                 r[r.length] = c;
35661             }
35662         }
35663         return r;
35664     },
35665
35666     /**
35667      * Returns true if the specified column is sortable.
35668      * @param {Number} col The column index
35669      * @return {Boolean}
35670      */
35671     isSortable : function(col){
35672         if(typeof this.config[col].sortable == "undefined"){
35673             return this.defaultSortable;
35674         }
35675         return this.config[col].sortable;
35676     },
35677
35678     /**
35679      * Returns the rendering (formatting) function defined for the column.
35680      * @param {Number} col The column index.
35681      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35682      */
35683     getRenderer : function(col){
35684         if(!this.config[col].renderer){
35685             return Roo.grid.ColumnModel.defaultRenderer;
35686         }
35687         return this.config[col].renderer;
35688     },
35689
35690     /**
35691      * Sets the rendering (formatting) function for a column.
35692      * @param {Number} col The column index
35693      * @param {Function} fn The function to use to process the cell's raw data
35694      * to return HTML markup for the grid view. The render function is called with
35695      * the following parameters:<ul>
35696      * <li>Data value.</li>
35697      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35698      * <li>css A CSS style string to apply to the table cell.</li>
35699      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35700      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35701      * <li>Row index</li>
35702      * <li>Column index</li>
35703      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35704      */
35705     setRenderer : function(col, fn){
35706         this.config[col].renderer = fn;
35707     },
35708
35709     /**
35710      * Returns the width for the specified column.
35711      * @param {Number} col The column index
35712      * @return {Number}
35713      */
35714     getColumnWidth : function(col){
35715         return this.config[col].width * 1 || this.defaultWidth;
35716     },
35717
35718     /**
35719      * Sets the width for a column.
35720      * @param {Number} col The column index
35721      * @param {Number} width The new width
35722      */
35723     setColumnWidth : function(col, width, suppressEvent){
35724         this.config[col].width = width;
35725         this.totalWidth = null;
35726         if(!suppressEvent){
35727              this.fireEvent("widthchange", this, col, width);
35728         }
35729     },
35730
35731     /**
35732      * Returns the total width of all columns.
35733      * @param {Boolean} includeHidden True to include hidden column widths
35734      * @return {Number}
35735      */
35736     getTotalWidth : function(includeHidden){
35737         if(!this.totalWidth){
35738             this.totalWidth = 0;
35739             for(var i = 0, len = this.config.length; i < len; i++){
35740                 if(includeHidden || !this.isHidden(i)){
35741                     this.totalWidth += this.getColumnWidth(i);
35742                 }
35743             }
35744         }
35745         return this.totalWidth;
35746     },
35747
35748     /**
35749      * Returns the header for the specified column.
35750      * @param {Number} col The column index
35751      * @return {String}
35752      */
35753     getColumnHeader : function(col){
35754         return this.config[col].header;
35755     },
35756
35757     /**
35758      * Sets the header for a column.
35759      * @param {Number} col The column index
35760      * @param {String} header The new header
35761      */
35762     setColumnHeader : function(col, header){
35763         this.config[col].header = header;
35764         this.fireEvent("headerchange", this, col, header);
35765     },
35766
35767     /**
35768      * Returns the tooltip for the specified column.
35769      * @param {Number} col The column index
35770      * @return {String}
35771      */
35772     getColumnTooltip : function(col){
35773             return this.config[col].tooltip;
35774     },
35775     /**
35776      * Sets the tooltip for a column.
35777      * @param {Number} col The column index
35778      * @param {String} tooltip The new tooltip
35779      */
35780     setColumnTooltip : function(col, tooltip){
35781             this.config[col].tooltip = tooltip;
35782     },
35783
35784     /**
35785      * Returns the dataIndex for the specified column.
35786      * @param {Number} col The column index
35787      * @return {Number}
35788      */
35789     getDataIndex : function(col){
35790         return this.config[col].dataIndex;
35791     },
35792
35793     /**
35794      * Sets the dataIndex for a column.
35795      * @param {Number} col The column index
35796      * @param {Number} dataIndex The new dataIndex
35797      */
35798     setDataIndex : function(col, dataIndex){
35799         this.config[col].dataIndex = dataIndex;
35800     },
35801
35802     
35803     
35804     /**
35805      * Returns true if the cell is editable.
35806      * @param {Number} colIndex The column index
35807      * @param {Number} rowIndex The row index - this is nto actually used..?
35808      * @return {Boolean}
35809      */
35810     isCellEditable : function(colIndex, rowIndex){
35811         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35812     },
35813
35814     /**
35815      * Returns the editor defined for the cell/column.
35816      * return false or null to disable editing.
35817      * @param {Number} colIndex The column index
35818      * @param {Number} rowIndex The row index
35819      * @return {Object}
35820      */
35821     getCellEditor : function(colIndex, rowIndex){
35822         return this.config[colIndex].editor;
35823     },
35824
35825     /**
35826      * Sets if a column is editable.
35827      * @param {Number} col The column index
35828      * @param {Boolean} editable True if the column is editable
35829      */
35830     setEditable : function(col, editable){
35831         this.config[col].editable = editable;
35832     },
35833
35834
35835     /**
35836      * Returns true if the column is hidden.
35837      * @param {Number} colIndex The column index
35838      * @return {Boolean}
35839      */
35840     isHidden : function(colIndex){
35841         return this.config[colIndex].hidden;
35842     },
35843
35844
35845     /**
35846      * Returns true if the column width cannot be changed
35847      */
35848     isFixed : function(colIndex){
35849         return this.config[colIndex].fixed;
35850     },
35851
35852     /**
35853      * Returns true if the column can be resized
35854      * @return {Boolean}
35855      */
35856     isResizable : function(colIndex){
35857         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35858     },
35859     /**
35860      * Sets if a column is hidden.
35861      * @param {Number} colIndex The column index
35862      * @param {Boolean} hidden True if the column is hidden
35863      */
35864     setHidden : function(colIndex, hidden){
35865         this.config[colIndex].hidden = hidden;
35866         this.totalWidth = null;
35867         this.fireEvent("hiddenchange", this, colIndex, hidden);
35868     },
35869
35870     /**
35871      * Sets the editor for a column.
35872      * @param {Number} col The column index
35873      * @param {Object} editor The editor object
35874      */
35875     setEditor : function(col, editor){
35876         this.config[col].editor = editor;
35877     }
35878 });
35879
35880 Roo.grid.ColumnModel.defaultRenderer = function(value)
35881 {
35882     if(typeof value == "object") {
35883         return value;
35884     }
35885         if(typeof value == "string" && value.length < 1){
35886             return "&#160;";
35887         }
35888     
35889         return String.format("{0}", value);
35890 };
35891
35892 // Alias for backwards compatibility
35893 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35894 /*
35895  * Based on:
35896  * Ext JS Library 1.1.1
35897  * Copyright(c) 2006-2007, Ext JS, LLC.
35898  *
35899  * Originally Released Under LGPL - original licence link has changed is not relivant.
35900  *
35901  * Fork - LGPL
35902  * <script type="text/javascript">
35903  */
35904
35905 /**
35906  * @class Roo.grid.AbstractSelectionModel
35907  * @extends Roo.util.Observable
35908  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35909  * implemented by descendant classes.  This class should not be directly instantiated.
35910  * @constructor
35911  */
35912 Roo.grid.AbstractSelectionModel = function(){
35913     this.locked = false;
35914     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35915 };
35916
35917 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35918     /** @ignore Called by the grid automatically. Do not call directly. */
35919     init : function(grid){
35920         this.grid = grid;
35921         this.initEvents();
35922     },
35923
35924     /**
35925      * Locks the selections.
35926      */
35927     lock : function(){
35928         this.locked = true;
35929     },
35930
35931     /**
35932      * Unlocks the selections.
35933      */
35934     unlock : function(){
35935         this.locked = false;
35936     },
35937
35938     /**
35939      * Returns true if the selections are locked.
35940      * @return {Boolean}
35941      */
35942     isLocked : function(){
35943         return this.locked;
35944     }
35945 });/*
35946  * Based on:
35947  * Ext JS Library 1.1.1
35948  * Copyright(c) 2006-2007, Ext JS, LLC.
35949  *
35950  * Originally Released Under LGPL - original licence link has changed is not relivant.
35951  *
35952  * Fork - LGPL
35953  * <script type="text/javascript">
35954  */
35955 /**
35956  * @extends Roo.grid.AbstractSelectionModel
35957  * @class Roo.grid.RowSelectionModel
35958  * The default SelectionModel used by {@link Roo.grid.Grid}.
35959  * It supports multiple selections and keyboard selection/navigation. 
35960  * @constructor
35961  * @param {Object} config
35962  */
35963 Roo.grid.RowSelectionModel = function(config){
35964     Roo.apply(this, config);
35965     this.selections = new Roo.util.MixedCollection(false, function(o){
35966         return o.id;
35967     });
35968
35969     this.last = false;
35970     this.lastActive = false;
35971
35972     this.addEvents({
35973         /**
35974              * @event selectionchange
35975              * Fires when the selection changes
35976              * @param {SelectionModel} this
35977              */
35978             "selectionchange" : true,
35979         /**
35980              * @event afterselectionchange
35981              * Fires after the selection changes (eg. by key press or clicking)
35982              * @param {SelectionModel} this
35983              */
35984             "afterselectionchange" : true,
35985         /**
35986              * @event beforerowselect
35987              * Fires when a row is selected being selected, return false to cancel.
35988              * @param {SelectionModel} this
35989              * @param {Number} rowIndex The selected index
35990              * @param {Boolean} keepExisting False if other selections will be cleared
35991              */
35992             "beforerowselect" : true,
35993         /**
35994              * @event rowselect
35995              * Fires when a row is selected.
35996              * @param {SelectionModel} this
35997              * @param {Number} rowIndex The selected index
35998              * @param {Roo.data.Record} r The record
35999              */
36000             "rowselect" : true,
36001         /**
36002              * @event rowdeselect
36003              * Fires when a row is deselected.
36004              * @param {SelectionModel} this
36005              * @param {Number} rowIndex The selected index
36006              */
36007         "rowdeselect" : true
36008     });
36009     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36010     this.locked = false;
36011 };
36012
36013 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36014     /**
36015      * @cfg {Boolean} singleSelect
36016      * True to allow selection of only one row at a time (defaults to false)
36017      */
36018     singleSelect : false,
36019
36020     // private
36021     initEvents : function(){
36022
36023         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36024             this.grid.on("mousedown", this.handleMouseDown, this);
36025         }else{ // allow click to work like normal
36026             this.grid.on("rowclick", this.handleDragableRowClick, this);
36027         }
36028
36029         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36030             "up" : function(e){
36031                 if(!e.shiftKey){
36032                     this.selectPrevious(e.shiftKey);
36033                 }else if(this.last !== false && this.lastActive !== false){
36034                     var last = this.last;
36035                     this.selectRange(this.last,  this.lastActive-1);
36036                     this.grid.getView().focusRow(this.lastActive);
36037                     if(last !== false){
36038                         this.last = last;
36039                     }
36040                 }else{
36041                     this.selectFirstRow();
36042                 }
36043                 this.fireEvent("afterselectionchange", this);
36044             },
36045             "down" : function(e){
36046                 if(!e.shiftKey){
36047                     this.selectNext(e.shiftKey);
36048                 }else if(this.last !== false && this.lastActive !== false){
36049                     var last = this.last;
36050                     this.selectRange(this.last,  this.lastActive+1);
36051                     this.grid.getView().focusRow(this.lastActive);
36052                     if(last !== false){
36053                         this.last = last;
36054                     }
36055                 }else{
36056                     this.selectFirstRow();
36057                 }
36058                 this.fireEvent("afterselectionchange", this);
36059             },
36060             scope: this
36061         });
36062
36063         var view = this.grid.view;
36064         view.on("refresh", this.onRefresh, this);
36065         view.on("rowupdated", this.onRowUpdated, this);
36066         view.on("rowremoved", this.onRemove, this);
36067     },
36068
36069     // private
36070     onRefresh : function(){
36071         var ds = this.grid.dataSource, i, v = this.grid.view;
36072         var s = this.selections;
36073         s.each(function(r){
36074             if((i = ds.indexOfId(r.id)) != -1){
36075                 v.onRowSelect(i);
36076                 s.add(ds.getAt(i)); // updating the selection relate data
36077             }else{
36078                 s.remove(r);
36079             }
36080         });
36081     },
36082
36083     // private
36084     onRemove : function(v, index, r){
36085         this.selections.remove(r);
36086     },
36087
36088     // private
36089     onRowUpdated : function(v, index, r){
36090         if(this.isSelected(r)){
36091             v.onRowSelect(index);
36092         }
36093     },
36094
36095     /**
36096      * Select records.
36097      * @param {Array} records The records to select
36098      * @param {Boolean} keepExisting (optional) True to keep existing selections
36099      */
36100     selectRecords : function(records, keepExisting){
36101         if(!keepExisting){
36102             this.clearSelections();
36103         }
36104         var ds = this.grid.dataSource;
36105         for(var i = 0, len = records.length; i < len; i++){
36106             this.selectRow(ds.indexOf(records[i]), true);
36107         }
36108     },
36109
36110     /**
36111      * Gets the number of selected rows.
36112      * @return {Number}
36113      */
36114     getCount : function(){
36115         return this.selections.length;
36116     },
36117
36118     /**
36119      * Selects the first row in the grid.
36120      */
36121     selectFirstRow : function(){
36122         this.selectRow(0);
36123     },
36124
36125     /**
36126      * Select the last row.
36127      * @param {Boolean} keepExisting (optional) True to keep existing selections
36128      */
36129     selectLastRow : function(keepExisting){
36130         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36131     },
36132
36133     /**
36134      * Selects the row immediately following the last selected row.
36135      * @param {Boolean} keepExisting (optional) True to keep existing selections
36136      */
36137     selectNext : function(keepExisting){
36138         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36139             this.selectRow(this.last+1, keepExisting);
36140             this.grid.getView().focusRow(this.last);
36141         }
36142     },
36143
36144     /**
36145      * Selects the row that precedes the last selected row.
36146      * @param {Boolean} keepExisting (optional) True to keep existing selections
36147      */
36148     selectPrevious : function(keepExisting){
36149         if(this.last){
36150             this.selectRow(this.last-1, keepExisting);
36151             this.grid.getView().focusRow(this.last);
36152         }
36153     },
36154
36155     /**
36156      * Returns the selected records
36157      * @return {Array} Array of selected records
36158      */
36159     getSelections : function(){
36160         return [].concat(this.selections.items);
36161     },
36162
36163     /**
36164      * Returns the first selected record.
36165      * @return {Record}
36166      */
36167     getSelected : function(){
36168         return this.selections.itemAt(0);
36169     },
36170
36171
36172     /**
36173      * Clears all selections.
36174      */
36175     clearSelections : function(fast){
36176         if(this.locked) {
36177             return;
36178         }
36179         if(fast !== true){
36180             var ds = this.grid.dataSource;
36181             var s = this.selections;
36182             s.each(function(r){
36183                 this.deselectRow(ds.indexOfId(r.id));
36184             }, this);
36185             s.clear();
36186         }else{
36187             this.selections.clear();
36188         }
36189         this.last = false;
36190     },
36191
36192
36193     /**
36194      * Selects all rows.
36195      */
36196     selectAll : function(){
36197         if(this.locked) {
36198             return;
36199         }
36200         this.selections.clear();
36201         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36202             this.selectRow(i, true);
36203         }
36204     },
36205
36206     /**
36207      * Returns True if there is a selection.
36208      * @return {Boolean}
36209      */
36210     hasSelection : function(){
36211         return this.selections.length > 0;
36212     },
36213
36214     /**
36215      * Returns True if the specified row is selected.
36216      * @param {Number/Record} record The record or index of the record to check
36217      * @return {Boolean}
36218      */
36219     isSelected : function(index){
36220         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36221         return (r && this.selections.key(r.id) ? true : false);
36222     },
36223
36224     /**
36225      * Returns True if the specified record id is selected.
36226      * @param {String} id The id of record to check
36227      * @return {Boolean}
36228      */
36229     isIdSelected : function(id){
36230         return (this.selections.key(id) ? true : false);
36231     },
36232
36233     // private
36234     handleMouseDown : function(e, t){
36235         var view = this.grid.getView(), rowIndex;
36236         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36237             return;
36238         };
36239         if(e.shiftKey && this.last !== false){
36240             var last = this.last;
36241             this.selectRange(last, rowIndex, e.ctrlKey);
36242             this.last = last; // reset the last
36243             view.focusRow(rowIndex);
36244         }else{
36245             var isSelected = this.isSelected(rowIndex);
36246             if(e.button !== 0 && isSelected){
36247                 view.focusRow(rowIndex);
36248             }else if(e.ctrlKey && isSelected){
36249                 this.deselectRow(rowIndex);
36250             }else if(!isSelected){
36251                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36252                 view.focusRow(rowIndex);
36253             }
36254         }
36255         this.fireEvent("afterselectionchange", this);
36256     },
36257     // private
36258     handleDragableRowClick :  function(grid, rowIndex, e) 
36259     {
36260         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36261             this.selectRow(rowIndex, false);
36262             grid.view.focusRow(rowIndex);
36263              this.fireEvent("afterselectionchange", this);
36264         }
36265     },
36266     
36267     /**
36268      * Selects multiple rows.
36269      * @param {Array} rows Array of the indexes of the row to select
36270      * @param {Boolean} keepExisting (optional) True to keep existing selections
36271      */
36272     selectRows : function(rows, keepExisting){
36273         if(!keepExisting){
36274             this.clearSelections();
36275         }
36276         for(var i = 0, len = rows.length; i < len; i++){
36277             this.selectRow(rows[i], true);
36278         }
36279     },
36280
36281     /**
36282      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36283      * @param {Number} startRow The index of the first row in the range
36284      * @param {Number} endRow The index of the last row in the range
36285      * @param {Boolean} keepExisting (optional) True to retain existing selections
36286      */
36287     selectRange : function(startRow, endRow, keepExisting){
36288         if(this.locked) {
36289             return;
36290         }
36291         if(!keepExisting){
36292             this.clearSelections();
36293         }
36294         if(startRow <= endRow){
36295             for(var i = startRow; i <= endRow; i++){
36296                 this.selectRow(i, true);
36297             }
36298         }else{
36299             for(var i = startRow; i >= endRow; i--){
36300                 this.selectRow(i, true);
36301             }
36302         }
36303     },
36304
36305     /**
36306      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36307      * @param {Number} startRow The index of the first row in the range
36308      * @param {Number} endRow The index of the last row in the range
36309      */
36310     deselectRange : function(startRow, endRow, preventViewNotify){
36311         if(this.locked) {
36312             return;
36313         }
36314         for(var i = startRow; i <= endRow; i++){
36315             this.deselectRow(i, preventViewNotify);
36316         }
36317     },
36318
36319     /**
36320      * Selects a row.
36321      * @param {Number} row The index of the row to select
36322      * @param {Boolean} keepExisting (optional) True to keep existing selections
36323      */
36324     selectRow : function(index, keepExisting, preventViewNotify){
36325         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36326             return;
36327         }
36328         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36329             if(!keepExisting || this.singleSelect){
36330                 this.clearSelections();
36331             }
36332             var r = this.grid.dataSource.getAt(index);
36333             this.selections.add(r);
36334             this.last = this.lastActive = index;
36335             if(!preventViewNotify){
36336                 this.grid.getView().onRowSelect(index);
36337             }
36338             this.fireEvent("rowselect", this, index, r);
36339             this.fireEvent("selectionchange", this);
36340         }
36341     },
36342
36343     /**
36344      * Deselects a row.
36345      * @param {Number} row The index of the row to deselect
36346      */
36347     deselectRow : function(index, preventViewNotify){
36348         if(this.locked) {
36349             return;
36350         }
36351         if(this.last == index){
36352             this.last = false;
36353         }
36354         if(this.lastActive == index){
36355             this.lastActive = false;
36356         }
36357         var r = this.grid.dataSource.getAt(index);
36358         this.selections.remove(r);
36359         if(!preventViewNotify){
36360             this.grid.getView().onRowDeselect(index);
36361         }
36362         this.fireEvent("rowdeselect", this, index);
36363         this.fireEvent("selectionchange", this);
36364     },
36365
36366     // private
36367     restoreLast : function(){
36368         if(this._last){
36369             this.last = this._last;
36370         }
36371     },
36372
36373     // private
36374     acceptsNav : function(row, col, cm){
36375         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36376     },
36377
36378     // private
36379     onEditorKey : function(field, e){
36380         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36381         if(k == e.TAB){
36382             e.stopEvent();
36383             ed.completeEdit();
36384             if(e.shiftKey){
36385                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36386             }else{
36387                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36388             }
36389         }else if(k == e.ENTER && !e.ctrlKey){
36390             e.stopEvent();
36391             ed.completeEdit();
36392             if(e.shiftKey){
36393                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36394             }else{
36395                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36396             }
36397         }else if(k == e.ESC){
36398             ed.cancelEdit();
36399         }
36400         if(newCell){
36401             g.startEditing(newCell[0], newCell[1]);
36402         }
36403     }
36404 });/*
36405  * Based on:
36406  * Ext JS Library 1.1.1
36407  * Copyright(c) 2006-2007, Ext JS, LLC.
36408  *
36409  * Originally Released Under LGPL - original licence link has changed is not relivant.
36410  *
36411  * Fork - LGPL
36412  * <script type="text/javascript">
36413  */
36414 /**
36415  * @class Roo.grid.CellSelectionModel
36416  * @extends Roo.grid.AbstractSelectionModel
36417  * This class provides the basic implementation for cell selection in a grid.
36418  * @constructor
36419  * @param {Object} config The object containing the configuration of this model.
36420  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36421  */
36422 Roo.grid.CellSelectionModel = function(config){
36423     Roo.apply(this, config);
36424
36425     this.selection = null;
36426
36427     this.addEvents({
36428         /**
36429              * @event beforerowselect
36430              * Fires before a cell is selected.
36431              * @param {SelectionModel} this
36432              * @param {Number} rowIndex The selected row index
36433              * @param {Number} colIndex The selected cell index
36434              */
36435             "beforecellselect" : true,
36436         /**
36437              * @event cellselect
36438              * Fires when a cell is selected.
36439              * @param {SelectionModel} this
36440              * @param {Number} rowIndex The selected row index
36441              * @param {Number} colIndex The selected cell index
36442              */
36443             "cellselect" : true,
36444         /**
36445              * @event selectionchange
36446              * Fires when the active selection changes.
36447              * @param {SelectionModel} this
36448              * @param {Object} selection null for no selection or an object (o) with two properties
36449                 <ul>
36450                 <li>o.record: the record object for the row the selection is in</li>
36451                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36452                 </ul>
36453              */
36454             "selectionchange" : true,
36455         /**
36456              * @event tabend
36457              * Fires when the tab (or enter) was pressed on the last editable cell
36458              * You can use this to trigger add new row.
36459              * @param {SelectionModel} this
36460              */
36461             "tabend" : true,
36462          /**
36463              * @event beforeeditnext
36464              * Fires before the next editable sell is made active
36465              * You can use this to skip to another cell or fire the tabend
36466              *    if you set cell to false
36467              * @param {Object} eventdata object : { cell : [ row, col ] } 
36468              */
36469             "beforeeditnext" : true
36470     });
36471     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36472 };
36473
36474 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36475     
36476     enter_is_tab: false,
36477
36478     /** @ignore */
36479     initEvents : function(){
36480         this.grid.on("mousedown", this.handleMouseDown, this);
36481         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36482         var view = this.grid.view;
36483         view.on("refresh", this.onViewChange, this);
36484         view.on("rowupdated", this.onRowUpdated, this);
36485         view.on("beforerowremoved", this.clearSelections, this);
36486         view.on("beforerowsinserted", this.clearSelections, this);
36487         if(this.grid.isEditor){
36488             this.grid.on("beforeedit", this.beforeEdit,  this);
36489         }
36490     },
36491
36492         //private
36493     beforeEdit : function(e){
36494         this.select(e.row, e.column, false, true, e.record);
36495     },
36496
36497         //private
36498     onRowUpdated : function(v, index, r){
36499         if(this.selection && this.selection.record == r){
36500             v.onCellSelect(index, this.selection.cell[1]);
36501         }
36502     },
36503
36504         //private
36505     onViewChange : function(){
36506         this.clearSelections(true);
36507     },
36508
36509         /**
36510          * Returns the currently selected cell,.
36511          * @return {Array} The selected cell (row, column) or null if none selected.
36512          */
36513     getSelectedCell : function(){
36514         return this.selection ? this.selection.cell : null;
36515     },
36516
36517     /**
36518      * Clears all selections.
36519      * @param {Boolean} true to prevent the gridview from being notified about the change.
36520      */
36521     clearSelections : function(preventNotify){
36522         var s = this.selection;
36523         if(s){
36524             if(preventNotify !== true){
36525                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36526             }
36527             this.selection = null;
36528             this.fireEvent("selectionchange", this, null);
36529         }
36530     },
36531
36532     /**
36533      * Returns true if there is a selection.
36534      * @return {Boolean}
36535      */
36536     hasSelection : function(){
36537         return this.selection ? true : false;
36538     },
36539
36540     /** @ignore */
36541     handleMouseDown : function(e, t){
36542         var v = this.grid.getView();
36543         if(this.isLocked()){
36544             return;
36545         };
36546         var row = v.findRowIndex(t);
36547         var cell = v.findCellIndex(t);
36548         if(row !== false && cell !== false){
36549             this.select(row, cell);
36550         }
36551     },
36552
36553     /**
36554      * Selects a cell.
36555      * @param {Number} rowIndex
36556      * @param {Number} collIndex
36557      */
36558     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36559         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36560             this.clearSelections();
36561             r = r || this.grid.dataSource.getAt(rowIndex);
36562             this.selection = {
36563                 record : r,
36564                 cell : [rowIndex, colIndex]
36565             };
36566             if(!preventViewNotify){
36567                 var v = this.grid.getView();
36568                 v.onCellSelect(rowIndex, colIndex);
36569                 if(preventFocus !== true){
36570                     v.focusCell(rowIndex, colIndex);
36571                 }
36572             }
36573             this.fireEvent("cellselect", this, rowIndex, colIndex);
36574             this.fireEvent("selectionchange", this, this.selection);
36575         }
36576     },
36577
36578         //private
36579     isSelectable : function(rowIndex, colIndex, cm){
36580         return !cm.isHidden(colIndex);
36581     },
36582
36583     /** @ignore */
36584     handleKeyDown : function(e){
36585         //Roo.log('Cell Sel Model handleKeyDown');
36586         if(!e.isNavKeyPress()){
36587             return;
36588         }
36589         var g = this.grid, s = this.selection;
36590         if(!s){
36591             e.stopEvent();
36592             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36593             if(cell){
36594                 this.select(cell[0], cell[1]);
36595             }
36596             return;
36597         }
36598         var sm = this;
36599         var walk = function(row, col, step){
36600             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36601         };
36602         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36603         var newCell;
36604
36605       
36606
36607         switch(k){
36608             case e.TAB:
36609                 // handled by onEditorKey
36610                 if (g.isEditor && g.editing) {
36611                     return;
36612                 }
36613                 if(e.shiftKey) {
36614                     newCell = walk(r, c-1, -1);
36615                 } else {
36616                     newCell = walk(r, c+1, 1);
36617                 }
36618                 break;
36619             
36620             case e.DOWN:
36621                newCell = walk(r+1, c, 1);
36622                 break;
36623             
36624             case e.UP:
36625                 newCell = walk(r-1, c, -1);
36626                 break;
36627             
36628             case e.RIGHT:
36629                 newCell = walk(r, c+1, 1);
36630                 break;
36631             
36632             case e.LEFT:
36633                 newCell = walk(r, c-1, -1);
36634                 break;
36635             
36636             case e.ENTER:
36637                 
36638                 if(g.isEditor && !g.editing){
36639                    g.startEditing(r, c);
36640                    e.stopEvent();
36641                    return;
36642                 }
36643                 
36644                 
36645              break;
36646         };
36647         if(newCell){
36648             this.select(newCell[0], newCell[1]);
36649             e.stopEvent();
36650             
36651         }
36652     },
36653
36654     acceptsNav : function(row, col, cm){
36655         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36656     },
36657     /**
36658      * Selects a cell.
36659      * @param {Number} field (not used) - as it's normally used as a listener
36660      * @param {Number} e - event - fake it by using
36661      *
36662      * var e = Roo.EventObjectImpl.prototype;
36663      * e.keyCode = e.TAB
36664      *
36665      * 
36666      */
36667     onEditorKey : function(field, e){
36668         
36669         var k = e.getKey(),
36670             newCell,
36671             g = this.grid,
36672             ed = g.activeEditor,
36673             forward = false;
36674         ///Roo.log('onEditorKey' + k);
36675         
36676         
36677         if (this.enter_is_tab && k == e.ENTER) {
36678             k = e.TAB;
36679         }
36680         
36681         if(k == e.TAB){
36682             if(e.shiftKey){
36683                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36684             }else{
36685                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36686                 forward = true;
36687             }
36688             
36689             e.stopEvent();
36690             
36691         } else if(k == e.ENTER &&  !e.ctrlKey){
36692             ed.completeEdit();
36693             e.stopEvent();
36694             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36695         
36696                 } else if(k == e.ESC){
36697             ed.cancelEdit();
36698         }
36699                 
36700         if (newCell) {
36701             var ecall = { cell : newCell, forward : forward };
36702             this.fireEvent('beforeeditnext', ecall );
36703             newCell = ecall.cell;
36704                         forward = ecall.forward;
36705         }
36706                 
36707         if(newCell){
36708             //Roo.log('next cell after edit');
36709             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36710         } else if (forward) {
36711             // tabbed past last
36712             this.fireEvent.defer(100, this, ['tabend',this]);
36713         }
36714     }
36715 });/*
36716  * Based on:
36717  * Ext JS Library 1.1.1
36718  * Copyright(c) 2006-2007, Ext JS, LLC.
36719  *
36720  * Originally Released Under LGPL - original licence link has changed is not relivant.
36721  *
36722  * Fork - LGPL
36723  * <script type="text/javascript">
36724  */
36725  
36726 /**
36727  * @class Roo.grid.EditorGrid
36728  * @extends Roo.grid.Grid
36729  * Class for creating and editable grid.
36730  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36731  * The container MUST have some type of size defined for the grid to fill. The container will be 
36732  * automatically set to position relative if it isn't already.
36733  * @param {Object} dataSource The data model to bind to
36734  * @param {Object} colModel The column model with info about this grid's columns
36735  */
36736 Roo.grid.EditorGrid = function(container, config){
36737     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36738     this.getGridEl().addClass("xedit-grid");
36739
36740     if(!this.selModel){
36741         this.selModel = new Roo.grid.CellSelectionModel();
36742     }
36743
36744     this.activeEditor = null;
36745
36746         this.addEvents({
36747             /**
36748              * @event beforeedit
36749              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36750              * <ul style="padding:5px;padding-left:16px;">
36751              * <li>grid - This grid</li>
36752              * <li>record - The record being edited</li>
36753              * <li>field - The field name being edited</li>
36754              * <li>value - The value for the field being edited.</li>
36755              * <li>row - The grid row index</li>
36756              * <li>column - The grid column index</li>
36757              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36758              * </ul>
36759              * @param {Object} e An edit event (see above for description)
36760              */
36761             "beforeedit" : true,
36762             /**
36763              * @event afteredit
36764              * Fires after a cell is edited. <br />
36765              * <ul style="padding:5px;padding-left:16px;">
36766              * <li>grid - This grid</li>
36767              * <li>record - The record being edited</li>
36768              * <li>field - The field name being edited</li>
36769              * <li>value - The value being set</li>
36770              * <li>originalValue - The original value for the field, before the edit.</li>
36771              * <li>row - The grid row index</li>
36772              * <li>column - The grid column index</li>
36773              * </ul>
36774              * @param {Object} e An edit event (see above for description)
36775              */
36776             "afteredit" : true,
36777             /**
36778              * @event validateedit
36779              * Fires after a cell is edited, but before the value is set in the record. 
36780          * You can use this to modify the value being set in the field, Return false
36781              * to cancel the change. The edit event object has the following properties <br />
36782              * <ul style="padding:5px;padding-left:16px;">
36783          * <li>editor - This editor</li>
36784              * <li>grid - This grid</li>
36785              * <li>record - The record being edited</li>
36786              * <li>field - The field name being edited</li>
36787              * <li>value - The value being set</li>
36788              * <li>originalValue - The original value for the field, before the edit.</li>
36789              * <li>row - The grid row index</li>
36790              * <li>column - The grid column index</li>
36791              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36792              * </ul>
36793              * @param {Object} e An edit event (see above for description)
36794              */
36795             "validateedit" : true
36796         });
36797     this.on("bodyscroll", this.stopEditing,  this);
36798     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36799 };
36800
36801 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36802     /**
36803      * @cfg {Number} clicksToEdit
36804      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36805      */
36806     clicksToEdit: 2,
36807
36808     // private
36809     isEditor : true,
36810     // private
36811     trackMouseOver: false, // causes very odd FF errors
36812
36813     onCellDblClick : function(g, row, col){
36814         this.startEditing(row, col);
36815     },
36816
36817     onEditComplete : function(ed, value, startValue){
36818         this.editing = false;
36819         this.activeEditor = null;
36820         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36821         var r = ed.record;
36822         var field = this.colModel.getDataIndex(ed.col);
36823         var e = {
36824             grid: this,
36825             record: r,
36826             field: field,
36827             originalValue: startValue,
36828             value: value,
36829             row: ed.row,
36830             column: ed.col,
36831             cancel:false,
36832             editor: ed
36833         };
36834         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36835         cell.show();
36836           
36837         if(String(value) !== String(startValue)){
36838             
36839             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36840                 r.set(field, e.value);
36841                 // if we are dealing with a combo box..
36842                 // then we also set the 'name' colum to be the displayField
36843                 if (ed.field.displayField && ed.field.name) {
36844                     r.set(ed.field.name, ed.field.el.dom.value);
36845                 }
36846                 
36847                 delete e.cancel; //?? why!!!
36848                 this.fireEvent("afteredit", e);
36849             }
36850         } else {
36851             this.fireEvent("afteredit", e); // always fire it!
36852         }
36853         this.view.focusCell(ed.row, ed.col);
36854     },
36855
36856     /**
36857      * Starts editing the specified for the specified row/column
36858      * @param {Number} rowIndex
36859      * @param {Number} colIndex
36860      */
36861     startEditing : function(row, col){
36862         this.stopEditing();
36863         if(this.colModel.isCellEditable(col, row)){
36864             this.view.ensureVisible(row, col, true);
36865           
36866             var r = this.dataSource.getAt(row);
36867             var field = this.colModel.getDataIndex(col);
36868             var cell = Roo.get(this.view.getCell(row,col));
36869             var e = {
36870                 grid: this,
36871                 record: r,
36872                 field: field,
36873                 value: r.data[field],
36874                 row: row,
36875                 column: col,
36876                 cancel:false 
36877             };
36878             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36879                 this.editing = true;
36880                 var ed = this.colModel.getCellEditor(col, row);
36881                 
36882                 if (!ed) {
36883                     return;
36884                 }
36885                 if(!ed.rendered){
36886                     ed.render(ed.parentEl || document.body);
36887                 }
36888                 ed.field.reset();
36889                
36890                 cell.hide();
36891                 
36892                 (function(){ // complex but required for focus issues in safari, ie and opera
36893                     ed.row = row;
36894                     ed.col = col;
36895                     ed.record = r;
36896                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36897                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36898                     this.activeEditor = ed;
36899                     var v = r.data[field];
36900                     ed.startEdit(this.view.getCell(row, col), v);
36901                     // combo's with 'displayField and name set
36902                     if (ed.field.displayField && ed.field.name) {
36903                         ed.field.el.dom.value = r.data[ed.field.name];
36904                     }
36905                     
36906                     
36907                 }).defer(50, this);
36908             }
36909         }
36910     },
36911         
36912     /**
36913      * Stops any active editing
36914      */
36915     stopEditing : function(){
36916         if(this.activeEditor){
36917             this.activeEditor.completeEdit();
36918         }
36919         this.activeEditor = null;
36920     },
36921         
36922          /**
36923      * Called to get grid's drag proxy text, by default returns this.ddText.
36924      * @return {String}
36925      */
36926     getDragDropText : function(){
36927         var count = this.selModel.getSelectedCell() ? 1 : 0;
36928         return String.format(this.ddText, count, count == 1 ? '' : 's');
36929     }
36930         
36931 });/*
36932  * Based on:
36933  * Ext JS Library 1.1.1
36934  * Copyright(c) 2006-2007, Ext JS, LLC.
36935  *
36936  * Originally Released Under LGPL - original licence link has changed is not relivant.
36937  *
36938  * Fork - LGPL
36939  * <script type="text/javascript">
36940  */
36941
36942 // private - not really -- you end up using it !
36943 // This is a support class used internally by the Grid components
36944
36945 /**
36946  * @class Roo.grid.GridEditor
36947  * @extends Roo.Editor
36948  * Class for creating and editable grid elements.
36949  * @param {Object} config any settings (must include field)
36950  */
36951 Roo.grid.GridEditor = function(field, config){
36952     if (!config && field.field) {
36953         config = field;
36954         field = Roo.factory(config.field, Roo.form);
36955     }
36956     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36957     field.monitorTab = false;
36958 };
36959
36960 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36961     
36962     /**
36963      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36964      */
36965     
36966     alignment: "tl-tl",
36967     autoSize: "width",
36968     hideEl : false,
36969     cls: "x-small-editor x-grid-editor",
36970     shim:false,
36971     shadow:"frame"
36972 });/*
36973  * Based on:
36974  * Ext JS Library 1.1.1
36975  * Copyright(c) 2006-2007, Ext JS, LLC.
36976  *
36977  * Originally Released Under LGPL - original licence link has changed is not relivant.
36978  *
36979  * Fork - LGPL
36980  * <script type="text/javascript">
36981  */
36982   
36983
36984   
36985 Roo.grid.PropertyRecord = Roo.data.Record.create([
36986     {name:'name',type:'string'},  'value'
36987 ]);
36988
36989
36990 Roo.grid.PropertyStore = function(grid, source){
36991     this.grid = grid;
36992     this.store = new Roo.data.Store({
36993         recordType : Roo.grid.PropertyRecord
36994     });
36995     this.store.on('update', this.onUpdate,  this);
36996     if(source){
36997         this.setSource(source);
36998     }
36999     Roo.grid.PropertyStore.superclass.constructor.call(this);
37000 };
37001
37002
37003
37004 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37005     setSource : function(o){
37006         this.source = o;
37007         this.store.removeAll();
37008         var data = [];
37009         for(var k in o){
37010             if(this.isEditableValue(o[k])){
37011                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37012             }
37013         }
37014         this.store.loadRecords({records: data}, {}, true);
37015     },
37016
37017     onUpdate : function(ds, record, type){
37018         if(type == Roo.data.Record.EDIT){
37019             var v = record.data['value'];
37020             var oldValue = record.modified['value'];
37021             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37022                 this.source[record.id] = v;
37023                 record.commit();
37024                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37025             }else{
37026                 record.reject();
37027             }
37028         }
37029     },
37030
37031     getProperty : function(row){
37032        return this.store.getAt(row);
37033     },
37034
37035     isEditableValue: function(val){
37036         if(val && val instanceof Date){
37037             return true;
37038         }else if(typeof val == 'object' || typeof val == 'function'){
37039             return false;
37040         }
37041         return true;
37042     },
37043
37044     setValue : function(prop, value){
37045         this.source[prop] = value;
37046         this.store.getById(prop).set('value', value);
37047     },
37048
37049     getSource : function(){
37050         return this.source;
37051     }
37052 });
37053
37054 Roo.grid.PropertyColumnModel = function(grid, store){
37055     this.grid = grid;
37056     var g = Roo.grid;
37057     g.PropertyColumnModel.superclass.constructor.call(this, [
37058         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37059         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37060     ]);
37061     this.store = store;
37062     this.bselect = Roo.DomHelper.append(document.body, {
37063         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37064             {tag: 'option', value: 'true', html: 'true'},
37065             {tag: 'option', value: 'false', html: 'false'}
37066         ]
37067     });
37068     Roo.id(this.bselect);
37069     var f = Roo.form;
37070     this.editors = {
37071         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37072         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37073         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37074         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37075         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37076     };
37077     this.renderCellDelegate = this.renderCell.createDelegate(this);
37078     this.renderPropDelegate = this.renderProp.createDelegate(this);
37079 };
37080
37081 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37082     
37083     
37084     nameText : 'Name',
37085     valueText : 'Value',
37086     
37087     dateFormat : 'm/j/Y',
37088     
37089     
37090     renderDate : function(dateVal){
37091         return dateVal.dateFormat(this.dateFormat);
37092     },
37093
37094     renderBool : function(bVal){
37095         return bVal ? 'true' : 'false';
37096     },
37097
37098     isCellEditable : function(colIndex, rowIndex){
37099         return colIndex == 1;
37100     },
37101
37102     getRenderer : function(col){
37103         return col == 1 ?
37104             this.renderCellDelegate : this.renderPropDelegate;
37105     },
37106
37107     renderProp : function(v){
37108         return this.getPropertyName(v);
37109     },
37110
37111     renderCell : function(val){
37112         var rv = val;
37113         if(val instanceof Date){
37114             rv = this.renderDate(val);
37115         }else if(typeof val == 'boolean'){
37116             rv = this.renderBool(val);
37117         }
37118         return Roo.util.Format.htmlEncode(rv);
37119     },
37120
37121     getPropertyName : function(name){
37122         var pn = this.grid.propertyNames;
37123         return pn && pn[name] ? pn[name] : name;
37124     },
37125
37126     getCellEditor : function(colIndex, rowIndex){
37127         var p = this.store.getProperty(rowIndex);
37128         var n = p.data['name'], val = p.data['value'];
37129         
37130         if(typeof(this.grid.customEditors[n]) == 'string'){
37131             return this.editors[this.grid.customEditors[n]];
37132         }
37133         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37134             return this.grid.customEditors[n];
37135         }
37136         if(val instanceof Date){
37137             return this.editors['date'];
37138         }else if(typeof val == 'number'){
37139             return this.editors['number'];
37140         }else if(typeof val == 'boolean'){
37141             return this.editors['boolean'];
37142         }else{
37143             return this.editors['string'];
37144         }
37145     }
37146 });
37147
37148 /**
37149  * @class Roo.grid.PropertyGrid
37150  * @extends Roo.grid.EditorGrid
37151  * This class represents the  interface of a component based property grid control.
37152  * <br><br>Usage:<pre><code>
37153  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37154       
37155  });
37156  // set any options
37157  grid.render();
37158  * </code></pre>
37159   
37160  * @constructor
37161  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37162  * The container MUST have some type of size defined for the grid to fill. The container will be
37163  * automatically set to position relative if it isn't already.
37164  * @param {Object} config A config object that sets properties on this grid.
37165  */
37166 Roo.grid.PropertyGrid = function(container, config){
37167     config = config || {};
37168     var store = new Roo.grid.PropertyStore(this);
37169     this.store = store;
37170     var cm = new Roo.grid.PropertyColumnModel(this, store);
37171     store.store.sort('name', 'ASC');
37172     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37173         ds: store.store,
37174         cm: cm,
37175         enableColLock:false,
37176         enableColumnMove:false,
37177         stripeRows:false,
37178         trackMouseOver: false,
37179         clicksToEdit:1
37180     }, config));
37181     this.getGridEl().addClass('x-props-grid');
37182     this.lastEditRow = null;
37183     this.on('columnresize', this.onColumnResize, this);
37184     this.addEvents({
37185          /**
37186              * @event beforepropertychange
37187              * Fires before a property changes (return false to stop?)
37188              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37189              * @param {String} id Record Id
37190              * @param {String} newval New Value
37191          * @param {String} oldval Old Value
37192              */
37193         "beforepropertychange": true,
37194         /**
37195              * @event propertychange
37196              * Fires after a property changes
37197              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37198              * @param {String} id Record Id
37199              * @param {String} newval New Value
37200          * @param {String} oldval Old Value
37201              */
37202         "propertychange": true
37203     });
37204     this.customEditors = this.customEditors || {};
37205 };
37206 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37207     
37208      /**
37209      * @cfg {Object} customEditors map of colnames=> custom editors.
37210      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37211      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37212      * false disables editing of the field.
37213          */
37214     
37215       /**
37216      * @cfg {Object} propertyNames map of property Names to their displayed value
37217          */
37218     
37219     render : function(){
37220         Roo.grid.PropertyGrid.superclass.render.call(this);
37221         this.autoSize.defer(100, this);
37222     },
37223
37224     autoSize : function(){
37225         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37226         if(this.view){
37227             this.view.fitColumns();
37228         }
37229     },
37230
37231     onColumnResize : function(){
37232         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37233         this.autoSize();
37234     },
37235     /**
37236      * Sets the data for the Grid
37237      * accepts a Key => Value object of all the elements avaiable.
37238      * @param {Object} data  to appear in grid.
37239      */
37240     setSource : function(source){
37241         this.store.setSource(source);
37242         //this.autoSize();
37243     },
37244     /**
37245      * Gets all the data from the grid.
37246      * @return {Object} data  data stored in grid
37247      */
37248     getSource : function(){
37249         return this.store.getSource();
37250     }
37251 });/*
37252   
37253  * Licence LGPL
37254  
37255  */
37256  
37257 /**
37258  * @class Roo.grid.Calendar
37259  * @extends Roo.util.Grid
37260  * This class extends the Grid to provide a calendar widget
37261  * <br><br>Usage:<pre><code>
37262  var grid = new Roo.grid.Calendar("my-container-id", {
37263      ds: myDataStore,
37264      cm: myColModel,
37265      selModel: mySelectionModel,
37266      autoSizeColumns: true,
37267      monitorWindowResize: false,
37268      trackMouseOver: true
37269      eventstore : real data store..
37270  });
37271  // set any options
37272  grid.render();
37273   
37274   * @constructor
37275  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37276  * The container MUST have some type of size defined for the grid to fill. The container will be
37277  * automatically set to position relative if it isn't already.
37278  * @param {Object} config A config object that sets properties on this grid.
37279  */
37280 Roo.grid.Calendar = function(container, config){
37281         // initialize the container
37282         this.container = Roo.get(container);
37283         this.container.update("");
37284         this.container.setStyle("overflow", "hidden");
37285     this.container.addClass('x-grid-container');
37286
37287     this.id = this.container.id;
37288
37289     Roo.apply(this, config);
37290     // check and correct shorthanded configs
37291     
37292     var rows = [];
37293     var d =1;
37294     for (var r = 0;r < 6;r++) {
37295         
37296         rows[r]=[];
37297         for (var c =0;c < 7;c++) {
37298             rows[r][c]= '';
37299         }
37300     }
37301     if (this.eventStore) {
37302         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37303         this.eventStore.on('load',this.onLoad, this);
37304         this.eventStore.on('beforeload',this.clearEvents, this);
37305          
37306     }
37307     
37308     this.dataSource = new Roo.data.Store({
37309             proxy: new Roo.data.MemoryProxy(rows),
37310             reader: new Roo.data.ArrayReader({}, [
37311                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37312     });
37313
37314     this.dataSource.load();
37315     this.ds = this.dataSource;
37316     this.ds.xmodule = this.xmodule || false;
37317     
37318     
37319     var cellRender = function(v,x,r)
37320     {
37321         return String.format(
37322             '<div class="fc-day  fc-widget-content"><div>' +
37323                 '<div class="fc-event-container"></div>' +
37324                 '<div class="fc-day-number">{0}</div>'+
37325                 
37326                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37327             '</div></div>', v);
37328     
37329     }
37330     
37331     
37332     this.colModel = new Roo.grid.ColumnModel( [
37333         {
37334             xtype: 'ColumnModel',
37335             xns: Roo.grid,
37336             dataIndex : 'weekday0',
37337             header : 'Sunday',
37338             renderer : cellRender
37339         },
37340         {
37341             xtype: 'ColumnModel',
37342             xns: Roo.grid,
37343             dataIndex : 'weekday1',
37344             header : 'Monday',
37345             renderer : cellRender
37346         },
37347         {
37348             xtype: 'ColumnModel',
37349             xns: Roo.grid,
37350             dataIndex : 'weekday2',
37351             header : 'Tuesday',
37352             renderer : cellRender
37353         },
37354         {
37355             xtype: 'ColumnModel',
37356             xns: Roo.grid,
37357             dataIndex : 'weekday3',
37358             header : 'Wednesday',
37359             renderer : cellRender
37360         },
37361         {
37362             xtype: 'ColumnModel',
37363             xns: Roo.grid,
37364             dataIndex : 'weekday4',
37365             header : 'Thursday',
37366             renderer : cellRender
37367         },
37368         {
37369             xtype: 'ColumnModel',
37370             xns: Roo.grid,
37371             dataIndex : 'weekday5',
37372             header : 'Friday',
37373             renderer : cellRender
37374         },
37375         {
37376             xtype: 'ColumnModel',
37377             xns: Roo.grid,
37378             dataIndex : 'weekday6',
37379             header : 'Saturday',
37380             renderer : cellRender
37381         }
37382     ]);
37383     this.cm = this.colModel;
37384     this.cm.xmodule = this.xmodule || false;
37385  
37386         
37387           
37388     //this.selModel = new Roo.grid.CellSelectionModel();
37389     //this.sm = this.selModel;
37390     //this.selModel.init(this);
37391     
37392     
37393     if(this.width){
37394         this.container.setWidth(this.width);
37395     }
37396
37397     if(this.height){
37398         this.container.setHeight(this.height);
37399     }
37400     /** @private */
37401         this.addEvents({
37402         // raw events
37403         /**
37404          * @event click
37405          * The raw click event for the entire grid.
37406          * @param {Roo.EventObject} e
37407          */
37408         "click" : true,
37409         /**
37410          * @event dblclick
37411          * The raw dblclick event for the entire grid.
37412          * @param {Roo.EventObject} e
37413          */
37414         "dblclick" : true,
37415         /**
37416          * @event contextmenu
37417          * The raw contextmenu event for the entire grid.
37418          * @param {Roo.EventObject} e
37419          */
37420         "contextmenu" : true,
37421         /**
37422          * @event mousedown
37423          * The raw mousedown event for the entire grid.
37424          * @param {Roo.EventObject} e
37425          */
37426         "mousedown" : true,
37427         /**
37428          * @event mouseup
37429          * The raw mouseup event for the entire grid.
37430          * @param {Roo.EventObject} e
37431          */
37432         "mouseup" : true,
37433         /**
37434          * @event mouseover
37435          * The raw mouseover event for the entire grid.
37436          * @param {Roo.EventObject} e
37437          */
37438         "mouseover" : true,
37439         /**
37440          * @event mouseout
37441          * The raw mouseout event for the entire grid.
37442          * @param {Roo.EventObject} e
37443          */
37444         "mouseout" : true,
37445         /**
37446          * @event keypress
37447          * The raw keypress event for the entire grid.
37448          * @param {Roo.EventObject} e
37449          */
37450         "keypress" : true,
37451         /**
37452          * @event keydown
37453          * The raw keydown event for the entire grid.
37454          * @param {Roo.EventObject} e
37455          */
37456         "keydown" : true,
37457
37458         // custom events
37459
37460         /**
37461          * @event cellclick
37462          * Fires when a cell is clicked
37463          * @param {Grid} this
37464          * @param {Number} rowIndex
37465          * @param {Number} columnIndex
37466          * @param {Roo.EventObject} e
37467          */
37468         "cellclick" : true,
37469         /**
37470          * @event celldblclick
37471          * Fires when a cell is double clicked
37472          * @param {Grid} this
37473          * @param {Number} rowIndex
37474          * @param {Number} columnIndex
37475          * @param {Roo.EventObject} e
37476          */
37477         "celldblclick" : true,
37478         /**
37479          * @event rowclick
37480          * Fires when a row is clicked
37481          * @param {Grid} this
37482          * @param {Number} rowIndex
37483          * @param {Roo.EventObject} e
37484          */
37485         "rowclick" : true,
37486         /**
37487          * @event rowdblclick
37488          * Fires when a row is double clicked
37489          * @param {Grid} this
37490          * @param {Number} rowIndex
37491          * @param {Roo.EventObject} e
37492          */
37493         "rowdblclick" : true,
37494         /**
37495          * @event headerclick
37496          * Fires when a header is clicked
37497          * @param {Grid} this
37498          * @param {Number} columnIndex
37499          * @param {Roo.EventObject} e
37500          */
37501         "headerclick" : true,
37502         /**
37503          * @event headerdblclick
37504          * Fires when a header cell is double clicked
37505          * @param {Grid} this
37506          * @param {Number} columnIndex
37507          * @param {Roo.EventObject} e
37508          */
37509         "headerdblclick" : true,
37510         /**
37511          * @event rowcontextmenu
37512          * Fires when a row is right clicked
37513          * @param {Grid} this
37514          * @param {Number} rowIndex
37515          * @param {Roo.EventObject} e
37516          */
37517         "rowcontextmenu" : true,
37518         /**
37519          * @event cellcontextmenu
37520          * Fires when a cell is right clicked
37521          * @param {Grid} this
37522          * @param {Number} rowIndex
37523          * @param {Number} cellIndex
37524          * @param {Roo.EventObject} e
37525          */
37526          "cellcontextmenu" : true,
37527         /**
37528          * @event headercontextmenu
37529          * Fires when a header is right clicked
37530          * @param {Grid} this
37531          * @param {Number} columnIndex
37532          * @param {Roo.EventObject} e
37533          */
37534         "headercontextmenu" : true,
37535         /**
37536          * @event bodyscroll
37537          * Fires when the body element is scrolled
37538          * @param {Number} scrollLeft
37539          * @param {Number} scrollTop
37540          */
37541         "bodyscroll" : true,
37542         /**
37543          * @event columnresize
37544          * Fires when the user resizes a column
37545          * @param {Number} columnIndex
37546          * @param {Number} newSize
37547          */
37548         "columnresize" : true,
37549         /**
37550          * @event columnmove
37551          * Fires when the user moves a column
37552          * @param {Number} oldIndex
37553          * @param {Number} newIndex
37554          */
37555         "columnmove" : true,
37556         /**
37557          * @event startdrag
37558          * Fires when row(s) start being dragged
37559          * @param {Grid} this
37560          * @param {Roo.GridDD} dd The drag drop object
37561          * @param {event} e The raw browser event
37562          */
37563         "startdrag" : true,
37564         /**
37565          * @event enddrag
37566          * Fires when a drag operation is complete
37567          * @param {Grid} this
37568          * @param {Roo.GridDD} dd The drag drop object
37569          * @param {event} e The raw browser event
37570          */
37571         "enddrag" : true,
37572         /**
37573          * @event dragdrop
37574          * Fires when dragged row(s) are dropped on a valid DD target
37575          * @param {Grid} this
37576          * @param {Roo.GridDD} dd The drag drop object
37577          * @param {String} targetId The target drag drop object
37578          * @param {event} e The raw browser event
37579          */
37580         "dragdrop" : true,
37581         /**
37582          * @event dragover
37583          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37584          * @param {Grid} this
37585          * @param {Roo.GridDD} dd The drag drop object
37586          * @param {String} targetId The target drag drop object
37587          * @param {event} e The raw browser event
37588          */
37589         "dragover" : true,
37590         /**
37591          * @event dragenter
37592          *  Fires when the dragged row(s) first cross another DD target while being dragged
37593          * @param {Grid} this
37594          * @param {Roo.GridDD} dd The drag drop object
37595          * @param {String} targetId The target drag drop object
37596          * @param {event} e The raw browser event
37597          */
37598         "dragenter" : true,
37599         /**
37600          * @event dragout
37601          * Fires when the dragged row(s) leave another DD target while being dragged
37602          * @param {Grid} this
37603          * @param {Roo.GridDD} dd The drag drop object
37604          * @param {String} targetId The target drag drop object
37605          * @param {event} e The raw browser event
37606          */
37607         "dragout" : true,
37608         /**
37609          * @event rowclass
37610          * Fires when a row is rendered, so you can change add a style to it.
37611          * @param {GridView} gridview   The grid view
37612          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37613          */
37614         'rowclass' : true,
37615
37616         /**
37617          * @event render
37618          * Fires when the grid is rendered
37619          * @param {Grid} grid
37620          */
37621         'render' : true,
37622             /**
37623              * @event select
37624              * Fires when a date is selected
37625              * @param {DatePicker} this
37626              * @param {Date} date The selected date
37627              */
37628         'select': true,
37629         /**
37630              * @event monthchange
37631              * Fires when the displayed month changes 
37632              * @param {DatePicker} this
37633              * @param {Date} date The selected month
37634              */
37635         'monthchange': true,
37636         /**
37637              * @event evententer
37638              * Fires when mouse over an event
37639              * @param {Calendar} this
37640              * @param {event} Event
37641              */
37642         'evententer': true,
37643         /**
37644              * @event eventleave
37645              * Fires when the mouse leaves an
37646              * @param {Calendar} this
37647              * @param {event}
37648              */
37649         'eventleave': true,
37650         /**
37651              * @event eventclick
37652              * Fires when the mouse click an
37653              * @param {Calendar} this
37654              * @param {event}
37655              */
37656         'eventclick': true,
37657         /**
37658              * @event eventrender
37659              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37660              * @param {Calendar} this
37661              * @param {data} data to be modified
37662              */
37663         'eventrender': true
37664         
37665     });
37666
37667     Roo.grid.Grid.superclass.constructor.call(this);
37668     this.on('render', function() {
37669         this.view.el.addClass('x-grid-cal'); 
37670         
37671         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37672
37673     },this);
37674     
37675     if (!Roo.grid.Calendar.style) {
37676         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37677             
37678             
37679             '.x-grid-cal .x-grid-col' :  {
37680                 height: 'auto !important',
37681                 'vertical-align': 'top'
37682             },
37683             '.x-grid-cal  .fc-event-hori' : {
37684                 height: '14px'
37685             }
37686              
37687             
37688         }, Roo.id());
37689     }
37690
37691     
37692     
37693 };
37694 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37695     /**
37696      * @cfg {Store} eventStore The store that loads events.
37697      */
37698     eventStore : 25,
37699
37700      
37701     activeDate : false,
37702     startDay : 0,
37703     autoWidth : true,
37704     monitorWindowResize : false,
37705
37706     
37707     resizeColumns : function() {
37708         var col = (this.view.el.getWidth() / 7) - 3;
37709         // loop through cols, and setWidth
37710         for(var i =0 ; i < 7 ; i++){
37711             this.cm.setColumnWidth(i, col);
37712         }
37713     },
37714      setDate :function(date) {
37715         
37716         Roo.log('setDate?');
37717         
37718         this.resizeColumns();
37719         var vd = this.activeDate;
37720         this.activeDate = date;
37721 //        if(vd && this.el){
37722 //            var t = date.getTime();
37723 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37724 //                Roo.log('using add remove');
37725 //                
37726 //                this.fireEvent('monthchange', this, date);
37727 //                
37728 //                this.cells.removeClass("fc-state-highlight");
37729 //                this.cells.each(function(c){
37730 //                   if(c.dateValue == t){
37731 //                       c.addClass("fc-state-highlight");
37732 //                       setTimeout(function(){
37733 //                            try{c.dom.firstChild.focus();}catch(e){}
37734 //                       }, 50);
37735 //                       return false;
37736 //                   }
37737 //                   return true;
37738 //                });
37739 //                return;
37740 //            }
37741 //        }
37742         
37743         var days = date.getDaysInMonth();
37744         
37745         var firstOfMonth = date.getFirstDateOfMonth();
37746         var startingPos = firstOfMonth.getDay()-this.startDay;
37747         
37748         if(startingPos < this.startDay){
37749             startingPos += 7;
37750         }
37751         
37752         var pm = date.add(Date.MONTH, -1);
37753         var prevStart = pm.getDaysInMonth()-startingPos;
37754 //        
37755         
37756         
37757         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37758         
37759         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37760         //this.cells.addClassOnOver('fc-state-hover');
37761         
37762         var cells = this.cells.elements;
37763         var textEls = this.textNodes;
37764         
37765         //Roo.each(cells, function(cell){
37766         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37767         //});
37768         
37769         days += startingPos;
37770
37771         // convert everything to numbers so it's fast
37772         var day = 86400000;
37773         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37774         //Roo.log(d);
37775         //Roo.log(pm);
37776         //Roo.log(prevStart);
37777         
37778         var today = new Date().clearTime().getTime();
37779         var sel = date.clearTime().getTime();
37780         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37781         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37782         var ddMatch = this.disabledDatesRE;
37783         var ddText = this.disabledDatesText;
37784         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37785         var ddaysText = this.disabledDaysText;
37786         var format = this.format;
37787         
37788         var setCellClass = function(cal, cell){
37789             
37790             //Roo.log('set Cell Class');
37791             cell.title = "";
37792             var t = d.getTime();
37793             
37794             //Roo.log(d);
37795             
37796             
37797             cell.dateValue = t;
37798             if(t == today){
37799                 cell.className += " fc-today";
37800                 cell.className += " fc-state-highlight";
37801                 cell.title = cal.todayText;
37802             }
37803             if(t == sel){
37804                 // disable highlight in other month..
37805                 cell.className += " fc-state-highlight";
37806                 
37807             }
37808             // disabling
37809             if(t < min) {
37810                 //cell.className = " fc-state-disabled";
37811                 cell.title = cal.minText;
37812                 return;
37813             }
37814             if(t > max) {
37815                 //cell.className = " fc-state-disabled";
37816                 cell.title = cal.maxText;
37817                 return;
37818             }
37819             if(ddays){
37820                 if(ddays.indexOf(d.getDay()) != -1){
37821                     // cell.title = ddaysText;
37822                    // cell.className = " fc-state-disabled";
37823                 }
37824             }
37825             if(ddMatch && format){
37826                 var fvalue = d.dateFormat(format);
37827                 if(ddMatch.test(fvalue)){
37828                     cell.title = ddText.replace("%0", fvalue);
37829                    cell.className = " fc-state-disabled";
37830                 }
37831             }
37832             
37833             if (!cell.initialClassName) {
37834                 cell.initialClassName = cell.dom.className;
37835             }
37836             
37837             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37838         };
37839
37840         var i = 0;
37841         
37842         for(; i < startingPos; i++) {
37843             cells[i].dayName =  (++prevStart);
37844             Roo.log(textEls[i]);
37845             d.setDate(d.getDate()+1);
37846             
37847             //cells[i].className = "fc-past fc-other-month";
37848             setCellClass(this, cells[i]);
37849         }
37850         
37851         var intDay = 0;
37852         
37853         for(; i < days; i++){
37854             intDay = i - startingPos + 1;
37855             cells[i].dayName =  (intDay);
37856             d.setDate(d.getDate()+1);
37857             
37858             cells[i].className = ''; // "x-date-active";
37859             setCellClass(this, cells[i]);
37860         }
37861         var extraDays = 0;
37862         
37863         for(; i < 42; i++) {
37864             //textEls[i].innerHTML = (++extraDays);
37865             
37866             d.setDate(d.getDate()+1);
37867             cells[i].dayName = (++extraDays);
37868             cells[i].className = "fc-future fc-other-month";
37869             setCellClass(this, cells[i]);
37870         }
37871         
37872         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37873         
37874         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37875         
37876         // this will cause all the cells to mis
37877         var rows= [];
37878         var i =0;
37879         for (var r = 0;r < 6;r++) {
37880             for (var c =0;c < 7;c++) {
37881                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37882             }    
37883         }
37884         
37885         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37886         for(i=0;i<cells.length;i++) {
37887             
37888             this.cells.elements[i].dayName = cells[i].dayName ;
37889             this.cells.elements[i].className = cells[i].className;
37890             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37891             this.cells.elements[i].title = cells[i].title ;
37892             this.cells.elements[i].dateValue = cells[i].dateValue ;
37893         }
37894         
37895         
37896         
37897         
37898         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37899         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37900         
37901         ////if(totalRows != 6){
37902             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37903            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37904        // }
37905         
37906         this.fireEvent('monthchange', this, date);
37907         
37908         
37909     },
37910  /**
37911      * Returns the grid's SelectionModel.
37912      * @return {SelectionModel}
37913      */
37914     getSelectionModel : function(){
37915         if(!this.selModel){
37916             this.selModel = new Roo.grid.CellSelectionModel();
37917         }
37918         return this.selModel;
37919     },
37920
37921     load: function() {
37922         this.eventStore.load()
37923         
37924         
37925         
37926     },
37927     
37928     findCell : function(dt) {
37929         dt = dt.clearTime().getTime();
37930         var ret = false;
37931         this.cells.each(function(c){
37932             //Roo.log("check " +c.dateValue + '?=' + dt);
37933             if(c.dateValue == dt){
37934                 ret = c;
37935                 return false;
37936             }
37937             return true;
37938         });
37939         
37940         return ret;
37941     },
37942     
37943     findCells : function(rec) {
37944         var s = rec.data.start_dt.clone().clearTime().getTime();
37945        // Roo.log(s);
37946         var e= rec.data.end_dt.clone().clearTime().getTime();
37947        // Roo.log(e);
37948         var ret = [];
37949         this.cells.each(function(c){
37950              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37951             
37952             if(c.dateValue > e){
37953                 return ;
37954             }
37955             if(c.dateValue < s){
37956                 return ;
37957             }
37958             ret.push(c);
37959         });
37960         
37961         return ret;    
37962     },
37963     
37964     findBestRow: function(cells)
37965     {
37966         var ret = 0;
37967         
37968         for (var i =0 ; i < cells.length;i++) {
37969             ret  = Math.max(cells[i].rows || 0,ret);
37970         }
37971         return ret;
37972         
37973     },
37974     
37975     
37976     addItem : function(rec)
37977     {
37978         // look for vertical location slot in
37979         var cells = this.findCells(rec);
37980         
37981         rec.row = this.findBestRow(cells);
37982         
37983         // work out the location.
37984         
37985         var crow = false;
37986         var rows = [];
37987         for(var i =0; i < cells.length; i++) {
37988             if (!crow) {
37989                 crow = {
37990                     start : cells[i],
37991                     end :  cells[i]
37992                 };
37993                 continue;
37994             }
37995             if (crow.start.getY() == cells[i].getY()) {
37996                 // on same row.
37997                 crow.end = cells[i];
37998                 continue;
37999             }
38000             // different row.
38001             rows.push(crow);
38002             crow = {
38003                 start: cells[i],
38004                 end : cells[i]
38005             };
38006             
38007         }
38008         
38009         rows.push(crow);
38010         rec.els = [];
38011         rec.rows = rows;
38012         rec.cells = cells;
38013         for (var i = 0; i < cells.length;i++) {
38014             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
38015             
38016         }
38017         
38018         
38019     },
38020     
38021     clearEvents: function() {
38022         
38023         if (!this.eventStore.getCount()) {
38024             return;
38025         }
38026         // reset number of rows in cells.
38027         Roo.each(this.cells.elements, function(c){
38028             c.rows = 0;
38029         });
38030         
38031         this.eventStore.each(function(e) {
38032             this.clearEvent(e);
38033         },this);
38034         
38035     },
38036     
38037     clearEvent : function(ev)
38038     {
38039         if (ev.els) {
38040             Roo.each(ev.els, function(el) {
38041                 el.un('mouseenter' ,this.onEventEnter, this);
38042                 el.un('mouseleave' ,this.onEventLeave, this);
38043                 el.remove();
38044             },this);
38045             ev.els = [];
38046         }
38047     },
38048     
38049     
38050     renderEvent : function(ev,ctr) {
38051         if (!ctr) {
38052              ctr = this.view.el.select('.fc-event-container',true).first();
38053         }
38054         
38055          
38056         this.clearEvent(ev);
38057             //code
38058        
38059         
38060         
38061         ev.els = [];
38062         var cells = ev.cells;
38063         var rows = ev.rows;
38064         this.fireEvent('eventrender', this, ev);
38065         
38066         for(var i =0; i < rows.length; i++) {
38067             
38068             cls = '';
38069             if (i == 0) {
38070                 cls += ' fc-event-start';
38071             }
38072             if ((i+1) == rows.length) {
38073                 cls += ' fc-event-end';
38074             }
38075             
38076             //Roo.log(ev.data);
38077             // how many rows should it span..
38078             var cg = this.eventTmpl.append(ctr,Roo.apply({
38079                 fccls : cls
38080                 
38081             }, ev.data) , true);
38082             
38083             
38084             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38085             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38086             cg.on('click', this.onEventClick, this, ev);
38087             
38088             ev.els.push(cg);
38089             
38090             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38091             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38092             //Roo.log(cg);
38093              
38094             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38095             cg.setWidth(ebox.right - sbox.x -2);
38096         }
38097     },
38098     
38099     renderEvents: function()
38100     {   
38101         // first make sure there is enough space..
38102         
38103         if (!this.eventTmpl) {
38104             this.eventTmpl = new Roo.Template(
38105                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38106                     '<div class="fc-event-inner">' +
38107                         '<span class="fc-event-time">{time}</span>' +
38108                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38109                     '</div>' +
38110                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38111                 '</div>'
38112             );
38113                 
38114         }
38115                
38116         
38117         
38118         this.cells.each(function(c) {
38119             //Roo.log(c.select('.fc-day-content div',true).first());
38120             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38121         });
38122         
38123         var ctr = this.view.el.select('.fc-event-container',true).first();
38124         
38125         var cls;
38126         this.eventStore.each(function(ev){
38127             
38128             this.renderEvent(ev);
38129              
38130              
38131         }, this);
38132         this.view.layout();
38133         
38134     },
38135     
38136     onEventEnter: function (e, el,event,d) {
38137         this.fireEvent('evententer', this, el, event);
38138     },
38139     
38140     onEventLeave: function (e, el,event,d) {
38141         this.fireEvent('eventleave', this, el, event);
38142     },
38143     
38144     onEventClick: function (e, el,event,d) {
38145         this.fireEvent('eventclick', this, el, event);
38146     },
38147     
38148     onMonthChange: function () {
38149         this.store.load();
38150     },
38151     
38152     onLoad: function () {
38153         
38154         //Roo.log('calendar onload');
38155 //         
38156         if(this.eventStore.getCount() > 0){
38157             
38158            
38159             
38160             this.eventStore.each(function(d){
38161                 
38162                 
38163                 // FIXME..
38164                 var add =   d.data;
38165                 if (typeof(add.end_dt) == 'undefined')  {
38166                     Roo.log("Missing End time in calendar data: ");
38167                     Roo.log(d);
38168                     return;
38169                 }
38170                 if (typeof(add.start_dt) == 'undefined')  {
38171                     Roo.log("Missing Start time in calendar data: ");
38172                     Roo.log(d);
38173                     return;
38174                 }
38175                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38176                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38177                 add.id = add.id || d.id;
38178                 add.title = add.title || '??';
38179                 
38180                 this.addItem(d);
38181                 
38182              
38183             },this);
38184         }
38185         
38186         this.renderEvents();
38187     }
38188     
38189
38190 });
38191 /*
38192  grid : {
38193                 xtype: 'Grid',
38194                 xns: Roo.grid,
38195                 listeners : {
38196                     render : function ()
38197                     {
38198                         _this.grid = this;
38199                         
38200                         if (!this.view.el.hasClass('course-timesheet')) {
38201                             this.view.el.addClass('course-timesheet');
38202                         }
38203                         if (this.tsStyle) {
38204                             this.ds.load({});
38205                             return; 
38206                         }
38207                         Roo.log('width');
38208                         Roo.log(_this.grid.view.el.getWidth());
38209                         
38210                         
38211                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38212                             '.course-timesheet .x-grid-row' : {
38213                                 height: '80px'
38214                             },
38215                             '.x-grid-row td' : {
38216                                 'vertical-align' : 0
38217                             },
38218                             '.course-edit-link' : {
38219                                 'color' : 'blue',
38220                                 'text-overflow' : 'ellipsis',
38221                                 'overflow' : 'hidden',
38222                                 'white-space' : 'nowrap',
38223                                 'cursor' : 'pointer'
38224                             },
38225                             '.sub-link' : {
38226                                 'color' : 'green'
38227                             },
38228                             '.de-act-sup-link' : {
38229                                 'color' : 'purple',
38230                                 'text-decoration' : 'line-through'
38231                             },
38232                             '.de-act-link' : {
38233                                 'color' : 'red',
38234                                 'text-decoration' : 'line-through'
38235                             },
38236                             '.course-timesheet .course-highlight' : {
38237                                 'border-top-style': 'dashed !important',
38238                                 'border-bottom-bottom': 'dashed !important'
38239                             },
38240                             '.course-timesheet .course-item' : {
38241                                 'font-family'   : 'tahoma, arial, helvetica',
38242                                 'font-size'     : '11px',
38243                                 'overflow'      : 'hidden',
38244                                 'padding-left'  : '10px',
38245                                 'padding-right' : '10px',
38246                                 'padding-top' : '10px' 
38247                             }
38248                             
38249                         }, Roo.id());
38250                                 this.ds.load({});
38251                     }
38252                 },
38253                 autoWidth : true,
38254                 monitorWindowResize : false,
38255                 cellrenderer : function(v,x,r)
38256                 {
38257                     return v;
38258                 },
38259                 sm : {
38260                     xtype: 'CellSelectionModel',
38261                     xns: Roo.grid
38262                 },
38263                 dataSource : {
38264                     xtype: 'Store',
38265                     xns: Roo.data,
38266                     listeners : {
38267                         beforeload : function (_self, options)
38268                         {
38269                             options.params = options.params || {};
38270                             options.params._month = _this.monthField.getValue();
38271                             options.params.limit = 9999;
38272                             options.params['sort'] = 'when_dt';    
38273                             options.params['dir'] = 'ASC';    
38274                             this.proxy.loadResponse = this.loadResponse;
38275                             Roo.log("load?");
38276                             //this.addColumns();
38277                         },
38278                         load : function (_self, records, options)
38279                         {
38280                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38281                                 // if you click on the translation.. you can edit it...
38282                                 var el = Roo.get(this);
38283                                 var id = el.dom.getAttribute('data-id');
38284                                 var d = el.dom.getAttribute('data-date');
38285                                 var t = el.dom.getAttribute('data-time');
38286                                 //var id = this.child('span').dom.textContent;
38287                                 
38288                                 //Roo.log(this);
38289                                 Pman.Dialog.CourseCalendar.show({
38290                                     id : id,
38291                                     when_d : d,
38292                                     when_t : t,
38293                                     productitem_active : id ? 1 : 0
38294                                 }, function() {
38295                                     _this.grid.ds.load({});
38296                                 });
38297                            
38298                            });
38299                            
38300                            _this.panel.fireEvent('resize', [ '', '' ]);
38301                         }
38302                     },
38303                     loadResponse : function(o, success, response){
38304                             // this is overridden on before load..
38305                             
38306                             Roo.log("our code?");       
38307                             //Roo.log(success);
38308                             //Roo.log(response)
38309                             delete this.activeRequest;
38310                             if(!success){
38311                                 this.fireEvent("loadexception", this, o, response);
38312                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38313                                 return;
38314                             }
38315                             var result;
38316                             try {
38317                                 result = o.reader.read(response);
38318                             }catch(e){
38319                                 Roo.log("load exception?");
38320                                 this.fireEvent("loadexception", this, o, response, e);
38321                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38322                                 return;
38323                             }
38324                             Roo.log("ready...");        
38325                             // loop through result.records;
38326                             // and set this.tdate[date] = [] << array of records..
38327                             _this.tdata  = {};
38328                             Roo.each(result.records, function(r){
38329                                 //Roo.log(r.data);
38330                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38331                                     _this.tdata[r.data.when_dt.format('j')] = [];
38332                                 }
38333                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38334                             });
38335                             
38336                             //Roo.log(_this.tdata);
38337                             
38338                             result.records = [];
38339                             result.totalRecords = 6;
38340                     
38341                             // let's generate some duumy records for the rows.
38342                             //var st = _this.dateField.getValue();
38343                             
38344                             // work out monday..
38345                             //st = st.add(Date.DAY, -1 * st.format('w'));
38346                             
38347                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38348                             
38349                             var firstOfMonth = date.getFirstDayOfMonth();
38350                             var days = date.getDaysInMonth();
38351                             var d = 1;
38352                             var firstAdded = false;
38353                             for (var i = 0; i < result.totalRecords ; i++) {
38354                                 //var d= st.add(Date.DAY, i);
38355                                 var row = {};
38356                                 var added = 0;
38357                                 for(var w = 0 ; w < 7 ; w++){
38358                                     if(!firstAdded && firstOfMonth != w){
38359                                         continue;
38360                                     }
38361                                     if(d > days){
38362                                         continue;
38363                                     }
38364                                     firstAdded = true;
38365                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38366                                     row['weekday'+w] = String.format(
38367                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38368                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38369                                                     d,
38370                                                     date.format('Y-m-')+dd
38371                                                 );
38372                                     added++;
38373                                     if(typeof(_this.tdata[d]) != 'undefined'){
38374                                         Roo.each(_this.tdata[d], function(r){
38375                                             var is_sub = '';
38376                                             var deactive = '';
38377                                             var id = r.id;
38378                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38379                                             if(r.parent_id*1>0){
38380                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38381                                                 id = r.parent_id;
38382                                             }
38383                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38384                                                 deactive = 'de-act-link';
38385                                             }
38386                                             
38387                                             row['weekday'+w] += String.format(
38388                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38389                                                     id, //0
38390                                                     r.product_id_name, //1
38391                                                     r.when_dt.format('h:ia'), //2
38392                                                     is_sub, //3
38393                                                     deactive, //4
38394                                                     desc // 5
38395                                             );
38396                                         });
38397                                     }
38398                                     d++;
38399                                 }
38400                                 
38401                                 // only do this if something added..
38402                                 if(added > 0){ 
38403                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38404                                 }
38405                                 
38406                                 
38407                                 // push it twice. (second one with an hour..
38408                                 
38409                             }
38410                             //Roo.log(result);
38411                             this.fireEvent("load", this, o, o.request.arg);
38412                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38413                         },
38414                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38415                     proxy : {
38416                         xtype: 'HttpProxy',
38417                         xns: Roo.data,
38418                         method : 'GET',
38419                         url : baseURL + '/Roo/Shop_course.php'
38420                     },
38421                     reader : {
38422                         xtype: 'JsonReader',
38423                         xns: Roo.data,
38424                         id : 'id',
38425                         fields : [
38426                             {
38427                                 'name': 'id',
38428                                 'type': 'int'
38429                             },
38430                             {
38431                                 'name': 'when_dt',
38432                                 'type': 'string'
38433                             },
38434                             {
38435                                 'name': 'end_dt',
38436                                 'type': 'string'
38437                             },
38438                             {
38439                                 'name': 'parent_id',
38440                                 'type': 'int'
38441                             },
38442                             {
38443                                 'name': 'product_id',
38444                                 'type': 'int'
38445                             },
38446                             {
38447                                 'name': 'productitem_id',
38448                                 'type': 'int'
38449                             },
38450                             {
38451                                 'name': 'guid',
38452                                 'type': 'int'
38453                             }
38454                         ]
38455                     }
38456                 },
38457                 toolbar : {
38458                     xtype: 'Toolbar',
38459                     xns: Roo,
38460                     items : [
38461                         {
38462                             xtype: 'Button',
38463                             xns: Roo.Toolbar,
38464                             listeners : {
38465                                 click : function (_self, e)
38466                                 {
38467                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38468                                     sd.setMonth(sd.getMonth()-1);
38469                                     _this.monthField.setValue(sd.format('Y-m-d'));
38470                                     _this.grid.ds.load({});
38471                                 }
38472                             },
38473                             text : "Back"
38474                         },
38475                         {
38476                             xtype: 'Separator',
38477                             xns: Roo.Toolbar
38478                         },
38479                         {
38480                             xtype: 'MonthField',
38481                             xns: Roo.form,
38482                             listeners : {
38483                                 render : function (_self)
38484                                 {
38485                                     _this.monthField = _self;
38486                                    // _this.monthField.set  today
38487                                 },
38488                                 select : function (combo, date)
38489                                 {
38490                                     _this.grid.ds.load({});
38491                                 }
38492                             },
38493                             value : (function() { return new Date(); })()
38494                         },
38495                         {
38496                             xtype: 'Separator',
38497                             xns: Roo.Toolbar
38498                         },
38499                         {
38500                             xtype: 'TextItem',
38501                             xns: Roo.Toolbar,
38502                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38503                         },
38504                         {
38505                             xtype: 'Fill',
38506                             xns: Roo.Toolbar
38507                         },
38508                         {
38509                             xtype: 'Button',
38510                             xns: Roo.Toolbar,
38511                             listeners : {
38512                                 click : function (_self, e)
38513                                 {
38514                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38515                                     sd.setMonth(sd.getMonth()+1);
38516                                     _this.monthField.setValue(sd.format('Y-m-d'));
38517                                     _this.grid.ds.load({});
38518                                 }
38519                             },
38520                             text : "Next"
38521                         }
38522                     ]
38523                 },
38524                  
38525             }
38526         };
38527         
38528         *//*
38529  * Based on:
38530  * Ext JS Library 1.1.1
38531  * Copyright(c) 2006-2007, Ext JS, LLC.
38532  *
38533  * Originally Released Under LGPL - original licence link has changed is not relivant.
38534  *
38535  * Fork - LGPL
38536  * <script type="text/javascript">
38537  */
38538  
38539 /**
38540  * @class Roo.LoadMask
38541  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38542  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38543  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38544  * element's UpdateManager load indicator and will be destroyed after the initial load.
38545  * @constructor
38546  * Create a new LoadMask
38547  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38548  * @param {Object} config The config object
38549  */
38550 Roo.LoadMask = function(el, config){
38551     this.el = Roo.get(el);
38552     Roo.apply(this, config);
38553     if(this.store){
38554         this.store.on('beforeload', this.onBeforeLoad, this);
38555         this.store.on('load', this.onLoad, this);
38556         this.store.on('loadexception', this.onLoadException, this);
38557         this.removeMask = false;
38558     }else{
38559         var um = this.el.getUpdateManager();
38560         um.showLoadIndicator = false; // disable the default indicator
38561         um.on('beforeupdate', this.onBeforeLoad, this);
38562         um.on('update', this.onLoad, this);
38563         um.on('failure', this.onLoad, this);
38564         this.removeMask = true;
38565     }
38566 };
38567
38568 Roo.LoadMask.prototype = {
38569     /**
38570      * @cfg {Boolean} removeMask
38571      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38572      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38573      */
38574     /**
38575      * @cfg {String} msg
38576      * The text to display in a centered loading message box (defaults to 'Loading...')
38577      */
38578     msg : 'Loading...',
38579     /**
38580      * @cfg {String} msgCls
38581      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38582      */
38583     msgCls : 'x-mask-loading',
38584
38585     /**
38586      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38587      * @type Boolean
38588      */
38589     disabled: false,
38590
38591     /**
38592      * Disables the mask to prevent it from being displayed
38593      */
38594     disable : function(){
38595        this.disabled = true;
38596     },
38597
38598     /**
38599      * Enables the mask so that it can be displayed
38600      */
38601     enable : function(){
38602         this.disabled = false;
38603     },
38604     
38605     onLoadException : function()
38606     {
38607         Roo.log(arguments);
38608         
38609         if (typeof(arguments[3]) != 'undefined') {
38610             Roo.MessageBox.alert("Error loading",arguments[3]);
38611         } 
38612         /*
38613         try {
38614             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38615                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38616             }   
38617         } catch(e) {
38618             
38619         }
38620         */
38621     
38622         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38623     },
38624     // private
38625     onLoad : function()
38626     {
38627         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38628     },
38629
38630     // private
38631     onBeforeLoad : function(){
38632         if(!this.disabled){
38633             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38634         }
38635     },
38636
38637     // private
38638     destroy : function(){
38639         if(this.store){
38640             this.store.un('beforeload', this.onBeforeLoad, this);
38641             this.store.un('load', this.onLoad, this);
38642             this.store.un('loadexception', this.onLoadException, this);
38643         }else{
38644             var um = this.el.getUpdateManager();
38645             um.un('beforeupdate', this.onBeforeLoad, this);
38646             um.un('update', this.onLoad, this);
38647             um.un('failure', this.onLoad, this);
38648         }
38649     }
38650 };/*
38651  * Based on:
38652  * Ext JS Library 1.1.1
38653  * Copyright(c) 2006-2007, Ext JS, LLC.
38654  *
38655  * Originally Released Under LGPL - original licence link has changed is not relivant.
38656  *
38657  * Fork - LGPL
38658  * <script type="text/javascript">
38659  */
38660
38661
38662 /**
38663  * @class Roo.XTemplate
38664  * @extends Roo.Template
38665  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38666 <pre><code>
38667 var t = new Roo.XTemplate(
38668         '&lt;select name="{name}"&gt;',
38669                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38670         '&lt;/select&gt;'
38671 );
38672  
38673 // then append, applying the master template values
38674  </code></pre>
38675  *
38676  * Supported features:
38677  *
38678  *  Tags:
38679
38680 <pre><code>
38681       {a_variable} - output encoded.
38682       {a_variable.format:("Y-m-d")} - call a method on the variable
38683       {a_variable:raw} - unencoded output
38684       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38685       {a_variable:this.method_on_template(...)} - call a method on the template object.
38686  
38687 </code></pre>
38688  *  The tpl tag:
38689 <pre><code>
38690         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38691         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38692         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38693         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38694   
38695         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38696         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38697 </code></pre>
38698  *      
38699  */
38700 Roo.XTemplate = function()
38701 {
38702     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38703     if (this.html) {
38704         this.compile();
38705     }
38706 };
38707
38708
38709 Roo.extend(Roo.XTemplate, Roo.Template, {
38710
38711     /**
38712      * The various sub templates
38713      */
38714     tpls : false,
38715     /**
38716      *
38717      * basic tag replacing syntax
38718      * WORD:WORD()
38719      *
38720      * // you can fake an object call by doing this
38721      *  x.t:(test,tesT) 
38722      * 
38723      */
38724     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38725
38726     /**
38727      * compile the template
38728      *
38729      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38730      *
38731      */
38732     compile: function()
38733     {
38734         var s = this.html;
38735      
38736         s = ['<tpl>', s, '</tpl>'].join('');
38737     
38738         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38739             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38740             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38741             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38742             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38743             m,
38744             id     = 0,
38745             tpls   = [];
38746     
38747         while(true == !!(m = s.match(re))){
38748             var forMatch   = m[0].match(nameRe),
38749                 ifMatch   = m[0].match(ifRe),
38750                 execMatch   = m[0].match(execRe),
38751                 namedMatch   = m[0].match(namedRe),
38752                 
38753                 exp  = null, 
38754                 fn   = null,
38755                 exec = null,
38756                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38757                 
38758             if (ifMatch) {
38759                 // if - puts fn into test..
38760                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38761                 if(exp){
38762                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38763                 }
38764             }
38765             
38766             if (execMatch) {
38767                 // exec - calls a function... returns empty if true is  returned.
38768                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38769                 if(exp){
38770                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38771                 }
38772             }
38773             
38774             
38775             if (name) {
38776                 // for = 
38777                 switch(name){
38778                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38779                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38780                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38781                 }
38782             }
38783             var uid = namedMatch ? namedMatch[1] : id;
38784             
38785             
38786             tpls.push({
38787                 id:     namedMatch ? namedMatch[1] : id,
38788                 target: name,
38789                 exec:   exec,
38790                 test:   fn,
38791                 body:   m[1] || ''
38792             });
38793             if (namedMatch) {
38794                 s = s.replace(m[0], '');
38795             } else { 
38796                 s = s.replace(m[0], '{xtpl'+ id + '}');
38797             }
38798             ++id;
38799         }
38800         this.tpls = [];
38801         for(var i = tpls.length-1; i >= 0; --i){
38802             this.compileTpl(tpls[i]);
38803             this.tpls[tpls[i].id] = tpls[i];
38804         }
38805         this.master = tpls[tpls.length-1];
38806         return this;
38807     },
38808     /**
38809      * same as applyTemplate, except it's done to one of the subTemplates
38810      * when using named templates, you can do:
38811      *
38812      * var str = pl.applySubTemplate('your-name', values);
38813      *
38814      * 
38815      * @param {Number} id of the template
38816      * @param {Object} values to apply to template
38817      * @param {Object} parent (normaly the instance of this object)
38818      */
38819     applySubTemplate : function(id, values, parent)
38820     {
38821         
38822         
38823         var t = this.tpls[id];
38824         
38825         
38826         try { 
38827             if(t.test && !t.test.call(this, values, parent)){
38828                 return '';
38829             }
38830         } catch(e) {
38831             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38832             Roo.log(e.toString());
38833             Roo.log(t.test);
38834             return ''
38835         }
38836         try { 
38837             
38838             if(t.exec && t.exec.call(this, values, parent)){
38839                 return '';
38840             }
38841         } catch(e) {
38842             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38843             Roo.log(e.toString());
38844             Roo.log(t.exec);
38845             return ''
38846         }
38847         try {
38848             var vs = t.target ? t.target.call(this, values, parent) : values;
38849             parent = t.target ? values : parent;
38850             if(t.target && vs instanceof Array){
38851                 var buf = [];
38852                 for(var i = 0, len = vs.length; i < len; i++){
38853                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38854                 }
38855                 return buf.join('');
38856             }
38857             return t.compiled.call(this, vs, parent);
38858         } catch (e) {
38859             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38860             Roo.log(e.toString());
38861             Roo.log(t.compiled);
38862             return '';
38863         }
38864     },
38865
38866     compileTpl : function(tpl)
38867     {
38868         var fm = Roo.util.Format;
38869         var useF = this.disableFormats !== true;
38870         var sep = Roo.isGecko ? "+" : ",";
38871         var undef = function(str) {
38872             Roo.log("Property not found :"  + str);
38873             return '';
38874         };
38875         
38876         var fn = function(m, name, format, args)
38877         {
38878             //Roo.log(arguments);
38879             args = args ? args.replace(/\\'/g,"'") : args;
38880             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38881             if (typeof(format) == 'undefined') {
38882                 format= 'htmlEncode';
38883             }
38884             if (format == 'raw' ) {
38885                 format = false;
38886             }
38887             
38888             if(name.substr(0, 4) == 'xtpl'){
38889                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38890             }
38891             
38892             // build an array of options to determine if value is undefined..
38893             
38894             // basically get 'xxxx.yyyy' then do
38895             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38896             //    (function () { Roo.log("Property not found"); return ''; })() :
38897             //    ......
38898             
38899             var udef_ar = [];
38900             var lookfor = '';
38901             Roo.each(name.split('.'), function(st) {
38902                 lookfor += (lookfor.length ? '.': '') + st;
38903                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38904             });
38905             
38906             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38907             
38908             
38909             if(format && useF){
38910                 
38911                 args = args ? ',' + args : "";
38912                  
38913                 if(format.substr(0, 5) != "this."){
38914                     format = "fm." + format + '(';
38915                 }else{
38916                     format = 'this.call("'+ format.substr(5) + '", ';
38917                     args = ", values";
38918                 }
38919                 
38920                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38921             }
38922              
38923             if (args.length) {
38924                 // called with xxyx.yuu:(test,test)
38925                 // change to ()
38926                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38927             }
38928             // raw.. - :raw modifier..
38929             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38930             
38931         };
38932         var body;
38933         // branched to use + in gecko and [].join() in others
38934         if(Roo.isGecko){
38935             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38936                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38937                     "';};};";
38938         }else{
38939             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38940             body.push(tpl.body.replace(/(\r\n|\n)/g,
38941                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38942             body.push("'].join('');};};");
38943             body = body.join('');
38944         }
38945         
38946         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38947        
38948         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38949         eval(body);
38950         
38951         return this;
38952     },
38953
38954     applyTemplate : function(values){
38955         return this.master.compiled.call(this, values, {});
38956         //var s = this.subs;
38957     },
38958
38959     apply : function(){
38960         return this.applyTemplate.apply(this, arguments);
38961     }
38962
38963  });
38964
38965 Roo.XTemplate.from = function(el){
38966     el = Roo.getDom(el);
38967     return new Roo.XTemplate(el.value || el.innerHTML);
38968 };