popover column resizing on grids
[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  * @static
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 [required] 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.DataReader} reader [required]  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          
716         if(!o){
717             if(success !== false){
718                 this.fireEvent("load", this, [], options, o);
719             }
720             if(options.callback){
721                 options.callback.call(options.scope || this, [], options, false);
722             }
723             return;
724         }
725         // if data returned failure - throw an exception.
726         if (o.success === false) {
727             // show a message if no listener is registered.
728             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
729                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
730             }
731             // loadmask wil be hooked into this..
732             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
733             return;
734         }
735         var r = o.records, t = o.totalRecords || r.length;
736         
737         this.fireEvent("beforeloadadd", this, r, options, o);
738         
739         if(!options || options.add !== true){
740             if(this.pruneModifiedRecords){
741                 this.modified = [];
742             }
743             for(var i = 0, len = r.length; i < len; i++){
744                 r[i].join(this);
745             }
746             if(this.snapshot){
747                 this.data = this.snapshot;
748                 delete this.snapshot;
749             }
750             this.data.clear();
751             this.data.addAll(r);
752             this.totalLength = t;
753             this.applySort();
754             this.fireEvent("datachanged", this);
755         }else{
756             this.totalLength = Math.max(t, this.data.length+r.length);
757             this.add(r);
758         }
759         
760         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
761                 
762             var e = new Roo.data.Record({});
763
764             e.set(this.parent.displayField, this.parent.emptyTitle);
765             e.set(this.parent.valueField, '');
766
767             this.insert(0, e);
768         }
769             
770         this.fireEvent("load", this, r, options, o);
771         if(options.callback){
772             options.callback.call(options.scope || this, r, options, true);
773         }
774     },
775
776
777     /**
778      * Loads data from a passed data block. A Reader which understands the format of the data
779      * must have been configured in the constructor.
780      * @param {Object} data The data block from which to read the Records.  The format of the data expected
781      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
782      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
783      */
784     loadData : function(o, append){
785         var r = this.reader.readRecords(o);
786         this.loadRecords(r, {add: append}, true);
787     },
788     
789      /**
790      * using 'cn' the nested child reader read the child array into it's child stores.
791      * @param {Object} rec The record with a 'children array
792      */
793     loadDataFromChildren : function(rec)
794     {
795         this.loadData(this.reader.toLoadData(rec));
796     },
797     
798
799     /**
800      * Gets the number of cached records.
801      * <p>
802      * <em>If using paging, this may not be the total size of the dataset. If the data object
803      * used by the Reader contains the dataset size, then the getTotalCount() function returns
804      * the data set size</em>
805      */
806     getCount : function(){
807         return this.data.length || 0;
808     },
809
810     /**
811      * Gets the total number of records in the dataset as returned by the server.
812      * <p>
813      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
814      * the dataset size</em>
815      */
816     getTotalCount : function(){
817         return this.totalLength || 0;
818     },
819
820     /**
821      * Returns the sort state of the Store as an object with two properties:
822      * <pre><code>
823  field {String} The name of the field by which the Records are sorted
824  direction {String} The sort order, "ASC" or "DESC"
825      * </code></pre>
826      */
827     getSortState : function(){
828         return this.sortInfo;
829     },
830
831     // private
832     applySort : function(){
833         if(this.sortInfo && !this.remoteSort){
834             var s = this.sortInfo, f = s.field;
835             var st = this.fields.get(f).sortType;
836             var fn = function(r1, r2){
837                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
838                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
839             };
840             this.data.sort(s.direction, fn);
841             if(this.snapshot && this.snapshot != this.data){
842                 this.snapshot.sort(s.direction, fn);
843             }
844         }
845     },
846
847     /**
848      * Sets the default sort column and order to be used by the next load operation.
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     setDefaultSort : function(field, dir){
853         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
854     },
855
856     /**
857      * Sort the Records.
858      * If remote sorting is used, the sort is performed on the server, and the cache is
859      * reloaded. If local sorting is used, the cache is sorted internally.
860      * @param {String} fieldName The name of the field to sort by.
861      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
862      */
863     sort : function(fieldName, dir){
864         var f = this.fields.get(fieldName);
865         if(!dir){
866             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
867             
868             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
869                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
870             }else{
871                 dir = f.sortDir;
872             }
873         }
874         this.sortToggle[f.name] = dir;
875         this.sortInfo = {field: f.name, direction: dir};
876         if(!this.remoteSort){
877             this.applySort();
878             this.fireEvent("datachanged", this);
879         }else{
880             this.load(this.lastOptions);
881         }
882     },
883
884     /**
885      * Calls the specified function for each of the Records in the cache.
886      * @param {Function} fn The function to call. The Record is passed as the first parameter.
887      * Returning <em>false</em> aborts and exits the iteration.
888      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
889      */
890     each : function(fn, scope){
891         this.data.each(fn, scope);
892     },
893
894     /**
895      * Gets all records modified since the last commit.  Modified records are persisted across load operations
896      * (e.g., during paging).
897      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
898      */
899     getModifiedRecords : function(){
900         return this.modified;
901     },
902
903     // private
904     createFilterFn : function(property, value, anyMatch){
905         if(!value.exec){ // not a regex
906             value = String(value);
907             if(value.length == 0){
908                 return false;
909             }
910             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
911         }
912         return function(r){
913             return value.test(r.data[property]);
914         };
915     },
916
917     /**
918      * Sums the value of <i>property</i> for each record between start and end and returns the result.
919      * @param {String} property A field on your records
920      * @param {Number} start The record index to start at (defaults to 0)
921      * @param {Number} end The last record index to include (defaults to length - 1)
922      * @return {Number} The sum
923      */
924     sum : function(property, start, end){
925         var rs = this.data.items, v = 0;
926         start = start || 0;
927         end = (end || end === 0) ? end : rs.length-1;
928
929         for(var i = start; i <= end; i++){
930             v += (rs[i].data[property] || 0);
931         }
932         return v;
933     },
934
935     /**
936      * Filter the records by a specified property.
937      * @param {String} field A field on your records
938      * @param {String/RegExp} value Either a string that the field
939      * should start with or a RegExp to test against the field
940      * @param {Boolean} anyMatch True to match any part not just the beginning
941      */
942     filter : function(property, value, anyMatch){
943         var fn = this.createFilterFn(property, value, anyMatch);
944         return fn ? this.filterBy(fn) : this.clearFilter();
945     },
946
947     /**
948      * Filter by a function. The specified function will be called with each
949      * record in this data source. If the function returns true the record is included,
950      * otherwise it is filtered.
951      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
952      * @param {Object} scope (optional) The scope of the function (defaults to this)
953      */
954     filterBy : function(fn, scope){
955         this.snapshot = this.snapshot || this.data;
956         this.data = this.queryBy(fn, scope||this);
957         this.fireEvent("datachanged", this);
958     },
959
960     /**
961      * Query the records by a specified property.
962      * @param {String} field A field on your records
963      * @param {String/RegExp} value Either a string that the field
964      * should start with or a RegExp to test against the field
965      * @param {Boolean} anyMatch True to match any part not just the beginning
966      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
967      */
968     query : function(property, value, anyMatch){
969         var fn = this.createFilterFn(property, value, anyMatch);
970         return fn ? this.queryBy(fn) : this.data.clone();
971     },
972
973     /**
974      * Query by a function. The specified function will be called with each
975      * record in this data source. If the function returns true the record is included
976      * in the results.
977      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
978      * @param {Object} scope (optional) The scope of the function (defaults to this)
979       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
980      **/
981     queryBy : function(fn, scope){
982         var data = this.snapshot || this.data;
983         return data.filterBy(fn, scope||this);
984     },
985
986     /**
987      * Collects unique values for a particular dataIndex from this store.
988      * @param {String} dataIndex The property to collect
989      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
990      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
991      * @return {Array} An array of the unique values
992      **/
993     collect : function(dataIndex, allowNull, bypassFilter){
994         var d = (bypassFilter === true && this.snapshot) ?
995                 this.snapshot.items : this.data.items;
996         var v, sv, r = [], l = {};
997         for(var i = 0, len = d.length; i < len; i++){
998             v = d[i].data[dataIndex];
999             sv = String(v);
1000             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1001                 l[sv] = true;
1002                 r[r.length] = v;
1003             }
1004         }
1005         return r;
1006     },
1007
1008     /**
1009      * Revert to a view of the Record cache with no filtering applied.
1010      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1011      */
1012     clearFilter : function(suppressEvent){
1013         if(this.snapshot && this.snapshot != this.data){
1014             this.data = this.snapshot;
1015             delete this.snapshot;
1016             if(suppressEvent !== true){
1017                 this.fireEvent("datachanged", this);
1018             }
1019         }
1020     },
1021
1022     // private
1023     afterEdit : function(record){
1024         if(this.modified.indexOf(record) == -1){
1025             this.modified.push(record);
1026         }
1027         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1028     },
1029     
1030     // private
1031     afterReject : function(record){
1032         this.modified.remove(record);
1033         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1034     },
1035
1036     // private
1037     afterCommit : function(record){
1038         this.modified.remove(record);
1039         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1040     },
1041
1042     /**
1043      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1044      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1045      */
1046     commitChanges : 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].commit();
1051         }
1052     },
1053
1054     /**
1055      * Cancel outstanding changes on all changed records.
1056      */
1057     rejectChanges : function(){
1058         var m = this.modified.slice(0);
1059         this.modified = [];
1060         for(var i = 0, len = m.length; i < len; i++){
1061             m[i].reject();
1062         }
1063     },
1064
1065     onMetaChange : function(meta, rtype, o){
1066         this.recordType = rtype;
1067         this.fields = rtype.prototype.fields;
1068         delete this.snapshot;
1069         this.sortInfo = meta.sortInfo || this.sortInfo;
1070         this.modified = [];
1071         this.fireEvent('metachange', this, this.reader.meta);
1072     },
1073     
1074     moveIndex : function(data, type)
1075     {
1076         var index = this.indexOf(data);
1077         
1078         var newIndex = index + type;
1079         
1080         this.remove(data);
1081         
1082         this.insert(newIndex, data);
1083         
1084     }
1085 });/*
1086  * Based on:
1087  * Ext JS Library 1.1.1
1088  * Copyright(c) 2006-2007, Ext JS, LLC.
1089  *
1090  * Originally Released Under LGPL - original licence link has changed is not relivant.
1091  *
1092  * Fork - LGPL
1093  * <script type="text/javascript">
1094  */
1095
1096 /**
1097  * @class Roo.data.SimpleStore
1098  * @extends Roo.data.Store
1099  * Small helper class to make creating Stores from Array data easier.
1100  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1101  * @cfg {Array} fields An array of field definition objects, or field name strings.
1102  * @cfg {Object} an existing reader (eg. copied from another store)
1103  * @cfg {Array} data The multi-dimensional array of data
1104  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1105  * @cfg {Roo.data.Reader} reader  [not-required] 
1106  * @constructor
1107  * @param {Object} config
1108  */
1109 Roo.data.SimpleStore = function(config)
1110 {
1111     Roo.data.SimpleStore.superclass.constructor.call(this, {
1112         isLocal : true,
1113         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1114                 id: config.id
1115             },
1116             Roo.data.Record.create(config.fields)
1117         ),
1118         proxy : new Roo.data.MemoryProxy(config.data)
1119     });
1120     this.load();
1121 };
1122 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1123  * Based on:
1124  * Ext JS Library 1.1.1
1125  * Copyright(c) 2006-2007, Ext JS, LLC.
1126  *
1127  * Originally Released Under LGPL - original licence link has changed is not relivant.
1128  *
1129  * Fork - LGPL
1130  * <script type="text/javascript">
1131  */
1132
1133 /**
1134 /**
1135  * @extends Roo.data.Store
1136  * @class Roo.data.JsonStore
1137  * Small helper class to make creating Stores for JSON data easier. <br/>
1138 <pre><code>
1139 var store = new Roo.data.JsonStore({
1140     url: 'get-images.php',
1141     root: 'images',
1142     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1143 });
1144 </code></pre>
1145  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1146  * JsonReader and HttpProxy (unless inline data is provided).</b>
1147  * @cfg {Array} fields An array of field definition objects, or field name strings.
1148  * @constructor
1149  * @param {Object} config
1150  */
1151 Roo.data.JsonStore = function(c){
1152     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1153         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1154         reader: new Roo.data.JsonReader(c, c.fields)
1155     }));
1156 };
1157 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1158  * Based on:
1159  * Ext JS Library 1.1.1
1160  * Copyright(c) 2006-2007, Ext JS, LLC.
1161  *
1162  * Originally Released Under LGPL - original licence link has changed is not relivant.
1163  *
1164  * Fork - LGPL
1165  * <script type="text/javascript">
1166  */
1167
1168  
1169 Roo.data.Field = function(config){
1170     if(typeof config == "string"){
1171         config = {name: config};
1172     }
1173     Roo.apply(this, config);
1174     
1175     if(!this.type){
1176         this.type = "auto";
1177     }
1178     
1179     var st = Roo.data.SortTypes;
1180     // named sortTypes are supported, here we look them up
1181     if(typeof this.sortType == "string"){
1182         this.sortType = st[this.sortType];
1183     }
1184     
1185     // set default sortType for strings and dates
1186     if(!this.sortType){
1187         switch(this.type){
1188             case "string":
1189                 this.sortType = st.asUCString;
1190                 break;
1191             case "date":
1192                 this.sortType = st.asDate;
1193                 break;
1194             default:
1195                 this.sortType = st.none;
1196         }
1197     }
1198
1199     // define once
1200     var stripRe = /[\$,%]/g;
1201
1202     // prebuilt conversion function for this field, instead of
1203     // switching every time we're reading a value
1204     if(!this.convert){
1205         var cv, dateFormat = this.dateFormat;
1206         switch(this.type){
1207             case "":
1208             case "auto":
1209             case undefined:
1210                 cv = function(v){ return v; };
1211                 break;
1212             case "string":
1213                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1214                 break;
1215             case "int":
1216                 cv = function(v){
1217                     return v !== undefined && v !== null && v !== '' ?
1218                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1219                     };
1220                 break;
1221             case "float":
1222                 cv = function(v){
1223                     return v !== undefined && v !== null && v !== '' ?
1224                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1225                     };
1226                 break;
1227             case "bool":
1228             case "boolean":
1229                 cv = function(v){ return v === true || v === "true" || v == 1; };
1230                 break;
1231             case "date":
1232                 cv = function(v){
1233                     if(!v){
1234                         return '';
1235                     }
1236                     if(v instanceof Date){
1237                         return v;
1238                     }
1239                     if(dateFormat){
1240                         if(dateFormat == "timestamp"){
1241                             return new Date(v*1000);
1242                         }
1243                         return Date.parseDate(v, dateFormat);
1244                     }
1245                     var parsed = Date.parse(v);
1246                     return parsed ? new Date(parsed) : null;
1247                 };
1248              break;
1249             
1250         }
1251         this.convert = cv;
1252     }
1253 };
1254
1255 Roo.data.Field.prototype = {
1256     dateFormat: null,
1257     defaultValue: "",
1258     mapping: null,
1259     sortType : null,
1260     sortDir : "ASC"
1261 };/*
1262  * Based on:
1263  * Ext JS Library 1.1.1
1264  * Copyright(c) 2006-2007, Ext JS, LLC.
1265  *
1266  * Originally Released Under LGPL - original licence link has changed is not relivant.
1267  *
1268  * Fork - LGPL
1269  * <script type="text/javascript">
1270  */
1271  
1272 // Base class for reading structured data from a data source.  This class is intended to be
1273 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1274
1275 /**
1276  * @class Roo.data.DataReader
1277  * @abstract
1278  * Base class for reading structured data from a data source.  This class is intended to be
1279  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1280  */
1281
1282 Roo.data.DataReader = function(meta, recordType){
1283     
1284     this.meta = meta;
1285     
1286     this.recordType = recordType instanceof Array ? 
1287         Roo.data.Record.create(recordType) : recordType;
1288 };
1289
1290 Roo.data.DataReader.prototype = {
1291     
1292     
1293     readerType : 'Data',
1294      /**
1295      * Create an empty record
1296      * @param {Object} data (optional) - overlay some values
1297      * @return {Roo.data.Record} record created.
1298      */
1299     newRow :  function(d) {
1300         var da =  {};
1301         this.recordType.prototype.fields.each(function(c) {
1302             switch( c.type) {
1303                 case 'int' : da[c.name] = 0; break;
1304                 case 'date' : da[c.name] = new Date(); break;
1305                 case 'float' : da[c.name] = 0.0; break;
1306                 case 'boolean' : da[c.name] = false; break;
1307                 default : da[c.name] = ""; break;
1308             }
1309             
1310         });
1311         return new this.recordType(Roo.apply(da, d));
1312     }
1313     
1314     
1315 };/*
1316  * Based on:
1317  * Ext JS Library 1.1.1
1318  * Copyright(c) 2006-2007, Ext JS, LLC.
1319  *
1320  * Originally Released Under LGPL - original licence link has changed is not relivant.
1321  *
1322  * Fork - LGPL
1323  * <script type="text/javascript">
1324  */
1325
1326 /**
1327  * @class Roo.data.DataProxy
1328  * @extends Roo.util.Observable
1329  * @abstract
1330  * This class is an abstract base class for implementations which provide retrieval of
1331  * unformatted data objects.<br>
1332  * <p>
1333  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1334  * (of the appropriate type which knows how to parse the data object) to provide a block of
1335  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1336  * <p>
1337  * Custom implementations must implement the load method as described in
1338  * {@link Roo.data.HttpProxy#load}.
1339  */
1340 Roo.data.DataProxy = function(){
1341     this.addEvents({
1342         /**
1343          * @event beforeload
1344          * Fires before a network request is made to retrieve a data object.
1345          * @param {Object} This DataProxy object.
1346          * @param {Object} params The params parameter to the load function.
1347          */
1348         beforeload : true,
1349         /**
1350          * @event load
1351          * Fires before the load method's callback is called.
1352          * @param {Object} This DataProxy object.
1353          * @param {Object} o The data object.
1354          * @param {Object} arg The callback argument object passed to the load function.
1355          */
1356         load : true,
1357         /**
1358          * @event loadexception
1359          * Fires if an Exception occurs during data retrieval.
1360          * @param {Object} This DataProxy object.
1361          * @param {Object} o The data object.
1362          * @param {Object} arg The callback argument object passed to the load function.
1363          * @param {Object} e The Exception.
1364          */
1365         loadexception : true
1366     });
1367     Roo.data.DataProxy.superclass.constructor.call(this);
1368 };
1369
1370 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1371
1372     /**
1373      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1374      */
1375 /*
1376  * Based on:
1377  * Ext JS Library 1.1.1
1378  * Copyright(c) 2006-2007, Ext JS, LLC.
1379  *
1380  * Originally Released Under LGPL - original licence link has changed is not relivant.
1381  *
1382  * Fork - LGPL
1383  * <script type="text/javascript">
1384  */
1385 /**
1386  * @class Roo.data.MemoryProxy
1387  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1388  * to the Reader when its load method is called.
1389  * @constructor
1390  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1391  */
1392 Roo.data.MemoryProxy = function(data){
1393     if (data.data) {
1394         data = data.data;
1395     }
1396     Roo.data.MemoryProxy.superclass.constructor.call(this);
1397     this.data = data;
1398 };
1399
1400 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1401     
1402     /**
1403      * Load data from the requested source (in this case an in-memory
1404      * data object passed to the constructor), read the data object into
1405      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1406      * process that block using the passed callback.
1407      * @param {Object} params This parameter is not used by the MemoryProxy class.
1408      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1409      * object into a block of Roo.data.Records.
1410      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1411      * The function must be passed <ul>
1412      * <li>The Record block object</li>
1413      * <li>The "arg" argument from the load function</li>
1414      * <li>A boolean success indicator</li>
1415      * </ul>
1416      * @param {Object} scope The scope in which to call the callback
1417      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1418      */
1419     load : function(params, reader, callback, scope, arg){
1420         params = params || {};
1421         var result;
1422         try {
1423             result = reader.readRecords(params.data ? params.data :this.data);
1424         }catch(e){
1425             this.fireEvent("loadexception", this, arg, null, e);
1426             callback.call(scope, null, arg, false);
1427             return;
1428         }
1429         callback.call(scope, result, arg, true);
1430     },
1431     
1432     // private
1433     update : function(params, records){
1434         
1435     }
1436 });/*
1437  * Based on:
1438  * Ext JS Library 1.1.1
1439  * Copyright(c) 2006-2007, Ext JS, LLC.
1440  *
1441  * Originally Released Under LGPL - original licence link has changed is not relivant.
1442  *
1443  * Fork - LGPL
1444  * <script type="text/javascript">
1445  */
1446 /**
1447  * @class Roo.data.HttpProxy
1448  * @extends Roo.data.DataProxy
1449  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1450  * configured to reference a certain URL.<br><br>
1451  * <p>
1452  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1453  * from which the running page was served.<br><br>
1454  * <p>
1455  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1456  * <p>
1457  * Be aware that to enable the browser to parse an XML document, the server must set
1458  * the Content-Type header in the HTTP response to "text/xml".
1459  * @constructor
1460  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1461  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1462  * will be used to make the request.
1463  */
1464 Roo.data.HttpProxy = function(conn){
1465     Roo.data.HttpProxy.superclass.constructor.call(this);
1466     // is conn a conn config or a real conn?
1467     this.conn = conn;
1468     this.useAjax = !conn || !conn.events;
1469   
1470 };
1471
1472 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1473     // thse are take from connection...
1474     
1475     /**
1476      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1477      */
1478     /**
1479      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1480      * extra parameters to each request made by this object. (defaults to undefined)
1481      */
1482     /**
1483      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1484      *  to each request made by this object. (defaults to undefined)
1485      */
1486     /**
1487      * @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)
1488      */
1489     /**
1490      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1491      */
1492      /**
1493      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1494      * @type Boolean
1495      */
1496   
1497
1498     /**
1499      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1500      * @type Boolean
1501      */
1502     /**
1503      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1504      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1505      * a finer-grained basis than the DataProxy events.
1506      */
1507     getConnection : function(){
1508         return this.useAjax ? Roo.Ajax : this.conn;
1509     },
1510
1511     /**
1512      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1513      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1514      * process that block using the passed callback.
1515      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1516      * for the request to the remote server.
1517      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1518      * object into a block of Roo.data.Records.
1519      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1520      * The function must be passed <ul>
1521      * <li>The Record block object</li>
1522      * <li>The "arg" argument from the load function</li>
1523      * <li>A boolean success indicator</li>
1524      * </ul>
1525      * @param {Object} scope The scope in which to call the callback
1526      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1527      */
1528     load : function(params, reader, callback, scope, arg){
1529         if(this.fireEvent("beforeload", this, params) !== false){
1530             var  o = {
1531                 params : params || {},
1532                 request: {
1533                     callback : callback,
1534                     scope : scope,
1535                     arg : arg
1536                 },
1537                 reader: reader,
1538                 callback : this.loadResponse,
1539                 scope: this
1540             };
1541             if(this.useAjax){
1542                 Roo.applyIf(o, this.conn);
1543                 if(this.activeRequest){
1544                     Roo.Ajax.abort(this.activeRequest);
1545                 }
1546                 this.activeRequest = Roo.Ajax.request(o);
1547             }else{
1548                 this.conn.request(o);
1549             }
1550         }else{
1551             callback.call(scope||this, null, arg, false);
1552         }
1553     },
1554
1555     // private
1556     loadResponse : function(o, success, response){
1557         delete this.activeRequest;
1558         if(!success){
1559             this.fireEvent("loadexception", this, o, response);
1560             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1561             return;
1562         }
1563         var result;
1564         try {
1565             result = o.reader.read(response);
1566         }catch(e){
1567             this.fireEvent("loadexception", this, o, response, e);
1568             o.request.callback.call(o.request.scope, {
1569                     success : false,
1570                     raw : {
1571                         errorMsg : response.responseText
1572                     }
1573                     
1574                 }, o.request.arg, false);
1575             return;
1576         }
1577         
1578         this.fireEvent("load", this, o, o.request.arg);
1579         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1580     },
1581
1582     // private
1583     update : function(dataSet){
1584
1585     },
1586
1587     // private
1588     updateResponse : function(dataSet){
1589
1590     }
1591 });/*
1592  * Based on:
1593  * Ext JS Library 1.1.1
1594  * Copyright(c) 2006-2007, Ext JS, LLC.
1595  *
1596  * Originally Released Under LGPL - original licence link has changed is not relivant.
1597  *
1598  * Fork - LGPL
1599  * <script type="text/javascript">
1600  */
1601
1602 /**
1603  * @class Roo.data.ScriptTagProxy
1604  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1605  * other than the originating domain of the running page.<br><br>
1606  * <p>
1607  * <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
1608  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1609  * <p>
1610  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1611  * source code that is used as the source inside a &lt;script> tag.<br><br>
1612  * <p>
1613  * In order for the browser to process the returned data, the server must wrap the data object
1614  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1615  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1616  * depending on whether the callback name was passed:
1617  * <p>
1618  * <pre><code>
1619 boolean scriptTag = false;
1620 String cb = request.getParameter("callback");
1621 if (cb != null) {
1622     scriptTag = true;
1623     response.setContentType("text/javascript");
1624 } else {
1625     response.setContentType("application/x-json");
1626 }
1627 Writer out = response.getWriter();
1628 if (scriptTag) {
1629     out.write(cb + "(");
1630 }
1631 out.print(dataBlock.toJsonString());
1632 if (scriptTag) {
1633     out.write(");");
1634 }
1635 </pre></code>
1636  *
1637  * @constructor
1638  * @param {Object} config A configuration object.
1639  */
1640 Roo.data.ScriptTagProxy = function(config){
1641     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1642     Roo.apply(this, config);
1643     this.head = document.getElementsByTagName("head")[0];
1644 };
1645
1646 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1647
1648 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1649     /**
1650      * @cfg {String} url The URL from which to request the data object.
1651      */
1652     /**
1653      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1654      */
1655     timeout : 30000,
1656     /**
1657      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1658      * the server the name of the callback function set up by the load call to process the returned data object.
1659      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1660      * javascript output which calls this named function passing the data object as its only parameter.
1661      */
1662     callbackParam : "callback",
1663     /**
1664      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1665      * name to the request.
1666      */
1667     nocache : true,
1668
1669     /**
1670      * Load data from the configured URL, read the data object into
1671      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1672      * process that block using the passed callback.
1673      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1674      * for the request to the remote server.
1675      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1676      * object into a block of Roo.data.Records.
1677      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1678      * The function must be passed <ul>
1679      * <li>The Record block object</li>
1680      * <li>The "arg" argument from the load function</li>
1681      * <li>A boolean success indicator</li>
1682      * </ul>
1683      * @param {Object} scope The scope in which to call the callback
1684      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1685      */
1686     load : function(params, reader, callback, scope, arg){
1687         if(this.fireEvent("beforeload", this, params) !== false){
1688
1689             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1690
1691             var url = this.url;
1692             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1693             if(this.nocache){
1694                 url += "&_dc=" + (new Date().getTime());
1695             }
1696             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1697             var trans = {
1698                 id : transId,
1699                 cb : "stcCallback"+transId,
1700                 scriptId : "stcScript"+transId,
1701                 params : params,
1702                 arg : arg,
1703                 url : url,
1704                 callback : callback,
1705                 scope : scope,
1706                 reader : reader
1707             };
1708             var conn = this;
1709
1710             window[trans.cb] = function(o){
1711                 conn.handleResponse(o, trans);
1712             };
1713
1714             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1715
1716             if(this.autoAbort !== false){
1717                 this.abort();
1718             }
1719
1720             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1721
1722             var script = document.createElement("script");
1723             script.setAttribute("src", url);
1724             script.setAttribute("type", "text/javascript");
1725             script.setAttribute("id", trans.scriptId);
1726             this.head.appendChild(script);
1727
1728             this.trans = trans;
1729         }else{
1730             callback.call(scope||this, null, arg, false);
1731         }
1732     },
1733
1734     // private
1735     isLoading : function(){
1736         return this.trans ? true : false;
1737     },
1738
1739     /**
1740      * Abort the current server request.
1741      */
1742     abort : function(){
1743         if(this.isLoading()){
1744             this.destroyTrans(this.trans);
1745         }
1746     },
1747
1748     // private
1749     destroyTrans : function(trans, isLoaded){
1750         this.head.removeChild(document.getElementById(trans.scriptId));
1751         clearTimeout(trans.timeoutId);
1752         if(isLoaded){
1753             window[trans.cb] = undefined;
1754             try{
1755                 delete window[trans.cb];
1756             }catch(e){}
1757         }else{
1758             // if hasn't been loaded, wait for load to remove it to prevent script error
1759             window[trans.cb] = function(){
1760                 window[trans.cb] = undefined;
1761                 try{
1762                     delete window[trans.cb];
1763                 }catch(e){}
1764             };
1765         }
1766     },
1767
1768     // private
1769     handleResponse : function(o, trans){
1770         this.trans = false;
1771         this.destroyTrans(trans, true);
1772         var result;
1773         try {
1774             result = trans.reader.readRecords(o);
1775         }catch(e){
1776             this.fireEvent("loadexception", this, o, trans.arg, e);
1777             trans.callback.call(trans.scope||window, null, trans.arg, false);
1778             return;
1779         }
1780         this.fireEvent("load", this, o, trans.arg);
1781         trans.callback.call(trans.scope||window, result, trans.arg, true);
1782     },
1783
1784     // private
1785     handleFailure : function(trans){
1786         this.trans = false;
1787         this.destroyTrans(trans, false);
1788         this.fireEvent("loadexception", this, null, trans.arg);
1789         trans.callback.call(trans.scope||window, null, trans.arg, false);
1790     }
1791 });/*
1792  * Based on:
1793  * Ext JS Library 1.1.1
1794  * Copyright(c) 2006-2007, Ext JS, LLC.
1795  *
1796  * Originally Released Under LGPL - original licence link has changed is not relivant.
1797  *
1798  * Fork - LGPL
1799  * <script type="text/javascript">
1800  */
1801
1802 /**
1803  * @class Roo.data.JsonReader
1804  * @extends Roo.data.DataReader
1805  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1806  * based on mappings in a provided Roo.data.Record constructor.
1807  * 
1808  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1809  * in the reply previously. 
1810  * 
1811  * <p>
1812  * Example code:
1813  * <pre><code>
1814 var RecordDef = Roo.data.Record.create([
1815     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1816     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1817 ]);
1818 var myReader = new Roo.data.JsonReader({
1819     totalProperty: "results",    // The property which contains the total dataset size (optional)
1820     root: "rows",                // The property which contains an Array of row objects
1821     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1822 }, RecordDef);
1823 </code></pre>
1824  * <p>
1825  * This would consume a JSON file like this:
1826  * <pre><code>
1827 { 'results': 2, 'rows': [
1828     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1829     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1830 }
1831 </code></pre>
1832  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1833  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1834  * paged from the remote server.
1835  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1836  * @cfg {String} root name of the property which contains the Array of row objects.
1837  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1838  * @cfg {Array} fields Array of field definition objects
1839  * @constructor
1840  * Create a new JsonReader
1841  * @param {Object} meta Metadata configuration options
1842  * @param {Object} recordType Either an Array of field definition objects,
1843  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1844  */
1845 Roo.data.JsonReader = function(meta, recordType){
1846     
1847     meta = meta || {};
1848     // set some defaults:
1849     Roo.applyIf(meta, {
1850         totalProperty: 'total',
1851         successProperty : 'success',
1852         root : 'data',
1853         id : 'id'
1854     });
1855     
1856     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1857 };
1858 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1859     
1860     readerType : 'Json',
1861     
1862     /**
1863      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1864      * Used by Store query builder to append _requestMeta to params.
1865      * 
1866      */
1867     metaFromRemote : false,
1868     /**
1869      * This method is only used by a DataProxy which has retrieved data from a remote server.
1870      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1871      * @return {Object} data A data block which is used by an Roo.data.Store object as
1872      * a cache of Roo.data.Records.
1873      */
1874     read : function(response){
1875         var json = response.responseText;
1876        
1877         var o = /* eval:var:o */ eval("("+json+")");
1878         if(!o) {
1879             throw {message: "JsonReader.read: Json object not found"};
1880         }
1881         
1882         if(o.metaData){
1883             
1884             delete this.ef;
1885             this.metaFromRemote = true;
1886             this.meta = o.metaData;
1887             this.recordType = Roo.data.Record.create(o.metaData.fields);
1888             this.onMetaChange(this.meta, this.recordType, o);
1889         }
1890         return this.readRecords(o);
1891     },
1892
1893     // private function a store will implement
1894     onMetaChange : function(meta, recordType, o){
1895
1896     },
1897
1898     /**
1899          * @ignore
1900          */
1901     simpleAccess: function(obj, subsc) {
1902         return obj[subsc];
1903     },
1904
1905         /**
1906          * @ignore
1907          */
1908     getJsonAccessor: function(){
1909         var re = /[\[\.]/;
1910         return function(expr) {
1911             try {
1912                 return(re.test(expr))
1913                     ? new Function("obj", "return obj." + expr)
1914                     : function(obj){
1915                         return obj[expr];
1916                     };
1917             } catch(e){}
1918             return Roo.emptyFn;
1919         };
1920     }(),
1921
1922     /**
1923      * Create a data block containing Roo.data.Records from an XML document.
1924      * @param {Object} o An object which contains an Array of row objects in the property specified
1925      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1926      * which contains the total size of the dataset.
1927      * @return {Object} data A data block which is used by an Roo.data.Store object as
1928      * a cache of Roo.data.Records.
1929      */
1930     readRecords : function(o){
1931         /**
1932          * After any data loads, the raw JSON data is available for further custom processing.
1933          * @type Object
1934          */
1935         this.o = o;
1936         var s = this.meta, Record = this.recordType,
1937             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1938
1939 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1940         if (!this.ef) {
1941             if(s.totalProperty) {
1942                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1943                 }
1944                 if(s.successProperty) {
1945                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1946                 }
1947                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1948                 if (s.id) {
1949                         var g = this.getJsonAccessor(s.id);
1950                         this.getId = function(rec) {
1951                                 var r = g(rec);  
1952                                 return (r === undefined || r === "") ? null : r;
1953                         };
1954                 } else {
1955                         this.getId = function(){return null;};
1956                 }
1957             this.ef = [];
1958             for(var jj = 0; jj < fl; jj++){
1959                 f = fi[jj];
1960                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1961                 this.ef[jj] = this.getJsonAccessor(map);
1962             }
1963         }
1964
1965         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1966         if(s.totalProperty){
1967             var vt = parseInt(this.getTotal(o), 10);
1968             if(!isNaN(vt)){
1969                 totalRecords = vt;
1970             }
1971         }
1972         if(s.successProperty){
1973             var vs = this.getSuccess(o);
1974             if(vs === false || vs === 'false'){
1975                 success = false;
1976             }
1977         }
1978         var records = [];
1979         for(var i = 0; i < c; i++){
1980                 var n = root[i];
1981             var values = {};
1982             var id = this.getId(n);
1983             for(var j = 0; j < fl; j++){
1984                 f = fi[j];
1985             var v = this.ef[j](n);
1986             if (!f.convert) {
1987                 Roo.log('missing convert for ' + f.name);
1988                 Roo.log(f);
1989                 continue;
1990             }
1991             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1992             }
1993             var record = new Record(values, id);
1994             record.json = n;
1995             records[i] = record;
1996         }
1997         return {
1998             raw : o,
1999             success : success,
2000             records : records,
2001             totalRecords : totalRecords
2002         };
2003     },
2004     // used when loading children.. @see loadDataFromChildren
2005     toLoadData: function(rec)
2006     {
2007         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2008         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2009         return { data : data, total : data.length };
2010         
2011     }
2012 });/*
2013  * Based on:
2014  * Ext JS Library 1.1.1
2015  * Copyright(c) 2006-2007, Ext JS, LLC.
2016  *
2017  * Originally Released Under LGPL - original licence link has changed is not relivant.
2018  *
2019  * Fork - LGPL
2020  * <script type="text/javascript">
2021  */
2022
2023 /**
2024  * @class Roo.data.XmlReader
2025  * @extends Roo.data.DataReader
2026  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2027  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2028  * <p>
2029  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2030  * header in the HTTP response must be set to "text/xml".</em>
2031  * <p>
2032  * Example code:
2033  * <pre><code>
2034 var RecordDef = Roo.data.Record.create([
2035    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2036    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2037 ]);
2038 var myReader = new Roo.data.XmlReader({
2039    totalRecords: "results", // The element which contains the total dataset size (optional)
2040    record: "row",           // The repeated element which contains row information
2041    id: "id"                 // The element within the row that provides an ID for the record (optional)
2042 }, RecordDef);
2043 </code></pre>
2044  * <p>
2045  * This would consume an XML file like this:
2046  * <pre><code>
2047 &lt;?xml?>
2048 &lt;dataset>
2049  &lt;results>2&lt;/results>
2050  &lt;row>
2051    &lt;id>1&lt;/id>
2052    &lt;name>Bill&lt;/name>
2053    &lt;occupation>Gardener&lt;/occupation>
2054  &lt;/row>
2055  &lt;row>
2056    &lt;id>2&lt;/id>
2057    &lt;name>Ben&lt;/name>
2058    &lt;occupation>Horticulturalist&lt;/occupation>
2059  &lt;/row>
2060 &lt;/dataset>
2061 </code></pre>
2062  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2063  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2064  * paged from the remote server.
2065  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2066  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2067  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2068  * a record identifier value.
2069  * @constructor
2070  * Create a new XmlReader
2071  * @param {Object} meta Metadata configuration options
2072  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2073  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2074  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2075  */
2076 Roo.data.XmlReader = function(meta, recordType){
2077     meta = meta || {};
2078     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2079 };
2080 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2081     
2082     readerType : 'Xml',
2083     
2084     /**
2085      * This method is only used by a DataProxy which has retrieved data from a remote server.
2086          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2087          * to contain a method called 'responseXML' that returns an XML document object.
2088      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2089      * a cache of Roo.data.Records.
2090      */
2091     read : function(response){
2092         var doc = response.responseXML;
2093         if(!doc) {
2094             throw {message: "XmlReader.read: XML Document not available"};
2095         }
2096         return this.readRecords(doc);
2097     },
2098
2099     /**
2100      * Create a data block containing Roo.data.Records from an XML document.
2101          * @param {Object} doc A parsed XML document.
2102      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2103      * a cache of Roo.data.Records.
2104      */
2105     readRecords : function(doc){
2106         /**
2107          * After any data loads/reads, the raw XML Document is available for further custom processing.
2108          * @type XMLDocument
2109          */
2110         this.xmlData = doc;
2111         var root = doc.documentElement || doc;
2112         var q = Roo.DomQuery;
2113         var recordType = this.recordType, fields = recordType.prototype.fields;
2114         var sid = this.meta.id;
2115         var totalRecords = 0, success = true;
2116         if(this.meta.totalRecords){
2117             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2118         }
2119         
2120         if(this.meta.success){
2121             var sv = q.selectValue(this.meta.success, root, true);
2122             success = sv !== false && sv !== 'false';
2123         }
2124         var records = [];
2125         var ns = q.select(this.meta.record, root);
2126         for(var i = 0, len = ns.length; i < len; i++) {
2127                 var n = ns[i];
2128                 var values = {};
2129                 var id = sid ? q.selectValue(sid, n) : undefined;
2130                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2131                     var f = fields.items[j];
2132                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2133                     v = f.convert(v);
2134                     values[f.name] = v;
2135                 }
2136                 var record = new recordType(values, id);
2137                 record.node = n;
2138                 records[records.length] = record;
2139             }
2140
2141             return {
2142                 success : success,
2143                 records : records,
2144                 totalRecords : totalRecords || records.length
2145             };
2146     }
2147 });/*
2148  * Based on:
2149  * Ext JS Library 1.1.1
2150  * Copyright(c) 2006-2007, Ext JS, LLC.
2151  *
2152  * Originally Released Under LGPL - original licence link has changed is not relivant.
2153  *
2154  * Fork - LGPL
2155  * <script type="text/javascript">
2156  */
2157
2158 /**
2159  * @class Roo.data.ArrayReader
2160  * @extends Roo.data.DataReader
2161  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2162  * Each element of that Array represents a row of data fields. The
2163  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2164  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2165  * <p>
2166  * Example code:.
2167  * <pre><code>
2168 var RecordDef = Roo.data.Record.create([
2169     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2170     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2171 ]);
2172 var myReader = new Roo.data.ArrayReader({
2173     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2174 }, RecordDef);
2175 </code></pre>
2176  * <p>
2177  * This would consume an Array like this:
2178  * <pre><code>
2179 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2180   </code></pre>
2181  
2182  * @constructor
2183  * Create a new JsonReader
2184  * @param {Object} meta Metadata configuration options.
2185  * @param {Object|Array} recordType Either an Array of field definition objects
2186  * 
2187  * @cfg {Array} fields Array of field definition objects
2188  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2189  * as specified to {@link Roo.data.Record#create},
2190  * or an {@link Roo.data.Record} object
2191  *
2192  * 
2193  * created using {@link Roo.data.Record#create}.
2194  */
2195 Roo.data.ArrayReader = function(meta, recordType)
2196 {    
2197     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2198 };
2199
2200 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2201     
2202       /**
2203      * Create a data block containing Roo.data.Records from an XML document.
2204      * @param {Object} o An Array of row objects which represents the dataset.
2205      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2206      * a cache of Roo.data.Records.
2207      */
2208     readRecords : function(o)
2209     {
2210         var sid = this.meta ? this.meta.id : null;
2211         var recordType = this.recordType, fields = recordType.prototype.fields;
2212         var records = [];
2213         var root = o;
2214         for(var i = 0; i < root.length; i++){
2215             var n = root[i];
2216             var values = {};
2217             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2218             for(var j = 0, jlen = fields.length; j < jlen; j++){
2219                 var f = fields.items[j];
2220                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2221                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2222                 v = f.convert(v);
2223                 values[f.name] = v;
2224             }
2225             var record = new recordType(values, id);
2226             record.json = n;
2227             records[records.length] = record;
2228         }
2229         return {
2230             records : records,
2231             totalRecords : records.length
2232         };
2233     },
2234     // used when loading children.. @see loadDataFromChildren
2235     toLoadData: function(rec)
2236     {
2237         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2238         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2239         
2240     }
2241     
2242     
2243 });/*
2244  * Based on:
2245  * Ext JS Library 1.1.1
2246  * Copyright(c) 2006-2007, Ext JS, LLC.
2247  *
2248  * Originally Released Under LGPL - original licence link has changed is not relivant.
2249  *
2250  * Fork - LGPL
2251  * <script type="text/javascript">
2252  */
2253
2254
2255 /**
2256  * @class Roo.data.Tree
2257  * @extends Roo.util.Observable
2258  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2259  * in the tree have most standard DOM functionality.
2260  * @constructor
2261  * @param {Node} root (optional) The root node
2262  */
2263 Roo.data.Tree = function(root){
2264    this.nodeHash = {};
2265    /**
2266     * The root node for this tree
2267     * @type Node
2268     */
2269    this.root = null;
2270    if(root){
2271        this.setRootNode(root);
2272    }
2273    this.addEvents({
2274        /**
2275         * @event append
2276         * Fires when a new child node is appended to a node in this tree.
2277         * @param {Tree} tree The owner tree
2278         * @param {Node} parent The parent node
2279         * @param {Node} node The newly appended node
2280         * @param {Number} index The index of the newly appended node
2281         */
2282        "append" : true,
2283        /**
2284         * @event remove
2285         * Fires when a child node is removed from a node in this tree.
2286         * @param {Tree} tree The owner tree
2287         * @param {Node} parent The parent node
2288         * @param {Node} node The child node removed
2289         */
2290        "remove" : true,
2291        /**
2292         * @event move
2293         * Fires when a node is moved to a new location in the tree
2294         * @param {Tree} tree The owner tree
2295         * @param {Node} node The node moved
2296         * @param {Node} oldParent The old parent of this node
2297         * @param {Node} newParent The new parent of this node
2298         * @param {Number} index The index it was moved to
2299         */
2300        "move" : true,
2301        /**
2302         * @event insert
2303         * Fires when a new child node is inserted in a node in this tree.
2304         * @param {Tree} tree The owner tree
2305         * @param {Node} parent The parent node
2306         * @param {Node} node The child node inserted
2307         * @param {Node} refNode The child node the node was inserted before
2308         */
2309        "insert" : true,
2310        /**
2311         * @event beforeappend
2312         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2313         * @param {Tree} tree The owner tree
2314         * @param {Node} parent The parent node
2315         * @param {Node} node The child node to be appended
2316         */
2317        "beforeappend" : true,
2318        /**
2319         * @event beforeremove
2320         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2321         * @param {Tree} tree The owner tree
2322         * @param {Node} parent The parent node
2323         * @param {Node} node The child node to be removed
2324         */
2325        "beforeremove" : true,
2326        /**
2327         * @event beforemove
2328         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2329         * @param {Tree} tree The owner tree
2330         * @param {Node} node The node being moved
2331         * @param {Node} oldParent The parent of the node
2332         * @param {Node} newParent The new parent the node is moving to
2333         * @param {Number} index The index it is being moved to
2334         */
2335        "beforemove" : true,
2336        /**
2337         * @event beforeinsert
2338         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2339         * @param {Tree} tree The owner tree
2340         * @param {Node} parent The parent node
2341         * @param {Node} node The child node to be inserted
2342         * @param {Node} refNode The child node the node is being inserted before
2343         */
2344        "beforeinsert" : true
2345    });
2346
2347     Roo.data.Tree.superclass.constructor.call(this);
2348 };
2349
2350 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2351     pathSeparator: "/",
2352
2353     proxyNodeEvent : function(){
2354         return this.fireEvent.apply(this, arguments);
2355     },
2356
2357     /**
2358      * Returns the root node for this tree.
2359      * @return {Node}
2360      */
2361     getRootNode : function(){
2362         return this.root;
2363     },
2364
2365     /**
2366      * Sets the root node for this tree.
2367      * @param {Node} node
2368      * @return {Node}
2369      */
2370     setRootNode : function(node){
2371         this.root = node;
2372         node.ownerTree = this;
2373         node.isRoot = true;
2374         this.registerNode(node);
2375         return node;
2376     },
2377
2378     /**
2379      * Gets a node in this tree by its id.
2380      * @param {String} id
2381      * @return {Node}
2382      */
2383     getNodeById : function(id){
2384         return this.nodeHash[id];
2385     },
2386
2387     registerNode : function(node){
2388         this.nodeHash[node.id] = node;
2389     },
2390
2391     unregisterNode : function(node){
2392         delete this.nodeHash[node.id];
2393     },
2394
2395     toString : function(){
2396         return "[Tree"+(this.id?" "+this.id:"")+"]";
2397     }
2398 });
2399
2400 /**
2401  * @class Roo.data.Node
2402  * @extends Roo.util.Observable
2403  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2404  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2405  * @constructor
2406  * @param {Object} attributes The attributes/config for the node
2407  */
2408 Roo.data.Node = function(attributes){
2409     /**
2410      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2411      * @type {Object}
2412      */
2413     this.attributes = attributes || {};
2414     this.leaf = this.attributes.leaf;
2415     /**
2416      * The node id. @type String
2417      */
2418     this.id = this.attributes.id;
2419     if(!this.id){
2420         this.id = Roo.id(null, "ynode-");
2421         this.attributes.id = this.id;
2422     }
2423      
2424     
2425     /**
2426      * All child nodes of this node. @type Array
2427      */
2428     this.childNodes = [];
2429     if(!this.childNodes.indexOf){ // indexOf is a must
2430         this.childNodes.indexOf = function(o){
2431             for(var i = 0, len = this.length; i < len; i++){
2432                 if(this[i] == o) {
2433                     return i;
2434                 }
2435             }
2436             return -1;
2437         };
2438     }
2439     /**
2440      * The parent node for this node. @type Node
2441      */
2442     this.parentNode = null;
2443     /**
2444      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2445      */
2446     this.firstChild = null;
2447     /**
2448      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2449      */
2450     this.lastChild = null;
2451     /**
2452      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2453      */
2454     this.previousSibling = null;
2455     /**
2456      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2457      */
2458     this.nextSibling = null;
2459
2460     this.addEvents({
2461        /**
2462         * @event append
2463         * Fires when a new child node is appended
2464         * @param {Tree} tree The owner tree
2465         * @param {Node} this This node
2466         * @param {Node} node The newly appended node
2467         * @param {Number} index The index of the newly appended node
2468         */
2469        "append" : true,
2470        /**
2471         * @event remove
2472         * Fires when a child node is removed
2473         * @param {Tree} tree The owner tree
2474         * @param {Node} this This node
2475         * @param {Node} node The removed node
2476         */
2477        "remove" : true,
2478        /**
2479         * @event move
2480         * Fires when this node is moved to a new location in the tree
2481         * @param {Tree} tree The owner tree
2482         * @param {Node} this This node
2483         * @param {Node} oldParent The old parent of this node
2484         * @param {Node} newParent The new parent of this node
2485         * @param {Number} index The index it was moved to
2486         */
2487        "move" : true,
2488        /**
2489         * @event insert
2490         * Fires when a new child node is inserted.
2491         * @param {Tree} tree The owner tree
2492         * @param {Node} this This node
2493         * @param {Node} node The child node inserted
2494         * @param {Node} refNode The child node the node was inserted before
2495         */
2496        "insert" : true,
2497        /**
2498         * @event beforeappend
2499         * Fires before a new child is appended, return false to cancel the append.
2500         * @param {Tree} tree The owner tree
2501         * @param {Node} this This node
2502         * @param {Node} node The child node to be appended
2503         */
2504        "beforeappend" : true,
2505        /**
2506         * @event beforeremove
2507         * Fires before a child is removed, return false to cancel the remove.
2508         * @param {Tree} tree The owner tree
2509         * @param {Node} this This node
2510         * @param {Node} node The child node to be removed
2511         */
2512        "beforeremove" : true,
2513        /**
2514         * @event beforemove
2515         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2516         * @param {Tree} tree The owner tree
2517         * @param {Node} this This node
2518         * @param {Node} oldParent The parent of this node
2519         * @param {Node} newParent The new parent this node is moving to
2520         * @param {Number} index The index it is being moved to
2521         */
2522        "beforemove" : true,
2523        /**
2524         * @event beforeinsert
2525         * Fires before a new child is inserted, return false to cancel the insert.
2526         * @param {Tree} tree The owner tree
2527         * @param {Node} this This node
2528         * @param {Node} node The child node to be inserted
2529         * @param {Node} refNode The child node the node is being inserted before
2530         */
2531        "beforeinsert" : true
2532    });
2533     this.listeners = this.attributes.listeners;
2534     Roo.data.Node.superclass.constructor.call(this);
2535 };
2536
2537 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2538     fireEvent : function(evtName){
2539         // first do standard event for this node
2540         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2541             return false;
2542         }
2543         // then bubble it up to the tree if the event wasn't cancelled
2544         var ot = this.getOwnerTree();
2545         if(ot){
2546             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2547                 return false;
2548             }
2549         }
2550         return true;
2551     },
2552
2553     /**
2554      * Returns true if this node is a leaf
2555      * @return {Boolean}
2556      */
2557     isLeaf : function(){
2558         return this.leaf === true;
2559     },
2560
2561     // private
2562     setFirstChild : function(node){
2563         this.firstChild = node;
2564     },
2565
2566     //private
2567     setLastChild : function(node){
2568         this.lastChild = node;
2569     },
2570
2571
2572     /**
2573      * Returns true if this node is the last child of its parent
2574      * @return {Boolean}
2575      */
2576     isLast : function(){
2577        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2578     },
2579
2580     /**
2581      * Returns true if this node is the first child of its parent
2582      * @return {Boolean}
2583      */
2584     isFirst : function(){
2585        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2586     },
2587
2588     hasChildNodes : function(){
2589         return !this.isLeaf() && this.childNodes.length > 0;
2590     },
2591
2592     /**
2593      * Insert node(s) as the last child node of this node.
2594      * @param {Node/Array} node The node or Array of nodes to append
2595      * @return {Node} The appended node if single append, or null if an array was passed
2596      */
2597     appendChild : function(node){
2598         var multi = false;
2599         if(node instanceof Array){
2600             multi = node;
2601         }else if(arguments.length > 1){
2602             multi = arguments;
2603         }
2604         
2605         // if passed an array or multiple args do them one by one
2606         if(multi){
2607             for(var i = 0, len = multi.length; i < len; i++) {
2608                 this.appendChild(multi[i]);
2609             }
2610         }else{
2611             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2612                 return false;
2613             }
2614             var index = this.childNodes.length;
2615             var oldParent = node.parentNode;
2616             // it's a move, make sure we move it cleanly
2617             if(oldParent){
2618                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2619                     return false;
2620                 }
2621                 oldParent.removeChild(node);
2622             }
2623             
2624             index = this.childNodes.length;
2625             if(index == 0){
2626                 this.setFirstChild(node);
2627             }
2628             this.childNodes.push(node);
2629             node.parentNode = this;
2630             var ps = this.childNodes[index-1];
2631             if(ps){
2632                 node.previousSibling = ps;
2633                 ps.nextSibling = node;
2634             }else{
2635                 node.previousSibling = null;
2636             }
2637             node.nextSibling = null;
2638             this.setLastChild(node);
2639             node.setOwnerTree(this.getOwnerTree());
2640             this.fireEvent("append", this.ownerTree, this, node, index);
2641             if(this.ownerTree) {
2642                 this.ownerTree.fireEvent("appendnode", this, node, index);
2643             }
2644             if(oldParent){
2645                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2646             }
2647             return node;
2648         }
2649     },
2650
2651     /**
2652      * Removes a child node from this node.
2653      * @param {Node} node The node to remove
2654      * @return {Node} The removed node
2655      */
2656     removeChild : function(node){
2657         var index = this.childNodes.indexOf(node);
2658         if(index == -1){
2659             return false;
2660         }
2661         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2662             return false;
2663         }
2664
2665         // remove it from childNodes collection
2666         this.childNodes.splice(index, 1);
2667
2668         // update siblings
2669         if(node.previousSibling){
2670             node.previousSibling.nextSibling = node.nextSibling;
2671         }
2672         if(node.nextSibling){
2673             node.nextSibling.previousSibling = node.previousSibling;
2674         }
2675
2676         // update child refs
2677         if(this.firstChild == node){
2678             this.setFirstChild(node.nextSibling);
2679         }
2680         if(this.lastChild == node){
2681             this.setLastChild(node.previousSibling);
2682         }
2683
2684         node.setOwnerTree(null);
2685         // clear any references from the node
2686         node.parentNode = null;
2687         node.previousSibling = null;
2688         node.nextSibling = null;
2689         this.fireEvent("remove", this.ownerTree, this, node);
2690         return node;
2691     },
2692
2693     /**
2694      * Inserts the first node before the second node in this nodes childNodes collection.
2695      * @param {Node} node The node to insert
2696      * @param {Node} refNode The node to insert before (if null the node is appended)
2697      * @return {Node} The inserted node
2698      */
2699     insertBefore : function(node, refNode){
2700         if(!refNode){ // like standard Dom, refNode can be null for append
2701             return this.appendChild(node);
2702         }
2703         // nothing to do
2704         if(node == refNode){
2705             return false;
2706         }
2707
2708         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2709             return false;
2710         }
2711         var index = this.childNodes.indexOf(refNode);
2712         var oldParent = node.parentNode;
2713         var refIndex = index;
2714
2715         // when moving internally, indexes will change after remove
2716         if(oldParent == this && this.childNodes.indexOf(node) < index){
2717             refIndex--;
2718         }
2719
2720         // it's a move, make sure we move it cleanly
2721         if(oldParent){
2722             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2723                 return false;
2724             }
2725             oldParent.removeChild(node);
2726         }
2727         if(refIndex == 0){
2728             this.setFirstChild(node);
2729         }
2730         this.childNodes.splice(refIndex, 0, node);
2731         node.parentNode = this;
2732         var ps = this.childNodes[refIndex-1];
2733         if(ps){
2734             node.previousSibling = ps;
2735             ps.nextSibling = node;
2736         }else{
2737             node.previousSibling = null;
2738         }
2739         node.nextSibling = refNode;
2740         refNode.previousSibling = node;
2741         node.setOwnerTree(this.getOwnerTree());
2742         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2743         if(oldParent){
2744             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2745         }
2746         return node;
2747     },
2748
2749     /**
2750      * Returns the child node at the specified index.
2751      * @param {Number} index
2752      * @return {Node}
2753      */
2754     item : function(index){
2755         return this.childNodes[index];
2756     },
2757
2758     /**
2759      * Replaces one child node in this node with another.
2760      * @param {Node} newChild The replacement node
2761      * @param {Node} oldChild The node to replace
2762      * @return {Node} The replaced node
2763      */
2764     replaceChild : function(newChild, oldChild){
2765         this.insertBefore(newChild, oldChild);
2766         this.removeChild(oldChild);
2767         return oldChild;
2768     },
2769
2770     /**
2771      * Returns the index of a child node
2772      * @param {Node} node
2773      * @return {Number} The index of the node or -1 if it was not found
2774      */
2775     indexOf : function(child){
2776         return this.childNodes.indexOf(child);
2777     },
2778
2779     /**
2780      * Returns the tree this node is in.
2781      * @return {Tree}
2782      */
2783     getOwnerTree : function(){
2784         // if it doesn't have one, look for one
2785         if(!this.ownerTree){
2786             var p = this;
2787             while(p){
2788                 if(p.ownerTree){
2789                     this.ownerTree = p.ownerTree;
2790                     break;
2791                 }
2792                 p = p.parentNode;
2793             }
2794         }
2795         return this.ownerTree;
2796     },
2797
2798     /**
2799      * Returns depth of this node (the root node has a depth of 0)
2800      * @return {Number}
2801      */
2802     getDepth : function(){
2803         var depth = 0;
2804         var p = this;
2805         while(p.parentNode){
2806             ++depth;
2807             p = p.parentNode;
2808         }
2809         return depth;
2810     },
2811
2812     // private
2813     setOwnerTree : function(tree){
2814         // if it's move, we need to update everyone
2815         if(tree != this.ownerTree){
2816             if(this.ownerTree){
2817                 this.ownerTree.unregisterNode(this);
2818             }
2819             this.ownerTree = tree;
2820             var cs = this.childNodes;
2821             for(var i = 0, len = cs.length; i < len; i++) {
2822                 cs[i].setOwnerTree(tree);
2823             }
2824             if(tree){
2825                 tree.registerNode(this);
2826             }
2827         }
2828     },
2829
2830     /**
2831      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2832      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2833      * @return {String} The path
2834      */
2835     getPath : function(attr){
2836         attr = attr || "id";
2837         var p = this.parentNode;
2838         var b = [this.attributes[attr]];
2839         while(p){
2840             b.unshift(p.attributes[attr]);
2841             p = p.parentNode;
2842         }
2843         var sep = this.getOwnerTree().pathSeparator;
2844         return sep + b.join(sep);
2845     },
2846
2847     /**
2848      * Bubbles up the tree from 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 bubble is stopped.
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     bubble : function(fn, scope, args){
2857         var p = this;
2858         while(p){
2859             if(fn.call(scope || p, args || p) === false){
2860                 break;
2861             }
2862             p = p.parentNode;
2863         }
2864     },
2865
2866     /**
2867      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2868      * function call will be the scope provided or the current node. The arguments to the function
2869      * will be the args provided or the current node. If the function returns false at any point,
2870      * the cascade is stopped on that branch.
2871      * @param {Function} fn The function to call
2872      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2873      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2874      */
2875     cascade : function(fn, scope, args){
2876         if(fn.call(scope || this, args || this) !== false){
2877             var cs = this.childNodes;
2878             for(var i = 0, len = cs.length; i < len; i++) {
2879                 cs[i].cascade(fn, scope, args);
2880             }
2881         }
2882     },
2883
2884     /**
2885      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2886      * function call will be the scope provided or the current node. The arguments to the function
2887      * will be the args provided or the current node. If the function returns false at any point,
2888      * the iteration stops.
2889      * @param {Function} fn The function to call
2890      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2891      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2892      */
2893     eachChild : function(fn, scope, args){
2894         var cs = this.childNodes;
2895         for(var i = 0, len = cs.length; i < len; i++) {
2896                 if(fn.call(scope || this, args || cs[i]) === false){
2897                     break;
2898                 }
2899         }
2900     },
2901
2902     /**
2903      * Finds the first child that has the attribute with the specified value.
2904      * @param {String} attribute The attribute name
2905      * @param {Mixed} value The value to search for
2906      * @return {Node} The found child or null if none was found
2907      */
2908     findChild : function(attribute, value){
2909         var cs = this.childNodes;
2910         for(var i = 0, len = cs.length; i < len; i++) {
2911                 if(cs[i].attributes[attribute] == value){
2912                     return cs[i];
2913                 }
2914         }
2915         return null;
2916     },
2917
2918     /**
2919      * Finds the first child by a custom function. The child matches if the function passed
2920      * returns true.
2921      * @param {Function} fn
2922      * @param {Object} scope (optional)
2923      * @return {Node} The found child or null if none was found
2924      */
2925     findChildBy : function(fn, scope){
2926         var cs = this.childNodes;
2927         for(var i = 0, len = cs.length; i < len; i++) {
2928                 if(fn.call(scope||cs[i], cs[i]) === true){
2929                     return cs[i];
2930                 }
2931         }
2932         return null;
2933     },
2934
2935     /**
2936      * Sorts this nodes children using the supplied sort function
2937      * @param {Function} fn
2938      * @param {Object} scope (optional)
2939      */
2940     sort : function(fn, scope){
2941         var cs = this.childNodes;
2942         var len = cs.length;
2943         if(len > 0){
2944             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2945             cs.sort(sortFn);
2946             for(var i = 0; i < len; i++){
2947                 var n = cs[i];
2948                 n.previousSibling = cs[i-1];
2949                 n.nextSibling = cs[i+1];
2950                 if(i == 0){
2951                     this.setFirstChild(n);
2952                 }
2953                 if(i == len-1){
2954                     this.setLastChild(n);
2955                 }
2956             }
2957         }
2958     },
2959
2960     /**
2961      * Returns true if this node is an ancestor (at any point) of the passed node.
2962      * @param {Node} node
2963      * @return {Boolean}
2964      */
2965     contains : function(node){
2966         return node.isAncestor(this);
2967     },
2968
2969     /**
2970      * Returns true if the passed node is an ancestor (at any point) of this node.
2971      * @param {Node} node
2972      * @return {Boolean}
2973      */
2974     isAncestor : function(node){
2975         var p = this.parentNode;
2976         while(p){
2977             if(p == node){
2978                 return true;
2979             }
2980             p = p.parentNode;
2981         }
2982         return false;
2983     },
2984
2985     toString : function(){
2986         return "[Node"+(this.id?" "+this.id:"")+"]";
2987     }
2988 });/*
2989  * Based on:
2990  * Ext JS Library 1.1.1
2991  * Copyright(c) 2006-2007, Ext JS, LLC.
2992  *
2993  * Originally Released Under LGPL - original licence link has changed is not relivant.
2994  *
2995  * Fork - LGPL
2996  * <script type="text/javascript">
2997  */
2998
2999
3000 /**
3001  * @class Roo.Shadow
3002  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3003  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3004  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3005  * @constructor
3006  * Create a new Shadow
3007  * @param {Object} config The config object
3008  */
3009 Roo.Shadow = function(config){
3010     Roo.apply(this, config);
3011     if(typeof this.mode != "string"){
3012         this.mode = this.defaultMode;
3013     }
3014     var o = this.offset, a = {h: 0};
3015     var rad = Math.floor(this.offset/2);
3016     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3017         case "drop":
3018             a.w = 0;
3019             a.l = a.t = o;
3020             a.t -= 1;
3021             if(Roo.isIE){
3022                 a.l -= this.offset + rad;
3023                 a.t -= this.offset + rad;
3024                 a.w -= rad;
3025                 a.h -= rad;
3026                 a.t += 1;
3027             }
3028         break;
3029         case "sides":
3030             a.w = (o*2);
3031             a.l = -o;
3032             a.t = o-1;
3033             if(Roo.isIE){
3034                 a.l -= (this.offset - rad);
3035                 a.t -= this.offset + rad;
3036                 a.l += 1;
3037                 a.w -= (this.offset - rad)*2;
3038                 a.w -= rad + 1;
3039                 a.h -= 1;
3040             }
3041         break;
3042         case "frame":
3043             a.w = a.h = (o*2);
3044             a.l = a.t = -o;
3045             a.t += 1;
3046             a.h -= 2;
3047             if(Roo.isIE){
3048                 a.l -= (this.offset - rad);
3049                 a.t -= (this.offset - rad);
3050                 a.l += 1;
3051                 a.w -= (this.offset + rad + 1);
3052                 a.h -= (this.offset + rad);
3053                 a.h += 1;
3054             }
3055         break;
3056     };
3057
3058     this.adjusts = a;
3059 };
3060
3061 Roo.Shadow.prototype = {
3062     /**
3063      * @cfg {String} mode
3064      * The shadow display mode.  Supports the following options:<br />
3065      * sides: Shadow displays on both sides and bottom only<br />
3066      * frame: Shadow displays equally on all four sides<br />
3067      * drop: Traditional bottom-right drop shadow (default)
3068      */
3069     mode: false,
3070     /**
3071      * @cfg {String} offset
3072      * The number of pixels to offset the shadow from the element (defaults to 4)
3073      */
3074     offset: 4,
3075
3076     // private
3077     defaultMode: "drop",
3078
3079     /**
3080      * Displays the shadow under the target element
3081      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3082      */
3083     show : function(target){
3084         target = Roo.get(target);
3085         if(!this.el){
3086             this.el = Roo.Shadow.Pool.pull();
3087             if(this.el.dom.nextSibling != target.dom){
3088                 this.el.insertBefore(target);
3089             }
3090         }
3091         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3092         if(Roo.isIE){
3093             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3094         }
3095         this.realign(
3096             target.getLeft(true),
3097             target.getTop(true),
3098             target.getWidth(),
3099             target.getHeight()
3100         );
3101         this.el.dom.style.display = "block";
3102     },
3103
3104     /**
3105      * Returns true if the shadow is visible, else false
3106      */
3107     isVisible : function(){
3108         return this.el ? true : false;  
3109     },
3110
3111     /**
3112      * Direct alignment when values are already available. Show must be called at least once before
3113      * calling this method to ensure it is initialized.
3114      * @param {Number} left The target element left position
3115      * @param {Number} top The target element top position
3116      * @param {Number} width The target element width
3117      * @param {Number} height The target element height
3118      */
3119     realign : function(l, t, w, h){
3120         if(!this.el){
3121             return;
3122         }
3123         var a = this.adjusts, d = this.el.dom, s = d.style;
3124         var iea = 0;
3125         s.left = (l+a.l)+"px";
3126         s.top = (t+a.t)+"px";
3127         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3128  
3129         if(s.width != sws || s.height != shs){
3130             s.width = sws;
3131             s.height = shs;
3132             if(!Roo.isIE){
3133                 var cn = d.childNodes;
3134                 var sww = Math.max(0, (sw-12))+"px";
3135                 cn[0].childNodes[1].style.width = sww;
3136                 cn[1].childNodes[1].style.width = sww;
3137                 cn[2].childNodes[1].style.width = sww;
3138                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3139             }
3140         }
3141     },
3142
3143     /**
3144      * Hides this shadow
3145      */
3146     hide : function(){
3147         if(this.el){
3148             this.el.dom.style.display = "none";
3149             Roo.Shadow.Pool.push(this.el);
3150             delete this.el;
3151         }
3152     },
3153
3154     /**
3155      * Adjust the z-index of this shadow
3156      * @param {Number} zindex The new z-index
3157      */
3158     setZIndex : function(z){
3159         this.zIndex = z;
3160         if(this.el){
3161             this.el.setStyle("z-index", z);
3162         }
3163     }
3164 };
3165
3166 // Private utility class that manages the internal Shadow cache
3167 Roo.Shadow.Pool = function(){
3168     var p = [];
3169     var markup = Roo.isIE ?
3170                  '<div class="x-ie-shadow"></div>' :
3171                  '<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>';
3172     return {
3173         pull : function(){
3174             var sh = p.shift();
3175             if(!sh){
3176                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3177                 sh.autoBoxAdjust = false;
3178             }
3179             return sh;
3180         },
3181
3182         push : function(sh){
3183             p.push(sh);
3184         }
3185     };
3186 }();/*
3187  * Based on:
3188  * Ext JS Library 1.1.1
3189  * Copyright(c) 2006-2007, Ext JS, LLC.
3190  *
3191  * Originally Released Under LGPL - original licence link has changed is not relivant.
3192  *
3193  * Fork - LGPL
3194  * <script type="text/javascript">
3195  */
3196
3197
3198 /**
3199  * @class Roo.SplitBar
3200  * @extends Roo.util.Observable
3201  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3202  * <br><br>
3203  * Usage:
3204  * <pre><code>
3205 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3206                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3207 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3208 split.minSize = 100;
3209 split.maxSize = 600;
3210 split.animate = true;
3211 split.on('moved', splitterMoved);
3212 </code></pre>
3213  * @constructor
3214  * Create a new SplitBar
3215  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3216  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3217  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3218  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3219                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3220                         position of the SplitBar).
3221  */
3222 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3223     
3224     /** @private */
3225     this.el = Roo.get(dragElement, true);
3226     this.el.dom.unselectable = "on";
3227     /** @private */
3228     this.resizingEl = Roo.get(resizingElement, true);
3229
3230     /**
3231      * @private
3232      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3233      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3234      * @type Number
3235      */
3236     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3237     
3238     /**
3239      * The minimum size of the resizing element. (Defaults to 0)
3240      * @type Number
3241      */
3242     this.minSize = 0;
3243     
3244     /**
3245      * The maximum size of the resizing element. (Defaults to 2000)
3246      * @type Number
3247      */
3248     this.maxSize = 2000;
3249     
3250     /**
3251      * Whether to animate the transition to the new size
3252      * @type Boolean
3253      */
3254     this.animate = false;
3255     
3256     /**
3257      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3258      * @type Boolean
3259      */
3260     this.useShim = false;
3261     
3262     /** @private */
3263     this.shim = null;
3264     
3265     if(!existingProxy){
3266         /** @private */
3267         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3268     }else{
3269         this.proxy = Roo.get(existingProxy).dom;
3270     }
3271     /** @private */
3272     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3273     
3274     /** @private */
3275     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3276     
3277     /** @private */
3278     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3279     
3280     /** @private */
3281     this.dragSpecs = {};
3282     
3283     /**
3284      * @private The adapter to use to positon and resize elements
3285      */
3286     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3287     this.adapter.init(this);
3288     
3289     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3290         /** @private */
3291         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3292         this.el.addClass("x-splitbar-h");
3293     }else{
3294         /** @private */
3295         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3296         this.el.addClass("x-splitbar-v");
3297     }
3298     
3299     this.addEvents({
3300         /**
3301          * @event resize
3302          * Fires when the splitter is moved (alias for {@link #event-moved})
3303          * @param {Roo.SplitBar} this
3304          * @param {Number} newSize the new width or height
3305          */
3306         "resize" : true,
3307         /**
3308          * @event moved
3309          * Fires when the splitter is moved
3310          * @param {Roo.SplitBar} this
3311          * @param {Number} newSize the new width or height
3312          */
3313         "moved" : true,
3314         /**
3315          * @event beforeresize
3316          * Fires before the splitter is dragged
3317          * @param {Roo.SplitBar} this
3318          */
3319         "beforeresize" : true,
3320
3321         "beforeapply" : true
3322     });
3323
3324     Roo.util.Observable.call(this);
3325 };
3326
3327 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3328     onStartProxyDrag : function(x, y){
3329         this.fireEvent("beforeresize", this);
3330         if(!this.overlay){
3331             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3332             o.unselectable();
3333             o.enableDisplayMode("block");
3334             // all splitbars share the same overlay
3335             Roo.SplitBar.prototype.overlay = o;
3336         }
3337         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3338         this.overlay.show();
3339         Roo.get(this.proxy).setDisplayed("block");
3340         var size = this.adapter.getElementSize(this);
3341         this.activeMinSize = this.getMinimumSize();;
3342         this.activeMaxSize = this.getMaximumSize();;
3343         var c1 = size - this.activeMinSize;
3344         var c2 = Math.max(this.activeMaxSize - size, 0);
3345         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3346             this.dd.resetConstraints();
3347             this.dd.setXConstraint(
3348                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3349                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3350             );
3351             this.dd.setYConstraint(0, 0);
3352         }else{
3353             this.dd.resetConstraints();
3354             this.dd.setXConstraint(0, 0);
3355             this.dd.setYConstraint(
3356                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3357                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3358             );
3359          }
3360         this.dragSpecs.startSize = size;
3361         this.dragSpecs.startPoint = [x, y];
3362         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3363     },
3364     
3365     /** 
3366      * @private Called after the drag operation by the DDProxy
3367      */
3368     onEndProxyDrag : function(e){
3369         Roo.get(this.proxy).setDisplayed(false);
3370         var endPoint = Roo.lib.Event.getXY(e);
3371         if(this.overlay){
3372             this.overlay.hide();
3373         }
3374         var newSize;
3375         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3376             newSize = this.dragSpecs.startSize + 
3377                 (this.placement == Roo.SplitBar.LEFT ?
3378                     endPoint[0] - this.dragSpecs.startPoint[0] :
3379                     this.dragSpecs.startPoint[0] - endPoint[0]
3380                 );
3381         }else{
3382             newSize = this.dragSpecs.startSize + 
3383                 (this.placement == Roo.SplitBar.TOP ?
3384                     endPoint[1] - this.dragSpecs.startPoint[1] :
3385                     this.dragSpecs.startPoint[1] - endPoint[1]
3386                 );
3387         }
3388         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3389         if(newSize != this.dragSpecs.startSize){
3390             if(this.fireEvent('beforeapply', this, newSize) !== false){
3391                 this.adapter.setElementSize(this, newSize);
3392                 this.fireEvent("moved", this, newSize);
3393                 this.fireEvent("resize", this, newSize);
3394             }
3395         }
3396     },
3397     
3398     /**
3399      * Get the adapter this SplitBar uses
3400      * @return The adapter object
3401      */
3402     getAdapter : function(){
3403         return this.adapter;
3404     },
3405     
3406     /**
3407      * Set the adapter this SplitBar uses
3408      * @param {Object} adapter A SplitBar adapter object
3409      */
3410     setAdapter : function(adapter){
3411         this.adapter = adapter;
3412         this.adapter.init(this);
3413     },
3414     
3415     /**
3416      * Gets the minimum size for the resizing element
3417      * @return {Number} The minimum size
3418      */
3419     getMinimumSize : function(){
3420         return this.minSize;
3421     },
3422     
3423     /**
3424      * Sets the minimum size for the resizing element
3425      * @param {Number} minSize The minimum size
3426      */
3427     setMinimumSize : function(minSize){
3428         this.minSize = minSize;
3429     },
3430     
3431     /**
3432      * Gets the maximum size for the resizing element
3433      * @return {Number} The maximum size
3434      */
3435     getMaximumSize : function(){
3436         return this.maxSize;
3437     },
3438     
3439     /**
3440      * Sets the maximum size for the resizing element
3441      * @param {Number} maxSize The maximum size
3442      */
3443     setMaximumSize : function(maxSize){
3444         this.maxSize = maxSize;
3445     },
3446     
3447     /**
3448      * Sets the initialize size for the resizing element
3449      * @param {Number} size The initial size
3450      */
3451     setCurrentSize : function(size){
3452         var oldAnimate = this.animate;
3453         this.animate = false;
3454         this.adapter.setElementSize(this, size);
3455         this.animate = oldAnimate;
3456     },
3457     
3458     /**
3459      * Destroy this splitbar. 
3460      * @param {Boolean} removeEl True to remove the element
3461      */
3462     destroy : function(removeEl){
3463         if(this.shim){
3464             this.shim.remove();
3465         }
3466         this.dd.unreg();
3467         this.proxy.parentNode.removeChild(this.proxy);
3468         if(removeEl){
3469             this.el.remove();
3470         }
3471     }
3472 });
3473
3474 /**
3475  * @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.
3476  */
3477 Roo.SplitBar.createProxy = function(dir){
3478     var proxy = new Roo.Element(document.createElement("div"));
3479     proxy.unselectable();
3480     var cls = 'x-splitbar-proxy';
3481     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3482     document.body.appendChild(proxy.dom);
3483     return proxy.dom;
3484 };
3485
3486 /** 
3487  * @class Roo.SplitBar.BasicLayoutAdapter
3488  * Default Adapter. It assumes the splitter and resizing element are not positioned
3489  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3490  */
3491 Roo.SplitBar.BasicLayoutAdapter = function(){
3492 };
3493
3494 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3495     // do nothing for now
3496     init : function(s){
3497     
3498     },
3499     /**
3500      * Called before drag operations to get the current size of the resizing element. 
3501      * @param {Roo.SplitBar} s The SplitBar using this adapter
3502      */
3503      getElementSize : function(s){
3504         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3505             return s.resizingEl.getWidth();
3506         }else{
3507             return s.resizingEl.getHeight();
3508         }
3509     },
3510     
3511     /**
3512      * Called after drag operations to set the size of the resizing element.
3513      * @param {Roo.SplitBar} s The SplitBar using this adapter
3514      * @param {Number} newSize The new size to set
3515      * @param {Function} onComplete A function to be invoked when resizing is complete
3516      */
3517     setElementSize : function(s, newSize, onComplete){
3518         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3519             if(!s.animate){
3520                 s.resizingEl.setWidth(newSize);
3521                 if(onComplete){
3522                     onComplete(s, newSize);
3523                 }
3524             }else{
3525                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3526             }
3527         }else{
3528             
3529             if(!s.animate){
3530                 s.resizingEl.setHeight(newSize);
3531                 if(onComplete){
3532                     onComplete(s, newSize);
3533                 }
3534             }else{
3535                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3536             }
3537         }
3538     }
3539 };
3540
3541 /** 
3542  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3543  * @extends Roo.SplitBar.BasicLayoutAdapter
3544  * Adapter that  moves the splitter element to align with the resized sizing element. 
3545  * Used with an absolute positioned SplitBar.
3546  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3547  * document.body, make sure you assign an id to the body element.
3548  */
3549 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3550     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3551     this.container = Roo.get(container);
3552 };
3553
3554 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3555     init : function(s){
3556         this.basic.init(s);
3557     },
3558     
3559     getElementSize : function(s){
3560         return this.basic.getElementSize(s);
3561     },
3562     
3563     setElementSize : function(s, newSize, onComplete){
3564         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3565     },
3566     
3567     moveSplitter : function(s){
3568         var yes = Roo.SplitBar;
3569         switch(s.placement){
3570             case yes.LEFT:
3571                 s.el.setX(s.resizingEl.getRight());
3572                 break;
3573             case yes.RIGHT:
3574                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3575                 break;
3576             case yes.TOP:
3577                 s.el.setY(s.resizingEl.getBottom());
3578                 break;
3579             case yes.BOTTOM:
3580                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3581                 break;
3582         }
3583     }
3584 };
3585
3586 /**
3587  * Orientation constant - Create a vertical SplitBar
3588  * @static
3589  * @type Number
3590  */
3591 Roo.SplitBar.VERTICAL = 1;
3592
3593 /**
3594  * Orientation constant - Create a horizontal SplitBar
3595  * @static
3596  * @type Number
3597  */
3598 Roo.SplitBar.HORIZONTAL = 2;
3599
3600 /**
3601  * Placement constant - The resizing element is to the left of the splitter element
3602  * @static
3603  * @type Number
3604  */
3605 Roo.SplitBar.LEFT = 1;
3606
3607 /**
3608  * Placement constant - The resizing element is to the right of the splitter element
3609  * @static
3610  * @type Number
3611  */
3612 Roo.SplitBar.RIGHT = 2;
3613
3614 /**
3615  * Placement constant - The resizing element is positioned above the splitter element
3616  * @static
3617  * @type Number
3618  */
3619 Roo.SplitBar.TOP = 3;
3620
3621 /**
3622  * Placement constant - The resizing element is positioned under splitter element
3623  * @static
3624  * @type Number
3625  */
3626 Roo.SplitBar.BOTTOM = 4;
3627 /*
3628  * Based on:
3629  * Ext JS Library 1.1.1
3630  * Copyright(c) 2006-2007, Ext JS, LLC.
3631  *
3632  * Originally Released Under LGPL - original licence link has changed is not relivant.
3633  *
3634  * Fork - LGPL
3635  * <script type="text/javascript">
3636  */
3637
3638 /**
3639  * @class Roo.View
3640  * @extends Roo.util.Observable
3641  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3642  * This class also supports single and multi selection modes. <br>
3643  * Create a data model bound view:
3644  <pre><code>
3645  var store = new Roo.data.Store(...);
3646
3647  var view = new Roo.View({
3648     el : "my-element",
3649     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3650  
3651     singleSelect: true,
3652     selectedClass: "ydataview-selected",
3653     store: store
3654  });
3655
3656  // listen for node click?
3657  view.on("click", function(vw, index, node, e){
3658  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3659  });
3660
3661  // load XML data
3662  dataModel.load("foobar.xml");
3663  </code></pre>
3664  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3665  * <br><br>
3666  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3667  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3668  * 
3669  * Note: old style constructor is still suported (container, template, config)
3670  * 
3671  * @constructor
3672  * Create a new View
3673  * @param {Object} config The config object
3674  * 
3675  */
3676 Roo.View = function(config, depreciated_tpl, depreciated_config){
3677     
3678     this.parent = false;
3679     
3680     if (typeof(depreciated_tpl) == 'undefined') {
3681         // new way.. - universal constructor.
3682         Roo.apply(this, config);
3683         this.el  = Roo.get(this.el);
3684     } else {
3685         // old format..
3686         this.el  = Roo.get(config);
3687         this.tpl = depreciated_tpl;
3688         Roo.apply(this, depreciated_config);
3689     }
3690     this.wrapEl  = this.el.wrap().wrap();
3691     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3692     
3693     
3694     if(typeof(this.tpl) == "string"){
3695         this.tpl = new Roo.Template(this.tpl);
3696     } else {
3697         // support xtype ctors..
3698         this.tpl = new Roo.factory(this.tpl, Roo);
3699     }
3700     
3701     
3702     this.tpl.compile();
3703     
3704     /** @private */
3705     this.addEvents({
3706         /**
3707          * @event beforeclick
3708          * Fires before a click is processed. Returns false to cancel the default action.
3709          * @param {Roo.View} this
3710          * @param {Number} index The index of the target node
3711          * @param {HTMLElement} node The target node
3712          * @param {Roo.EventObject} e The raw event object
3713          */
3714             "beforeclick" : true,
3715         /**
3716          * @event click
3717          * Fires when a template node is clicked.
3718          * @param {Roo.View} this
3719          * @param {Number} index The index of the target node
3720          * @param {HTMLElement} node The target node
3721          * @param {Roo.EventObject} e The raw event object
3722          */
3723             "click" : true,
3724         /**
3725          * @event dblclick
3726          * Fires when a template node is double clicked.
3727          * @param {Roo.View} this
3728          * @param {Number} index The index of the target node
3729          * @param {HTMLElement} node The target node
3730          * @param {Roo.EventObject} e The raw event object
3731          */
3732             "dblclick" : true,
3733         /**
3734          * @event contextmenu
3735          * Fires when a template node is right clicked.
3736          * @param {Roo.View} this
3737          * @param {Number} index The index of the target node
3738          * @param {HTMLElement} node The target node
3739          * @param {Roo.EventObject} e The raw event object
3740          */
3741             "contextmenu" : true,
3742         /**
3743          * @event selectionchange
3744          * Fires when the selected nodes change.
3745          * @param {Roo.View} this
3746          * @param {Array} selections Array of the selected nodes
3747          */
3748             "selectionchange" : true,
3749     
3750         /**
3751          * @event beforeselect
3752          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3753          * @param {Roo.View} this
3754          * @param {HTMLElement} node The node to be selected
3755          * @param {Array} selections Array of currently selected nodes
3756          */
3757             "beforeselect" : true,
3758         /**
3759          * @event preparedata
3760          * Fires on every row to render, to allow you to change the data.
3761          * @param {Roo.View} this
3762          * @param {Object} data to be rendered (change this)
3763          */
3764           "preparedata" : true
3765           
3766           
3767         });
3768
3769
3770
3771     this.el.on({
3772         "click": this.onClick,
3773         "dblclick": this.onDblClick,
3774         "contextmenu": this.onContextMenu,
3775         scope:this
3776     });
3777
3778     this.selections = [];
3779     this.nodes = [];
3780     this.cmp = new Roo.CompositeElementLite([]);
3781     if(this.store){
3782         this.store = Roo.factory(this.store, Roo.data);
3783         this.setStore(this.store, true);
3784     }
3785     
3786     if ( this.footer && this.footer.xtype) {
3787            
3788          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3789         
3790         this.footer.dataSource = this.store;
3791         this.footer.container = fctr;
3792         this.footer = Roo.factory(this.footer, Roo);
3793         fctr.insertFirst(this.el);
3794         
3795         // this is a bit insane - as the paging toolbar seems to detach the el..
3796 //        dom.parentNode.parentNode.parentNode
3797          // they get detached?
3798     }
3799     
3800     
3801     Roo.View.superclass.constructor.call(this);
3802     
3803     
3804 };
3805
3806 Roo.extend(Roo.View, Roo.util.Observable, {
3807     
3808      /**
3809      * @cfg {Roo.data.Store} store Data store to load data from.
3810      */
3811     store : false,
3812     
3813     /**
3814      * @cfg {String|Roo.Element} el The container element.
3815      */
3816     el : '',
3817     
3818     /**
3819      * @cfg {String|Roo.Template} tpl The template used by this View 
3820      */
3821     tpl : false,
3822     /**
3823      * @cfg {String} dataName the named area of the template to use as the data area
3824      *                          Works with domtemplates roo-name="name"
3825      */
3826     dataName: false,
3827     /**
3828      * @cfg {String} selectedClass The css class to add to selected nodes
3829      */
3830     selectedClass : "x-view-selected",
3831      /**
3832      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3833      */
3834     emptyText : "",
3835     
3836     /**
3837      * @cfg {String} text to display on mask (default Loading)
3838      */
3839     mask : false,
3840     /**
3841      * @cfg {Boolean} multiSelect Allow multiple selection
3842      */
3843     multiSelect : false,
3844     /**
3845      * @cfg {Boolean} singleSelect Allow single selection
3846      */
3847     singleSelect:  false,
3848     
3849     /**
3850      * @cfg {Boolean} toggleSelect - selecting 
3851      */
3852     toggleSelect : false,
3853     
3854     /**
3855      * @cfg {Boolean} tickable - selecting 
3856      */
3857     tickable : false,
3858     
3859     /**
3860      * Returns the element this view is bound to.
3861      * @return {Roo.Element}
3862      */
3863     getEl : function(){
3864         return this.wrapEl;
3865     },
3866     
3867     
3868
3869     /**
3870      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3871      */
3872     refresh : function(){
3873         //Roo.log('refresh');
3874         var t = this.tpl;
3875         
3876         // if we are using something like 'domtemplate', then
3877         // the what gets used is:
3878         // t.applySubtemplate(NAME, data, wrapping data..)
3879         // the outer template then get' applied with
3880         //     the store 'extra data'
3881         // and the body get's added to the
3882         //      roo-name="data" node?
3883         //      <span class='roo-tpl-{name}'></span> ?????
3884         
3885         
3886         
3887         this.clearSelections();
3888         this.el.update("");
3889         var html = [];
3890         var records = this.store.getRange();
3891         if(records.length < 1) {
3892             
3893             // is this valid??  = should it render a template??
3894             
3895             this.el.update(this.emptyText);
3896             return;
3897         }
3898         var el = this.el;
3899         if (this.dataName) {
3900             this.el.update(t.apply(this.store.meta)); //????
3901             el = this.el.child('.roo-tpl-' + this.dataName);
3902         }
3903         
3904         for(var i = 0, len = records.length; i < len; i++){
3905             var data = this.prepareData(records[i].data, i, records[i]);
3906             this.fireEvent("preparedata", this, data, i, records[i]);
3907             
3908             var d = Roo.apply({}, data);
3909             
3910             if(this.tickable){
3911                 Roo.apply(d, {'roo-id' : Roo.id()});
3912                 
3913                 var _this = this;
3914             
3915                 Roo.each(this.parent.item, function(item){
3916                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3917                         return;
3918                     }
3919                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3920                 });
3921             }
3922             
3923             html[html.length] = Roo.util.Format.trim(
3924                 this.dataName ?
3925                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3926                     t.apply(d)
3927             );
3928         }
3929         
3930         
3931         
3932         el.update(html.join(""));
3933         this.nodes = el.dom.childNodes;
3934         this.updateIndexes(0);
3935     },
3936     
3937
3938     /**
3939      * Function to override to reformat the data that is sent to
3940      * the template for each node.
3941      * DEPRICATED - use the preparedata event handler.
3942      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3943      * a JSON object for an UpdateManager bound view).
3944      */
3945     prepareData : function(data, index, record)
3946     {
3947         this.fireEvent("preparedata", this, data, index, record);
3948         return data;
3949     },
3950
3951     onUpdate : function(ds, record){
3952         // Roo.log('on update');   
3953         this.clearSelections();
3954         var index = this.store.indexOf(record);
3955         var n = this.nodes[index];
3956         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3957         n.parentNode.removeChild(n);
3958         this.updateIndexes(index, index);
3959     },
3960
3961     
3962     
3963 // --------- FIXME     
3964     onAdd : function(ds, records, index)
3965     {
3966         //Roo.log(['on Add', ds, records, index] );        
3967         this.clearSelections();
3968         if(this.nodes.length == 0){
3969             this.refresh();
3970             return;
3971         }
3972         var n = this.nodes[index];
3973         for(var i = 0, len = records.length; i < len; i++){
3974             var d = this.prepareData(records[i].data, i, records[i]);
3975             if(n){
3976                 this.tpl.insertBefore(n, d);
3977             }else{
3978                 
3979                 this.tpl.append(this.el, d);
3980             }
3981         }
3982         this.updateIndexes(index);
3983     },
3984
3985     onRemove : function(ds, record, index){
3986        // Roo.log('onRemove');
3987         this.clearSelections();
3988         var el = this.dataName  ?
3989             this.el.child('.roo-tpl-' + this.dataName) :
3990             this.el; 
3991         
3992         el.dom.removeChild(this.nodes[index]);
3993         this.updateIndexes(index);
3994     },
3995
3996     /**
3997      * Refresh an individual node.
3998      * @param {Number} index
3999      */
4000     refreshNode : function(index){
4001         this.onUpdate(this.store, this.store.getAt(index));
4002     },
4003
4004     updateIndexes : function(startIndex, endIndex){
4005         var ns = this.nodes;
4006         startIndex = startIndex || 0;
4007         endIndex = endIndex || ns.length - 1;
4008         for(var i = startIndex; i <= endIndex; i++){
4009             ns[i].nodeIndex = i;
4010         }
4011     },
4012
4013     /**
4014      * Changes the data store this view uses and refresh the view.
4015      * @param {Store} store
4016      */
4017     setStore : function(store, initial){
4018         if(!initial && this.store){
4019             this.store.un("datachanged", this.refresh);
4020             this.store.un("add", this.onAdd);
4021             this.store.un("remove", this.onRemove);
4022             this.store.un("update", this.onUpdate);
4023             this.store.un("clear", this.refresh);
4024             this.store.un("beforeload", this.onBeforeLoad);
4025             this.store.un("load", this.onLoad);
4026             this.store.un("loadexception", this.onLoad);
4027         }
4028         if(store){
4029           
4030             store.on("datachanged", this.refresh, this);
4031             store.on("add", this.onAdd, this);
4032             store.on("remove", this.onRemove, this);
4033             store.on("update", this.onUpdate, this);
4034             store.on("clear", this.refresh, this);
4035             store.on("beforeload", this.onBeforeLoad, this);
4036             store.on("load", this.onLoad, this);
4037             store.on("loadexception", this.onLoad, this);
4038         }
4039         
4040         if(store){
4041             this.refresh();
4042         }
4043     },
4044     /**
4045      * onbeforeLoad - masks the loading area.
4046      *
4047      */
4048     onBeforeLoad : function(store,opts)
4049     {
4050          //Roo.log('onBeforeLoad');   
4051         if (!opts.add) {
4052             this.el.update("");
4053         }
4054         this.el.mask(this.mask ? this.mask : "Loading" ); 
4055     },
4056     onLoad : function ()
4057     {
4058         this.el.unmask();
4059     },
4060     
4061
4062     /**
4063      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4064      * @param {HTMLElement} node
4065      * @return {HTMLElement} The template node
4066      */
4067     findItemFromChild : function(node){
4068         var el = this.dataName  ?
4069             this.el.child('.roo-tpl-' + this.dataName,true) :
4070             this.el.dom; 
4071         
4072         if(!node || node.parentNode == el){
4073                     return node;
4074             }
4075             var p = node.parentNode;
4076             while(p && p != el){
4077             if(p.parentNode == el){
4078                 return p;
4079             }
4080             p = p.parentNode;
4081         }
4082             return null;
4083     },
4084
4085     /** @ignore */
4086     onClick : function(e){
4087         var item = this.findItemFromChild(e.getTarget());
4088         if(item){
4089             var index = this.indexOf(item);
4090             if(this.onItemClick(item, index, e) !== false){
4091                 this.fireEvent("click", this, index, item, e);
4092             }
4093         }else{
4094             this.clearSelections();
4095         }
4096     },
4097
4098     /** @ignore */
4099     onContextMenu : function(e){
4100         var item = this.findItemFromChild(e.getTarget());
4101         if(item){
4102             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4103         }
4104     },
4105
4106     /** @ignore */
4107     onDblClick : function(e){
4108         var item = this.findItemFromChild(e.getTarget());
4109         if(item){
4110             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4111         }
4112     },
4113
4114     onItemClick : function(item, index, e)
4115     {
4116         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4117             return false;
4118         }
4119         if (this.toggleSelect) {
4120             var m = this.isSelected(item) ? 'unselect' : 'select';
4121             //Roo.log(m);
4122             var _t = this;
4123             _t[m](item, true, false);
4124             return true;
4125         }
4126         if(this.multiSelect || this.singleSelect){
4127             if(this.multiSelect && e.shiftKey && this.lastSelection){
4128                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4129             }else{
4130                 this.select(item, this.multiSelect && e.ctrlKey);
4131                 this.lastSelection = item;
4132             }
4133             
4134             if(!this.tickable){
4135                 e.preventDefault();
4136             }
4137             
4138         }
4139         return true;
4140     },
4141
4142     /**
4143      * Get the number of selected nodes.
4144      * @return {Number}
4145      */
4146     getSelectionCount : function(){
4147         return this.selections.length;
4148     },
4149
4150     /**
4151      * Get the currently selected nodes.
4152      * @return {Array} An array of HTMLElements
4153      */
4154     getSelectedNodes : function(){
4155         return this.selections;
4156     },
4157
4158     /**
4159      * Get the indexes of the selected nodes.
4160      * @return {Array}
4161      */
4162     getSelectedIndexes : function(){
4163         var indexes = [], s = this.selections;
4164         for(var i = 0, len = s.length; i < len; i++){
4165             indexes.push(s[i].nodeIndex);
4166         }
4167         return indexes;
4168     },
4169
4170     /**
4171      * Clear all selections
4172      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4173      */
4174     clearSelections : function(suppressEvent){
4175         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4176             this.cmp.elements = this.selections;
4177             this.cmp.removeClass(this.selectedClass);
4178             this.selections = [];
4179             if(!suppressEvent){
4180                 this.fireEvent("selectionchange", this, this.selections);
4181             }
4182         }
4183     },
4184
4185     /**
4186      * Returns true if the passed node is selected
4187      * @param {HTMLElement/Number} node The node or node index
4188      * @return {Boolean}
4189      */
4190     isSelected : function(node){
4191         var s = this.selections;
4192         if(s.length < 1){
4193             return false;
4194         }
4195         node = this.getNode(node);
4196         return s.indexOf(node) !== -1;
4197     },
4198
4199     /**
4200      * Selects nodes.
4201      * @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
4202      * @param {Boolean} keepExisting (optional) true to keep existing selections
4203      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4204      */
4205     select : function(nodeInfo, keepExisting, suppressEvent){
4206         if(nodeInfo instanceof Array){
4207             if(!keepExisting){
4208                 this.clearSelections(true);
4209             }
4210             for(var i = 0, len = nodeInfo.length; i < len; i++){
4211                 this.select(nodeInfo[i], true, true);
4212             }
4213             return;
4214         } 
4215         var node = this.getNode(nodeInfo);
4216         if(!node || this.isSelected(node)){
4217             return; // already selected.
4218         }
4219         if(!keepExisting){
4220             this.clearSelections(true);
4221         }
4222         
4223         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4224             Roo.fly(node).addClass(this.selectedClass);
4225             this.selections.push(node);
4226             if(!suppressEvent){
4227                 this.fireEvent("selectionchange", this, this.selections);
4228             }
4229         }
4230         
4231         
4232     },
4233       /**
4234      * Unselects nodes.
4235      * @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
4236      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4237      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4238      */
4239     unselect : function(nodeInfo, keepExisting, suppressEvent)
4240     {
4241         if(nodeInfo instanceof Array){
4242             Roo.each(this.selections, function(s) {
4243                 this.unselect(s, nodeInfo);
4244             }, this);
4245             return;
4246         }
4247         var node = this.getNode(nodeInfo);
4248         if(!node || !this.isSelected(node)){
4249             //Roo.log("not selected");
4250             return; // not selected.
4251         }
4252         // fireevent???
4253         var ns = [];
4254         Roo.each(this.selections, function(s) {
4255             if (s == node ) {
4256                 Roo.fly(node).removeClass(this.selectedClass);
4257
4258                 return;
4259             }
4260             ns.push(s);
4261         },this);
4262         
4263         this.selections= ns;
4264         this.fireEvent("selectionchange", this, this.selections);
4265     },
4266
4267     /**
4268      * Gets a template node.
4269      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4270      * @return {HTMLElement} The node or null if it wasn't found
4271      */
4272     getNode : function(nodeInfo){
4273         if(typeof nodeInfo == "string"){
4274             return document.getElementById(nodeInfo);
4275         }else if(typeof nodeInfo == "number"){
4276             return this.nodes[nodeInfo];
4277         }
4278         return nodeInfo;
4279     },
4280
4281     /**
4282      * Gets a range template nodes.
4283      * @param {Number} startIndex
4284      * @param {Number} endIndex
4285      * @return {Array} An array of nodes
4286      */
4287     getNodes : function(start, end){
4288         var ns = this.nodes;
4289         start = start || 0;
4290         end = typeof end == "undefined" ? ns.length - 1 : end;
4291         var nodes = [];
4292         if(start <= end){
4293             for(var i = start; i <= end; i++){
4294                 nodes.push(ns[i]);
4295             }
4296         } else{
4297             for(var i = start; i >= end; i--){
4298                 nodes.push(ns[i]);
4299             }
4300         }
4301         return nodes;
4302     },
4303
4304     /**
4305      * Finds the index of the passed node
4306      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4307      * @return {Number} The index of the node or -1
4308      */
4309     indexOf : function(node){
4310         node = this.getNode(node);
4311         if(typeof node.nodeIndex == "number"){
4312             return node.nodeIndex;
4313         }
4314         var ns = this.nodes;
4315         for(var i = 0, len = ns.length; i < len; i++){
4316             if(ns[i] == node){
4317                 return i;
4318             }
4319         }
4320         return -1;
4321     }
4322 });
4323 /*
4324  * Based on:
4325  * Ext JS Library 1.1.1
4326  * Copyright(c) 2006-2007, Ext JS, LLC.
4327  *
4328  * Originally Released Under LGPL - original licence link has changed is not relivant.
4329  *
4330  * Fork - LGPL
4331  * <script type="text/javascript">
4332  */
4333
4334 /**
4335  * @class Roo.JsonView
4336  * @extends Roo.View
4337  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4338 <pre><code>
4339 var view = new Roo.JsonView({
4340     container: "my-element",
4341     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4342     multiSelect: true, 
4343     jsonRoot: "data" 
4344 });
4345
4346 // listen for node click?
4347 view.on("click", function(vw, index, node, e){
4348     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4349 });
4350
4351 // direct load of JSON data
4352 view.load("foobar.php");
4353
4354 // Example from my blog list
4355 var tpl = new Roo.Template(
4356     '&lt;div class="entry"&gt;' +
4357     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4358     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4359     "&lt;/div&gt;&lt;hr /&gt;"
4360 );
4361
4362 var moreView = new Roo.JsonView({
4363     container :  "entry-list", 
4364     template : tpl,
4365     jsonRoot: "posts"
4366 });
4367 moreView.on("beforerender", this.sortEntries, this);
4368 moreView.load({
4369     url: "/blog/get-posts.php",
4370     params: "allposts=true",
4371     text: "Loading Blog Entries..."
4372 });
4373 </code></pre>
4374
4375 * Note: old code is supported with arguments : (container, template, config)
4376
4377
4378  * @constructor
4379  * Create a new JsonView
4380  * 
4381  * @param {Object} config The config object
4382  * 
4383  */
4384 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4385     
4386     
4387     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4388
4389     var um = this.el.getUpdateManager();
4390     um.setRenderer(this);
4391     um.on("update", this.onLoad, this);
4392     um.on("failure", this.onLoadException, this);
4393
4394     /**
4395      * @event beforerender
4396      * Fires before rendering of the downloaded JSON data.
4397      * @param {Roo.JsonView} this
4398      * @param {Object} data The JSON data loaded
4399      */
4400     /**
4401      * @event load
4402      * Fires when data is loaded.
4403      * @param {Roo.JsonView} this
4404      * @param {Object} data The JSON data loaded
4405      * @param {Object} response The raw Connect response object
4406      */
4407     /**
4408      * @event loadexception
4409      * Fires when loading fails.
4410      * @param {Roo.JsonView} this
4411      * @param {Object} response The raw Connect response object
4412      */
4413     this.addEvents({
4414         'beforerender' : true,
4415         'load' : true,
4416         'loadexception' : true
4417     });
4418 };
4419 Roo.extend(Roo.JsonView, Roo.View, {
4420     /**
4421      * @type {String} The root property in the loaded JSON object that contains the data
4422      */
4423     jsonRoot : "",
4424
4425     /**
4426      * Refreshes the view.
4427      */
4428     refresh : function(){
4429         this.clearSelections();
4430         this.el.update("");
4431         var html = [];
4432         var o = this.jsonData;
4433         if(o && o.length > 0){
4434             for(var i = 0, len = o.length; i < len; i++){
4435                 var data = this.prepareData(o[i], i, o);
4436                 html[html.length] = this.tpl.apply(data);
4437             }
4438         }else{
4439             html.push(this.emptyText);
4440         }
4441         this.el.update(html.join(""));
4442         this.nodes = this.el.dom.childNodes;
4443         this.updateIndexes(0);
4444     },
4445
4446     /**
4447      * 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.
4448      * @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:
4449      <pre><code>
4450      view.load({
4451          url: "your-url.php",
4452          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4453          callback: yourFunction,
4454          scope: yourObject, //(optional scope)
4455          discardUrl: false,
4456          nocache: false,
4457          text: "Loading...",
4458          timeout: 30,
4459          scripts: false
4460      });
4461      </code></pre>
4462      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4463      * 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.
4464      * @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}
4465      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4466      * @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.
4467      */
4468     load : function(){
4469         var um = this.el.getUpdateManager();
4470         um.update.apply(um, arguments);
4471     },
4472
4473     // note - render is a standard framework call...
4474     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4475     render : function(el, response){
4476         
4477         this.clearSelections();
4478         this.el.update("");
4479         var o;
4480         try{
4481             if (response != '') {
4482                 o = Roo.util.JSON.decode(response.responseText);
4483                 if(this.jsonRoot){
4484                     
4485                     o = o[this.jsonRoot];
4486                 }
4487             }
4488         } catch(e){
4489         }
4490         /**
4491          * The current JSON data or null
4492          */
4493         this.jsonData = o;
4494         this.beforeRender();
4495         this.refresh();
4496     },
4497
4498 /**
4499  * Get the number of records in the current JSON dataset
4500  * @return {Number}
4501  */
4502     getCount : function(){
4503         return this.jsonData ? this.jsonData.length : 0;
4504     },
4505
4506 /**
4507  * Returns the JSON object for the specified node(s)
4508  * @param {HTMLElement/Array} node The node or an array of nodes
4509  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4510  * you get the JSON object for the node
4511  */
4512     getNodeData : function(node){
4513         if(node instanceof Array){
4514             var data = [];
4515             for(var i = 0, len = node.length; i < len; i++){
4516                 data.push(this.getNodeData(node[i]));
4517             }
4518             return data;
4519         }
4520         return this.jsonData[this.indexOf(node)] || null;
4521     },
4522
4523     beforeRender : function(){
4524         this.snapshot = this.jsonData;
4525         if(this.sortInfo){
4526             this.sort.apply(this, this.sortInfo);
4527         }
4528         this.fireEvent("beforerender", this, this.jsonData);
4529     },
4530
4531     onLoad : function(el, o){
4532         this.fireEvent("load", this, this.jsonData, o);
4533     },
4534
4535     onLoadException : function(el, o){
4536         this.fireEvent("loadexception", this, o);
4537     },
4538
4539 /**
4540  * Filter the data by a specific property.
4541  * @param {String} property A property on your JSON objects
4542  * @param {String/RegExp} value Either string that the property values
4543  * should start with, or a RegExp to test against the property
4544  */
4545     filter : function(property, value){
4546         if(this.jsonData){
4547             var data = [];
4548             var ss = this.snapshot;
4549             if(typeof value == "string"){
4550                 var vlen = value.length;
4551                 if(vlen == 0){
4552                     this.clearFilter();
4553                     return;
4554                 }
4555                 value = value.toLowerCase();
4556                 for(var i = 0, len = ss.length; i < len; i++){
4557                     var o = ss[i];
4558                     if(o[property].substr(0, vlen).toLowerCase() == value){
4559                         data.push(o);
4560                     }
4561                 }
4562             } else if(value.exec){ // regex?
4563                 for(var i = 0, len = ss.length; i < len; i++){
4564                     var o = ss[i];
4565                     if(value.test(o[property])){
4566                         data.push(o);
4567                     }
4568                 }
4569             } else{
4570                 return;
4571             }
4572             this.jsonData = data;
4573             this.refresh();
4574         }
4575     },
4576
4577 /**
4578  * Filter by a function. The passed function will be called with each
4579  * object in the current dataset. If the function returns true the value is kept,
4580  * otherwise it is filtered.
4581  * @param {Function} fn
4582  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4583  */
4584     filterBy : function(fn, scope){
4585         if(this.jsonData){
4586             var data = [];
4587             var ss = this.snapshot;
4588             for(var i = 0, len = ss.length; i < len; i++){
4589                 var o = ss[i];
4590                 if(fn.call(scope || this, o)){
4591                     data.push(o);
4592                 }
4593             }
4594             this.jsonData = data;
4595             this.refresh();
4596         }
4597     },
4598
4599 /**
4600  * Clears the current filter.
4601  */
4602     clearFilter : function(){
4603         if(this.snapshot && this.jsonData != this.snapshot){
4604             this.jsonData = this.snapshot;
4605             this.refresh();
4606         }
4607     },
4608
4609
4610 /**
4611  * Sorts the data for this view and refreshes it.
4612  * @param {String} property A property on your JSON objects to sort on
4613  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4614  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4615  */
4616     sort : function(property, dir, sortType){
4617         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4618         if(this.jsonData){
4619             var p = property;
4620             var dsc = dir && dir.toLowerCase() == "desc";
4621             var f = function(o1, o2){
4622                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4623                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4624                 ;
4625                 if(v1 < v2){
4626                     return dsc ? +1 : -1;
4627                 } else if(v1 > v2){
4628                     return dsc ? -1 : +1;
4629                 } else{
4630                     return 0;
4631                 }
4632             };
4633             this.jsonData.sort(f);
4634             this.refresh();
4635             if(this.jsonData != this.snapshot){
4636                 this.snapshot.sort(f);
4637             }
4638         }
4639     }
4640 });/*
4641  * Based on:
4642  * Ext JS Library 1.1.1
4643  * Copyright(c) 2006-2007, Ext JS, LLC.
4644  *
4645  * Originally Released Under LGPL - original licence link has changed is not relivant.
4646  *
4647  * Fork - LGPL
4648  * <script type="text/javascript">
4649  */
4650  
4651
4652 /**
4653  * @class Roo.ColorPalette
4654  * @extends Roo.Component
4655  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4656  * Here's an example of typical usage:
4657  * <pre><code>
4658 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4659 cp.render('my-div');
4660
4661 cp.on('select', function(palette, selColor){
4662     // do something with selColor
4663 });
4664 </code></pre>
4665  * @constructor
4666  * Create a new ColorPalette
4667  * @param {Object} config The config object
4668  */
4669 Roo.ColorPalette = function(config){
4670     Roo.ColorPalette.superclass.constructor.call(this, config);
4671     this.addEvents({
4672         /**
4673              * @event select
4674              * Fires when a color is selected
4675              * @param {ColorPalette} this
4676              * @param {String} color The 6-digit color hex code (without the # symbol)
4677              */
4678         select: true
4679     });
4680
4681     if(this.handler){
4682         this.on("select", this.handler, this.scope, true);
4683     }
4684 };
4685 Roo.extend(Roo.ColorPalette, Roo.Component, {
4686     /**
4687      * @cfg {String} itemCls
4688      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4689      */
4690     itemCls : "x-color-palette",
4691     /**
4692      * @cfg {String} value
4693      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4694      * the hex codes are case-sensitive.
4695      */
4696     value : null,
4697     clickEvent:'click',
4698     // private
4699     ctype: "Roo.ColorPalette",
4700
4701     /**
4702      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4703      */
4704     allowReselect : false,
4705
4706     /**
4707      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4708      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4709      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4710      * of colors with the width setting until the box is symmetrical.</p>
4711      * <p>You can override individual colors if needed:</p>
4712      * <pre><code>
4713 var cp = new Roo.ColorPalette();
4714 cp.colors[0] = "FF0000";  // change the first box to red
4715 </code></pre>
4716
4717 Or you can provide a custom array of your own for complete control:
4718 <pre><code>
4719 var cp = new Roo.ColorPalette();
4720 cp.colors = ["000000", "993300", "333300"];
4721 </code></pre>
4722      * @type Array
4723      */
4724     colors : [
4725         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4726         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4727         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4728         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4729         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4730     ],
4731
4732     // private
4733     onRender : function(container, position){
4734         var t = new Roo.MasterTemplate(
4735             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4736         );
4737         var c = this.colors;
4738         for(var i = 0, len = c.length; i < len; i++){
4739             t.add([c[i]]);
4740         }
4741         var el = document.createElement("div");
4742         el.className = this.itemCls;
4743         t.overwrite(el);
4744         container.dom.insertBefore(el, position);
4745         this.el = Roo.get(el);
4746         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4747         if(this.clickEvent != 'click'){
4748             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4749         }
4750     },
4751
4752     // private
4753     afterRender : function(){
4754         Roo.ColorPalette.superclass.afterRender.call(this);
4755         if(this.value){
4756             var s = this.value;
4757             this.value = null;
4758             this.select(s);
4759         }
4760     },
4761
4762     // private
4763     handleClick : function(e, t){
4764         e.preventDefault();
4765         if(!this.disabled){
4766             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4767             this.select(c.toUpperCase());
4768         }
4769     },
4770
4771     /**
4772      * Selects the specified color in the palette (fires the select event)
4773      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4774      */
4775     select : function(color){
4776         color = color.replace("#", "");
4777         if(color != this.value || this.allowReselect){
4778             var el = this.el;
4779             if(this.value){
4780                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4781             }
4782             el.child("a.color-"+color).addClass("x-color-palette-sel");
4783             this.value = color;
4784             this.fireEvent("select", this, color);
4785         }
4786     }
4787 });/*
4788  * Based on:
4789  * Ext JS Library 1.1.1
4790  * Copyright(c) 2006-2007, Ext JS, LLC.
4791  *
4792  * Originally Released Under LGPL - original licence link has changed is not relivant.
4793  *
4794  * Fork - LGPL
4795  * <script type="text/javascript">
4796  */
4797  
4798 /**
4799  * @class Roo.DatePicker
4800  * @extends Roo.Component
4801  * Simple date picker class.
4802  * @constructor
4803  * Create a new DatePicker
4804  * @param {Object} config The config object
4805  */
4806 Roo.DatePicker = function(config){
4807     Roo.DatePicker.superclass.constructor.call(this, config);
4808
4809     this.value = config && config.value ?
4810                  config.value.clearTime() : new Date().clearTime();
4811
4812     this.addEvents({
4813         /**
4814              * @event select
4815              * Fires when a date is selected
4816              * @param {DatePicker} this
4817              * @param {Date} date The selected date
4818              */
4819         'select': true,
4820         /**
4821              * @event monthchange
4822              * Fires when the displayed month changes 
4823              * @param {DatePicker} this
4824              * @param {Date} date The selected month
4825              */
4826         'monthchange': true
4827     });
4828
4829     if(this.handler){
4830         this.on("select", this.handler,  this.scope || this);
4831     }
4832     // build the disabledDatesRE
4833     if(!this.disabledDatesRE && this.disabledDates){
4834         var dd = this.disabledDates;
4835         var re = "(?:";
4836         for(var i = 0; i < dd.length; i++){
4837             re += dd[i];
4838             if(i != dd.length-1) {
4839                 re += "|";
4840             }
4841         }
4842         this.disabledDatesRE = new RegExp(re + ")");
4843     }
4844 };
4845
4846 Roo.extend(Roo.DatePicker, Roo.Component, {
4847     /**
4848      * @cfg {String} todayText
4849      * The text to display on the button that selects the current date (defaults to "Today")
4850      */
4851     todayText : "Today",
4852     /**
4853      * @cfg {String} okText
4854      * The text to display on the ok button
4855      */
4856     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4857     /**
4858      * @cfg {String} cancelText
4859      * The text to display on the cancel button
4860      */
4861     cancelText : "Cancel",
4862     /**
4863      * @cfg {String} todayTip
4864      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4865      */
4866     todayTip : "{0} (Spacebar)",
4867     /**
4868      * @cfg {Date} minDate
4869      * Minimum allowable date (JavaScript date object, defaults to null)
4870      */
4871     minDate : null,
4872     /**
4873      * @cfg {Date} maxDate
4874      * Maximum allowable date (JavaScript date object, defaults to null)
4875      */
4876     maxDate : null,
4877     /**
4878      * @cfg {String} minText
4879      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4880      */
4881     minText : "This date is before the minimum date",
4882     /**
4883      * @cfg {String} maxText
4884      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4885      */
4886     maxText : "This date is after the maximum date",
4887     /**
4888      * @cfg {String} format
4889      * The default date format string which can be overriden for localization support.  The format must be
4890      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4891      */
4892     format : "m/d/y",
4893     /**
4894      * @cfg {Array} disabledDays
4895      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4896      */
4897     disabledDays : null,
4898     /**
4899      * @cfg {String} disabledDaysText
4900      * The tooltip to display when the date falls on a disabled day (defaults to "")
4901      */
4902     disabledDaysText : "",
4903     /**
4904      * @cfg {RegExp} disabledDatesRE
4905      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4906      */
4907     disabledDatesRE : null,
4908     /**
4909      * @cfg {String} disabledDatesText
4910      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4911      */
4912     disabledDatesText : "",
4913     /**
4914      * @cfg {Boolean} constrainToViewport
4915      * True to constrain the date picker to the viewport (defaults to true)
4916      */
4917     constrainToViewport : true,
4918     /**
4919      * @cfg {Array} monthNames
4920      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4921      */
4922     monthNames : Date.monthNames,
4923     /**
4924      * @cfg {Array} dayNames
4925      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4926      */
4927     dayNames : Date.dayNames,
4928     /**
4929      * @cfg {String} nextText
4930      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4931      */
4932     nextText: 'Next Month (Control+Right)',
4933     /**
4934      * @cfg {String} prevText
4935      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4936      */
4937     prevText: 'Previous Month (Control+Left)',
4938     /**
4939      * @cfg {String} monthYearText
4940      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4941      */
4942     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4943     /**
4944      * @cfg {Number} startDay
4945      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4946      */
4947     startDay : 0,
4948     /**
4949      * @cfg {Bool} showClear
4950      * Show a clear button (usefull for date form elements that can be blank.)
4951      */
4952     
4953     showClear: false,
4954     
4955     /**
4956      * Sets the value of the date field
4957      * @param {Date} value The date to set
4958      */
4959     setValue : function(value){
4960         var old = this.value;
4961         
4962         if (typeof(value) == 'string') {
4963          
4964             value = Date.parseDate(value, this.format);
4965         }
4966         if (!value) {
4967             value = new Date();
4968         }
4969         
4970         this.value = value.clearTime(true);
4971         if(this.el){
4972             this.update(this.value);
4973         }
4974     },
4975
4976     /**
4977      * Gets the current selected value of the date field
4978      * @return {Date} The selected date
4979      */
4980     getValue : function(){
4981         return this.value;
4982     },
4983
4984     // private
4985     focus : function(){
4986         if(this.el){
4987             this.update(this.activeDate);
4988         }
4989     },
4990
4991     // privateval
4992     onRender : function(container, position){
4993         
4994         var m = [
4995              '<table cellspacing="0">',
4996                 '<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>',
4997                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4998         var dn = this.dayNames;
4999         for(var i = 0; i < 7; i++){
5000             var d = this.startDay+i;
5001             if(d > 6){
5002                 d = d-7;
5003             }
5004             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5005         }
5006         m[m.length] = "</tr></thead><tbody><tr>";
5007         for(var i = 0; i < 42; i++) {
5008             if(i % 7 == 0 && i != 0){
5009                 m[m.length] = "</tr><tr>";
5010             }
5011             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5012         }
5013         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5014             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5015
5016         var el = document.createElement("div");
5017         el.className = "x-date-picker";
5018         el.innerHTML = m.join("");
5019
5020         container.dom.insertBefore(el, position);
5021
5022         this.el = Roo.get(el);
5023         this.eventEl = Roo.get(el.firstChild);
5024
5025         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5026             handler: this.showPrevMonth,
5027             scope: this,
5028             preventDefault:true,
5029             stopDefault:true
5030         });
5031
5032         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5033             handler: this.showNextMonth,
5034             scope: this,
5035             preventDefault:true,
5036             stopDefault:true
5037         });
5038
5039         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5040
5041         this.monthPicker = this.el.down('div.x-date-mp');
5042         this.monthPicker.enableDisplayMode('block');
5043         
5044         var kn = new Roo.KeyNav(this.eventEl, {
5045             "left" : function(e){
5046                 e.ctrlKey ?
5047                     this.showPrevMonth() :
5048                     this.update(this.activeDate.add("d", -1));
5049             },
5050
5051             "right" : function(e){
5052                 e.ctrlKey ?
5053                     this.showNextMonth() :
5054                     this.update(this.activeDate.add("d", 1));
5055             },
5056
5057             "up" : function(e){
5058                 e.ctrlKey ?
5059                     this.showNextYear() :
5060                     this.update(this.activeDate.add("d", -7));
5061             },
5062
5063             "down" : function(e){
5064                 e.ctrlKey ?
5065                     this.showPrevYear() :
5066                     this.update(this.activeDate.add("d", 7));
5067             },
5068
5069             "pageUp" : function(e){
5070                 this.showNextMonth();
5071             },
5072
5073             "pageDown" : function(e){
5074                 this.showPrevMonth();
5075             },
5076
5077             "enter" : function(e){
5078                 e.stopPropagation();
5079                 return true;
5080             },
5081
5082             scope : this
5083         });
5084
5085         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5086
5087         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5088
5089         this.el.unselectable();
5090         
5091         this.cells = this.el.select("table.x-date-inner tbody td");
5092         this.textNodes = this.el.query("table.x-date-inner tbody span");
5093
5094         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5095             text: "&#160;",
5096             tooltip: this.monthYearText
5097         });
5098
5099         this.mbtn.on('click', this.showMonthPicker, this);
5100         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5101
5102
5103         var today = (new Date()).dateFormat(this.format);
5104         
5105         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5106         if (this.showClear) {
5107             baseTb.add( new Roo.Toolbar.Fill());
5108         }
5109         baseTb.add({
5110             text: String.format(this.todayText, today),
5111             tooltip: String.format(this.todayTip, today),
5112             handler: this.selectToday,
5113             scope: this
5114         });
5115         
5116         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5117             
5118         //});
5119         if (this.showClear) {
5120             
5121             baseTb.add( new Roo.Toolbar.Fill());
5122             baseTb.add({
5123                 text: '&#160;',
5124                 cls: 'x-btn-icon x-btn-clear',
5125                 handler: function() {
5126                     //this.value = '';
5127                     this.fireEvent("select", this, '');
5128                 },
5129                 scope: this
5130             });
5131         }
5132         
5133         
5134         if(Roo.isIE){
5135             this.el.repaint();
5136         }
5137         this.update(this.value);
5138     },
5139
5140     createMonthPicker : function(){
5141         if(!this.monthPicker.dom.firstChild){
5142             var buf = ['<table border="0" cellspacing="0">'];
5143             for(var i = 0; i < 6; i++){
5144                 buf.push(
5145                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5146                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5147                     i == 0 ?
5148                     '<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>' :
5149                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5150                 );
5151             }
5152             buf.push(
5153                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5154                     this.okText,
5155                     '</button><button type="button" class="x-date-mp-cancel">',
5156                     this.cancelText,
5157                     '</button></td></tr>',
5158                 '</table>'
5159             );
5160             this.monthPicker.update(buf.join(''));
5161             this.monthPicker.on('click', this.onMonthClick, this);
5162             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5163
5164             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5165             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5166
5167             this.mpMonths.each(function(m, a, i){
5168                 i += 1;
5169                 if((i%2) == 0){
5170                     m.dom.xmonth = 5 + Math.round(i * .5);
5171                 }else{
5172                     m.dom.xmonth = Math.round((i-1) * .5);
5173                 }
5174             });
5175         }
5176     },
5177
5178     showMonthPicker : function(){
5179         this.createMonthPicker();
5180         var size = this.el.getSize();
5181         this.monthPicker.setSize(size);
5182         this.monthPicker.child('table').setSize(size);
5183
5184         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5185         this.updateMPMonth(this.mpSelMonth);
5186         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5187         this.updateMPYear(this.mpSelYear);
5188
5189         this.monthPicker.slideIn('t', {duration:.2});
5190     },
5191
5192     updateMPYear : function(y){
5193         this.mpyear = y;
5194         var ys = this.mpYears.elements;
5195         for(var i = 1; i <= 10; i++){
5196             var td = ys[i-1], y2;
5197             if((i%2) == 0){
5198                 y2 = y + Math.round(i * .5);
5199                 td.firstChild.innerHTML = y2;
5200                 td.xyear = y2;
5201             }else{
5202                 y2 = y - (5-Math.round(i * .5));
5203                 td.firstChild.innerHTML = y2;
5204                 td.xyear = y2;
5205             }
5206             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5207         }
5208     },
5209
5210     updateMPMonth : function(sm){
5211         this.mpMonths.each(function(m, a, i){
5212             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5213         });
5214     },
5215
5216     selectMPMonth: function(m){
5217         
5218     },
5219
5220     onMonthClick : function(e, t){
5221         e.stopEvent();
5222         var el = new Roo.Element(t), pn;
5223         if(el.is('button.x-date-mp-cancel')){
5224             this.hideMonthPicker();
5225         }
5226         else if(el.is('button.x-date-mp-ok')){
5227             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5228             this.hideMonthPicker();
5229         }
5230         else if(pn = el.up('td.x-date-mp-month', 2)){
5231             this.mpMonths.removeClass('x-date-mp-sel');
5232             pn.addClass('x-date-mp-sel');
5233             this.mpSelMonth = pn.dom.xmonth;
5234         }
5235         else if(pn = el.up('td.x-date-mp-year', 2)){
5236             this.mpYears.removeClass('x-date-mp-sel');
5237             pn.addClass('x-date-mp-sel');
5238             this.mpSelYear = pn.dom.xyear;
5239         }
5240         else if(el.is('a.x-date-mp-prev')){
5241             this.updateMPYear(this.mpyear-10);
5242         }
5243         else if(el.is('a.x-date-mp-next')){
5244             this.updateMPYear(this.mpyear+10);
5245         }
5246     },
5247
5248     onMonthDblClick : function(e, t){
5249         e.stopEvent();
5250         var el = new Roo.Element(t), pn;
5251         if(pn = el.up('td.x-date-mp-month', 2)){
5252             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5253             this.hideMonthPicker();
5254         }
5255         else if(pn = el.up('td.x-date-mp-year', 2)){
5256             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5257             this.hideMonthPicker();
5258         }
5259     },
5260
5261     hideMonthPicker : function(disableAnim){
5262         if(this.monthPicker){
5263             if(disableAnim === true){
5264                 this.monthPicker.hide();
5265             }else{
5266                 this.monthPicker.slideOut('t', {duration:.2});
5267             }
5268         }
5269     },
5270
5271     // private
5272     showPrevMonth : function(e){
5273         this.update(this.activeDate.add("mo", -1));
5274     },
5275
5276     // private
5277     showNextMonth : function(e){
5278         this.update(this.activeDate.add("mo", 1));
5279     },
5280
5281     // private
5282     showPrevYear : function(){
5283         this.update(this.activeDate.add("y", -1));
5284     },
5285
5286     // private
5287     showNextYear : function(){
5288         this.update(this.activeDate.add("y", 1));
5289     },
5290
5291     // private
5292     handleMouseWheel : function(e){
5293         var delta = e.getWheelDelta();
5294         if(delta > 0){
5295             this.showPrevMonth();
5296             e.stopEvent();
5297         } else if(delta < 0){
5298             this.showNextMonth();
5299             e.stopEvent();
5300         }
5301     },
5302
5303     // private
5304     handleDateClick : function(e, t){
5305         e.stopEvent();
5306         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5307             this.setValue(new Date(t.dateValue));
5308             this.fireEvent("select", this, this.value);
5309         }
5310     },
5311
5312     // private
5313     selectToday : function(){
5314         this.setValue(new Date().clearTime());
5315         this.fireEvent("select", this, this.value);
5316     },
5317
5318     // private
5319     update : function(date)
5320     {
5321         var vd = this.activeDate;
5322         this.activeDate = date;
5323         if(vd && this.el){
5324             var t = date.getTime();
5325             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5326                 this.cells.removeClass("x-date-selected");
5327                 this.cells.each(function(c){
5328                    if(c.dom.firstChild.dateValue == t){
5329                        c.addClass("x-date-selected");
5330                        setTimeout(function(){
5331                             try{c.dom.firstChild.focus();}catch(e){}
5332                        }, 50);
5333                        return false;
5334                    }
5335                 });
5336                 return;
5337             }
5338         }
5339         
5340         var days = date.getDaysInMonth();
5341         var firstOfMonth = date.getFirstDateOfMonth();
5342         var startingPos = firstOfMonth.getDay()-this.startDay;
5343
5344         if(startingPos <= this.startDay){
5345             startingPos += 7;
5346         }
5347
5348         var pm = date.add("mo", -1);
5349         var prevStart = pm.getDaysInMonth()-startingPos;
5350
5351         var cells = this.cells.elements;
5352         var textEls = this.textNodes;
5353         days += startingPos;
5354
5355         // convert everything to numbers so it's fast
5356         var day = 86400000;
5357         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5358         var today = new Date().clearTime().getTime();
5359         var sel = date.clearTime().getTime();
5360         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5361         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5362         var ddMatch = this.disabledDatesRE;
5363         var ddText = this.disabledDatesText;
5364         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5365         var ddaysText = this.disabledDaysText;
5366         var format = this.format;
5367
5368         var setCellClass = function(cal, cell){
5369             cell.title = "";
5370             var t = d.getTime();
5371             cell.firstChild.dateValue = t;
5372             if(t == today){
5373                 cell.className += " x-date-today";
5374                 cell.title = cal.todayText;
5375             }
5376             if(t == sel){
5377                 cell.className += " x-date-selected";
5378                 setTimeout(function(){
5379                     try{cell.firstChild.focus();}catch(e){}
5380                 }, 50);
5381             }
5382             // disabling
5383             if(t < min) {
5384                 cell.className = " x-date-disabled";
5385                 cell.title = cal.minText;
5386                 return;
5387             }
5388             if(t > max) {
5389                 cell.className = " x-date-disabled";
5390                 cell.title = cal.maxText;
5391                 return;
5392             }
5393             if(ddays){
5394                 if(ddays.indexOf(d.getDay()) != -1){
5395                     cell.title = ddaysText;
5396                     cell.className = " x-date-disabled";
5397                 }
5398             }
5399             if(ddMatch && format){
5400                 var fvalue = d.dateFormat(format);
5401                 if(ddMatch.test(fvalue)){
5402                     cell.title = ddText.replace("%0", fvalue);
5403                     cell.className = " x-date-disabled";
5404                 }
5405             }
5406         };
5407
5408         var i = 0;
5409         for(; i < startingPos; i++) {
5410             textEls[i].innerHTML = (++prevStart);
5411             d.setDate(d.getDate()+1);
5412             cells[i].className = "x-date-prevday";
5413             setCellClass(this, cells[i]);
5414         }
5415         for(; i < days; i++){
5416             intDay = i - startingPos + 1;
5417             textEls[i].innerHTML = (intDay);
5418             d.setDate(d.getDate()+1);
5419             cells[i].className = "x-date-active";
5420             setCellClass(this, cells[i]);
5421         }
5422         var extraDays = 0;
5423         for(; i < 42; i++) {
5424              textEls[i].innerHTML = (++extraDays);
5425              d.setDate(d.getDate()+1);
5426              cells[i].className = "x-date-nextday";
5427              setCellClass(this, cells[i]);
5428         }
5429
5430         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5431         this.fireEvent('monthchange', this, date);
5432         
5433         if(!this.internalRender){
5434             var main = this.el.dom.firstChild;
5435             var w = main.offsetWidth;
5436             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5437             Roo.fly(main).setWidth(w);
5438             this.internalRender = true;
5439             // opera does not respect the auto grow header center column
5440             // then, after it gets a width opera refuses to recalculate
5441             // without a second pass
5442             if(Roo.isOpera && !this.secondPass){
5443                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5444                 this.secondPass = true;
5445                 this.update.defer(10, this, [date]);
5446             }
5447         }
5448         
5449         
5450     }
5451 });        /*
5452  * Based on:
5453  * Ext JS Library 1.1.1
5454  * Copyright(c) 2006-2007, Ext JS, LLC.
5455  *
5456  * Originally Released Under LGPL - original licence link has changed is not relivant.
5457  *
5458  * Fork - LGPL
5459  * <script type="text/javascript">
5460  */
5461 /**
5462  * @class Roo.TabPanel
5463  * @extends Roo.util.Observable
5464  * A lightweight tab container.
5465  * <br><br>
5466  * Usage:
5467  * <pre><code>
5468 // basic tabs 1, built from existing content
5469 var tabs = new Roo.TabPanel("tabs1");
5470 tabs.addTab("script", "View Script");
5471 tabs.addTab("markup", "View Markup");
5472 tabs.activate("script");
5473
5474 // more advanced tabs, built from javascript
5475 var jtabs = new Roo.TabPanel("jtabs");
5476 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5477
5478 // set up the UpdateManager
5479 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5480 var updater = tab2.getUpdateManager();
5481 updater.setDefaultUrl("ajax1.htm");
5482 tab2.on('activate', updater.refresh, updater, true);
5483
5484 // Use setUrl for Ajax loading
5485 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5486 tab3.setUrl("ajax2.htm", null, true);
5487
5488 // Disabled tab
5489 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5490 tab4.disable();
5491
5492 jtabs.activate("jtabs-1");
5493  * </code></pre>
5494  * @constructor
5495  * Create a new TabPanel.
5496  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5497  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5498  */
5499 Roo.TabPanel = function(container, config){
5500     /**
5501     * The container element for this TabPanel.
5502     * @type Roo.Element
5503     */
5504     this.el = Roo.get(container, true);
5505     if(config){
5506         if(typeof config == "boolean"){
5507             this.tabPosition = config ? "bottom" : "top";
5508         }else{
5509             Roo.apply(this, config);
5510         }
5511     }
5512     if(this.tabPosition == "bottom"){
5513         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5514         this.el.addClass("x-tabs-bottom");
5515     }
5516     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5517     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5518     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5519     if(Roo.isIE){
5520         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5521     }
5522     if(this.tabPosition != "bottom"){
5523         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5524          * @type Roo.Element
5525          */
5526         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5527         this.el.addClass("x-tabs-top");
5528     }
5529     this.items = [];
5530
5531     this.bodyEl.setStyle("position", "relative");
5532
5533     this.active = null;
5534     this.activateDelegate = this.activate.createDelegate(this);
5535
5536     this.addEvents({
5537         /**
5538          * @event tabchange
5539          * Fires when the active tab changes
5540          * @param {Roo.TabPanel} this
5541          * @param {Roo.TabPanelItem} activePanel The new active tab
5542          */
5543         "tabchange": true,
5544         /**
5545          * @event beforetabchange
5546          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5547          * @param {Roo.TabPanel} this
5548          * @param {Object} e Set cancel to true on this object to cancel the tab change
5549          * @param {Roo.TabPanelItem} tab The tab being changed to
5550          */
5551         "beforetabchange" : true
5552     });
5553
5554     Roo.EventManager.onWindowResize(this.onResize, this);
5555     this.cpad = this.el.getPadding("lr");
5556     this.hiddenCount = 0;
5557
5558
5559     // toolbar on the tabbar support...
5560     if (this.toolbar) {
5561         var tcfg = this.toolbar;
5562         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5563         this.toolbar = new Roo.Toolbar(tcfg);
5564         if (Roo.isSafari) {
5565             var tbl = tcfg.container.child('table', true);
5566             tbl.setAttribute('width', '100%');
5567         }
5568         
5569     }
5570    
5571
5572
5573     Roo.TabPanel.superclass.constructor.call(this);
5574 };
5575
5576 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5577     /*
5578      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5579      */
5580     tabPosition : "top",
5581     /*
5582      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5583      */
5584     currentTabWidth : 0,
5585     /*
5586      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5587      */
5588     minTabWidth : 40,
5589     /*
5590      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5591      */
5592     maxTabWidth : 250,
5593     /*
5594      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5595      */
5596     preferredTabWidth : 175,
5597     /*
5598      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5599      */
5600     resizeTabs : false,
5601     /*
5602      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5603      */
5604     monitorResize : true,
5605     /*
5606      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5607      */
5608     toolbar : false,
5609
5610     /**
5611      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5612      * @param {String} id The id of the div to use <b>or create</b>
5613      * @param {String} text The text for the tab
5614      * @param {String} content (optional) Content to put in the TabPanelItem body
5615      * @param {Boolean} closable (optional) True to create a close icon on the tab
5616      * @return {Roo.TabPanelItem} The created TabPanelItem
5617      */
5618     addTab : function(id, text, content, closable){
5619         var item = new Roo.TabPanelItem(this, id, text, closable);
5620         this.addTabItem(item);
5621         if(content){
5622             item.setContent(content);
5623         }
5624         return item;
5625     },
5626
5627     /**
5628      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5629      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5630      * @return {Roo.TabPanelItem}
5631      */
5632     getTab : function(id){
5633         return this.items[id];
5634     },
5635
5636     /**
5637      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5638      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5639      */
5640     hideTab : function(id){
5641         var t = this.items[id];
5642         if(!t.isHidden()){
5643            t.setHidden(true);
5644            this.hiddenCount++;
5645            this.autoSizeTabs();
5646         }
5647     },
5648
5649     /**
5650      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5651      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5652      */
5653     unhideTab : function(id){
5654         var t = this.items[id];
5655         if(t.isHidden()){
5656            t.setHidden(false);
5657            this.hiddenCount--;
5658            this.autoSizeTabs();
5659         }
5660     },
5661
5662     /**
5663      * Adds an existing {@link Roo.TabPanelItem}.
5664      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5665      */
5666     addTabItem : function(item){
5667         this.items[item.id] = item;
5668         this.items.push(item);
5669         if(this.resizeTabs){
5670            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5671            this.autoSizeTabs();
5672         }else{
5673             item.autoSize();
5674         }
5675     },
5676
5677     /**
5678      * Removes a {@link Roo.TabPanelItem}.
5679      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5680      */
5681     removeTab : function(id){
5682         var items = this.items;
5683         var tab = items[id];
5684         if(!tab) { return; }
5685         var index = items.indexOf(tab);
5686         if(this.active == tab && items.length > 1){
5687             var newTab = this.getNextAvailable(index);
5688             if(newTab) {
5689                 newTab.activate();
5690             }
5691         }
5692         this.stripEl.dom.removeChild(tab.pnode.dom);
5693         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5694             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5695         }
5696         items.splice(index, 1);
5697         delete this.items[tab.id];
5698         tab.fireEvent("close", tab);
5699         tab.purgeListeners();
5700         this.autoSizeTabs();
5701     },
5702
5703     getNextAvailable : function(start){
5704         var items = this.items;
5705         var index = start;
5706         // look for a next tab that will slide over to
5707         // replace the one being removed
5708         while(index < items.length){
5709             var item = items[++index];
5710             if(item && !item.isHidden()){
5711                 return item;
5712             }
5713         }
5714         // if one isn't found select the previous tab (on the left)
5715         index = start;
5716         while(index >= 0){
5717             var item = items[--index];
5718             if(item && !item.isHidden()){
5719                 return item;
5720             }
5721         }
5722         return null;
5723     },
5724
5725     /**
5726      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5727      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5728      */
5729     disableTab : function(id){
5730         var tab = this.items[id];
5731         if(tab && this.active != tab){
5732             tab.disable();
5733         }
5734     },
5735
5736     /**
5737      * Enables a {@link Roo.TabPanelItem} that is disabled.
5738      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5739      */
5740     enableTab : function(id){
5741         var tab = this.items[id];
5742         tab.enable();
5743     },
5744
5745     /**
5746      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5747      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5748      * @return {Roo.TabPanelItem} The TabPanelItem.
5749      */
5750     activate : function(id){
5751         var tab = this.items[id];
5752         if(!tab){
5753             return null;
5754         }
5755         if(tab == this.active || tab.disabled){
5756             return tab;
5757         }
5758         var e = {};
5759         this.fireEvent("beforetabchange", this, e, tab);
5760         if(e.cancel !== true && !tab.disabled){
5761             if(this.active){
5762                 this.active.hide();
5763             }
5764             this.active = this.items[id];
5765             this.active.show();
5766             this.fireEvent("tabchange", this, this.active);
5767         }
5768         return tab;
5769     },
5770
5771     /**
5772      * Gets the active {@link Roo.TabPanelItem}.
5773      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5774      */
5775     getActiveTab : function(){
5776         return this.active;
5777     },
5778
5779     /**
5780      * Updates the tab body element to fit the height of the container element
5781      * for overflow scrolling
5782      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5783      */
5784     syncHeight : function(targetHeight){
5785         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5786         var bm = this.bodyEl.getMargins();
5787         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5788         this.bodyEl.setHeight(newHeight);
5789         return newHeight;
5790     },
5791
5792     onResize : function(){
5793         if(this.monitorResize){
5794             this.autoSizeTabs();
5795         }
5796     },
5797
5798     /**
5799      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5800      */
5801     beginUpdate : function(){
5802         this.updating = true;
5803     },
5804
5805     /**
5806      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5807      */
5808     endUpdate : function(){
5809         this.updating = false;
5810         this.autoSizeTabs();
5811     },
5812
5813     /**
5814      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5815      */
5816     autoSizeTabs : function(){
5817         var count = this.items.length;
5818         var vcount = count - this.hiddenCount;
5819         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5820             return;
5821         }
5822         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5823         var availWidth = Math.floor(w / vcount);
5824         var b = this.stripBody;
5825         if(b.getWidth() > w){
5826             var tabs = this.items;
5827             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5828             if(availWidth < this.minTabWidth){
5829                 /*if(!this.sleft){    // incomplete scrolling code
5830                     this.createScrollButtons();
5831                 }
5832                 this.showScroll();
5833                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5834             }
5835         }else{
5836             if(this.currentTabWidth < this.preferredTabWidth){
5837                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5838             }
5839         }
5840     },
5841
5842     /**
5843      * Returns the number of tabs in this TabPanel.
5844      * @return {Number}
5845      */
5846      getCount : function(){
5847          return this.items.length;
5848      },
5849
5850     /**
5851      * Resizes all the tabs to the passed width
5852      * @param {Number} The new width
5853      */
5854     setTabWidth : function(width){
5855         this.currentTabWidth = width;
5856         for(var i = 0, len = this.items.length; i < len; i++) {
5857                 if(!this.items[i].isHidden()) {
5858                 this.items[i].setWidth(width);
5859             }
5860         }
5861     },
5862
5863     /**
5864      * Destroys this TabPanel
5865      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5866      */
5867     destroy : function(removeEl){
5868         Roo.EventManager.removeResizeListener(this.onResize, this);
5869         for(var i = 0, len = this.items.length; i < len; i++){
5870             this.items[i].purgeListeners();
5871         }
5872         if(removeEl === true){
5873             this.el.update("");
5874             this.el.remove();
5875         }
5876     }
5877 });
5878
5879 /**
5880  * @class Roo.TabPanelItem
5881  * @extends Roo.util.Observable
5882  * Represents an individual item (tab plus body) in a TabPanel.
5883  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5884  * @param {String} id The id of this TabPanelItem
5885  * @param {String} text The text for the tab of this TabPanelItem
5886  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5887  */
5888 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5889     /**
5890      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5891      * @type Roo.TabPanel
5892      */
5893     this.tabPanel = tabPanel;
5894     /**
5895      * The id for this TabPanelItem
5896      * @type String
5897      */
5898     this.id = id;
5899     /** @private */
5900     this.disabled = false;
5901     /** @private */
5902     this.text = text;
5903     /** @private */
5904     this.loaded = false;
5905     this.closable = closable;
5906
5907     /**
5908      * The body element for this TabPanelItem.
5909      * @type Roo.Element
5910      */
5911     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5912     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5913     this.bodyEl.setStyle("display", "block");
5914     this.bodyEl.setStyle("zoom", "1");
5915     this.hideAction();
5916
5917     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5918     /** @private */
5919     this.el = Roo.get(els.el, true);
5920     this.inner = Roo.get(els.inner, true);
5921     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5922     this.pnode = Roo.get(els.el.parentNode, true);
5923     this.el.on("mousedown", this.onTabMouseDown, this);
5924     this.el.on("click", this.onTabClick, this);
5925     /** @private */
5926     if(closable){
5927         var c = Roo.get(els.close, true);
5928         c.dom.title = this.closeText;
5929         c.addClassOnOver("close-over");
5930         c.on("click", this.closeClick, this);
5931      }
5932
5933     this.addEvents({
5934          /**
5935          * @event activate
5936          * Fires when this tab becomes the active tab.
5937          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5938          * @param {Roo.TabPanelItem} this
5939          */
5940         "activate": true,
5941         /**
5942          * @event beforeclose
5943          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5944          * @param {Roo.TabPanelItem} this
5945          * @param {Object} e Set cancel to true on this object to cancel the close.
5946          */
5947         "beforeclose": true,
5948         /**
5949          * @event close
5950          * Fires when this tab is closed.
5951          * @param {Roo.TabPanelItem} this
5952          */
5953          "close": true,
5954         /**
5955          * @event deactivate
5956          * Fires when this tab is no longer the active tab.
5957          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5958          * @param {Roo.TabPanelItem} this
5959          */
5960          "deactivate" : true
5961     });
5962     this.hidden = false;
5963
5964     Roo.TabPanelItem.superclass.constructor.call(this);
5965 };
5966
5967 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5968     purgeListeners : function(){
5969        Roo.util.Observable.prototype.purgeListeners.call(this);
5970        this.el.removeAllListeners();
5971     },
5972     /**
5973      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5974      */
5975     show : function(){
5976         this.pnode.addClass("on");
5977         this.showAction();
5978         if(Roo.isOpera){
5979             this.tabPanel.stripWrap.repaint();
5980         }
5981         this.fireEvent("activate", this.tabPanel, this);
5982     },
5983
5984     /**
5985      * Returns true if this tab is the active tab.
5986      * @return {Boolean}
5987      */
5988     isActive : function(){
5989         return this.tabPanel.getActiveTab() == this;
5990     },
5991
5992     /**
5993      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5994      */
5995     hide : function(){
5996         this.pnode.removeClass("on");
5997         this.hideAction();
5998         this.fireEvent("deactivate", this.tabPanel, this);
5999     },
6000
6001     hideAction : function(){
6002         this.bodyEl.hide();
6003         this.bodyEl.setStyle("position", "absolute");
6004         this.bodyEl.setLeft("-20000px");
6005         this.bodyEl.setTop("-20000px");
6006     },
6007
6008     showAction : function(){
6009         this.bodyEl.setStyle("position", "relative");
6010         this.bodyEl.setTop("");
6011         this.bodyEl.setLeft("");
6012         this.bodyEl.show();
6013     },
6014
6015     /**
6016      * Set the tooltip for the tab.
6017      * @param {String} tooltip The tab's tooltip
6018      */
6019     setTooltip : function(text){
6020         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6021             this.textEl.dom.qtip = text;
6022             this.textEl.dom.removeAttribute('title');
6023         }else{
6024             this.textEl.dom.title = text;
6025         }
6026     },
6027
6028     onTabClick : function(e){
6029         e.preventDefault();
6030         this.tabPanel.activate(this.id);
6031     },
6032
6033     onTabMouseDown : function(e){
6034         e.preventDefault();
6035         this.tabPanel.activate(this.id);
6036     },
6037
6038     getWidth : function(){
6039         return this.inner.getWidth();
6040     },
6041
6042     setWidth : function(width){
6043         var iwidth = width - this.pnode.getPadding("lr");
6044         this.inner.setWidth(iwidth);
6045         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6046         this.pnode.setWidth(width);
6047     },
6048
6049     /**
6050      * Show or hide the tab
6051      * @param {Boolean} hidden True to hide or false to show.
6052      */
6053     setHidden : function(hidden){
6054         this.hidden = hidden;
6055         this.pnode.setStyle("display", hidden ? "none" : "");
6056     },
6057
6058     /**
6059      * Returns true if this tab is "hidden"
6060      * @return {Boolean}
6061      */
6062     isHidden : function(){
6063         return this.hidden;
6064     },
6065
6066     /**
6067      * Returns the text for this tab
6068      * @return {String}
6069      */
6070     getText : function(){
6071         return this.text;
6072     },
6073
6074     autoSize : function(){
6075         //this.el.beginMeasure();
6076         this.textEl.setWidth(1);
6077         /*
6078          *  #2804 [new] Tabs in Roojs
6079          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6080          */
6081         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6082         //this.el.endMeasure();
6083     },
6084
6085     /**
6086      * Sets the text for the tab (Note: this also sets the tooltip text)
6087      * @param {String} text The tab's text and tooltip
6088      */
6089     setText : function(text){
6090         this.text = text;
6091         this.textEl.update(text);
6092         this.setTooltip(text);
6093         if(!this.tabPanel.resizeTabs){
6094             this.autoSize();
6095         }
6096     },
6097     /**
6098      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6099      */
6100     activate : function(){
6101         this.tabPanel.activate(this.id);
6102     },
6103
6104     /**
6105      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6106      */
6107     disable : function(){
6108         if(this.tabPanel.active != this){
6109             this.disabled = true;
6110             this.pnode.addClass("disabled");
6111         }
6112     },
6113
6114     /**
6115      * Enables this TabPanelItem if it was previously disabled.
6116      */
6117     enable : function(){
6118         this.disabled = false;
6119         this.pnode.removeClass("disabled");
6120     },
6121
6122     /**
6123      * Sets the content for this TabPanelItem.
6124      * @param {String} content The content
6125      * @param {Boolean} loadScripts true to look for and load scripts
6126      */
6127     setContent : function(content, loadScripts){
6128         this.bodyEl.update(content, loadScripts);
6129     },
6130
6131     /**
6132      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6133      * @return {Roo.UpdateManager} The UpdateManager
6134      */
6135     getUpdateManager : function(){
6136         return this.bodyEl.getUpdateManager();
6137     },
6138
6139     /**
6140      * Set a URL to be used to load the content for this TabPanelItem.
6141      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6142      * @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)
6143      * @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)
6144      * @return {Roo.UpdateManager} The UpdateManager
6145      */
6146     setUrl : function(url, params, loadOnce){
6147         if(this.refreshDelegate){
6148             this.un('activate', this.refreshDelegate);
6149         }
6150         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6151         this.on("activate", this.refreshDelegate);
6152         return this.bodyEl.getUpdateManager();
6153     },
6154
6155     /** @private */
6156     _handleRefresh : function(url, params, loadOnce){
6157         if(!loadOnce || !this.loaded){
6158             var updater = this.bodyEl.getUpdateManager();
6159             updater.update(url, params, this._setLoaded.createDelegate(this));
6160         }
6161     },
6162
6163     /**
6164      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6165      *   Will fail silently if the setUrl method has not been called.
6166      *   This does not activate the panel, just updates its content.
6167      */
6168     refresh : function(){
6169         if(this.refreshDelegate){
6170            this.loaded = false;
6171            this.refreshDelegate();
6172         }
6173     },
6174
6175     /** @private */
6176     _setLoaded : function(){
6177         this.loaded = true;
6178     },
6179
6180     /** @private */
6181     closeClick : function(e){
6182         var o = {};
6183         e.stopEvent();
6184         this.fireEvent("beforeclose", this, o);
6185         if(o.cancel !== true){
6186             this.tabPanel.removeTab(this.id);
6187         }
6188     },
6189     /**
6190      * The text displayed in the tooltip for the close icon.
6191      * @type String
6192      */
6193     closeText : "Close this tab"
6194 });
6195
6196 /** @private */
6197 Roo.TabPanel.prototype.createStrip = function(container){
6198     var strip = document.createElement("div");
6199     strip.className = "x-tabs-wrap";
6200     container.appendChild(strip);
6201     return strip;
6202 };
6203 /** @private */
6204 Roo.TabPanel.prototype.createStripList = function(strip){
6205     // div wrapper for retard IE
6206     // returns the "tr" element.
6207     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6208         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6209         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6210     return strip.firstChild.firstChild.firstChild.firstChild;
6211 };
6212 /** @private */
6213 Roo.TabPanel.prototype.createBody = function(container){
6214     var body = document.createElement("div");
6215     Roo.id(body, "tab-body");
6216     Roo.fly(body).addClass("x-tabs-body");
6217     container.appendChild(body);
6218     return body;
6219 };
6220 /** @private */
6221 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6222     var body = Roo.getDom(id);
6223     if(!body){
6224         body = document.createElement("div");
6225         body.id = id;
6226     }
6227     Roo.fly(body).addClass("x-tabs-item-body");
6228     bodyEl.insertBefore(body, bodyEl.firstChild);
6229     return body;
6230 };
6231 /** @private */
6232 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6233     var td = document.createElement("td");
6234     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6235     //stripEl.appendChild(td);
6236     if(closable){
6237         td.className = "x-tabs-closable";
6238         if(!this.closeTpl){
6239             this.closeTpl = new Roo.Template(
6240                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6241                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6242                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6243             );
6244         }
6245         var el = this.closeTpl.overwrite(td, {"text": text});
6246         var close = el.getElementsByTagName("div")[0];
6247         var inner = el.getElementsByTagName("em")[0];
6248         return {"el": el, "close": close, "inner": inner};
6249     } else {
6250         if(!this.tabTpl){
6251             this.tabTpl = new Roo.Template(
6252                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6253                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6254             );
6255         }
6256         var el = this.tabTpl.overwrite(td, {"text": text});
6257         var inner = el.getElementsByTagName("em")[0];
6258         return {"el": el, "inner": inner};
6259     }
6260 };/*
6261  * Based on:
6262  * Ext JS Library 1.1.1
6263  * Copyright(c) 2006-2007, Ext JS, LLC.
6264  *
6265  * Originally Released Under LGPL - original licence link has changed is not relivant.
6266  *
6267  * Fork - LGPL
6268  * <script type="text/javascript">
6269  */
6270
6271 /**
6272  * @class Roo.Button
6273  * @extends Roo.util.Observable
6274  * Simple Button class
6275  * @cfg {String} text The button text
6276  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6277  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6278  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6279  * @cfg {Object} scope The scope of the handler
6280  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6281  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6282  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6283  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6284  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6285  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6286    applies if enableToggle = true)
6287  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6288  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6289   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6290  * @constructor
6291  * Create a new button
6292  * @param {Object} config The config object
6293  */
6294 Roo.Button = function(renderTo, config)
6295 {
6296     if (!config) {
6297         config = renderTo;
6298         renderTo = config.renderTo || false;
6299     }
6300     
6301     Roo.apply(this, config);
6302     this.addEvents({
6303         /**
6304              * @event click
6305              * Fires when this button is clicked
6306              * @param {Button} this
6307              * @param {EventObject} e The click event
6308              */
6309             "click" : true,
6310         /**
6311              * @event toggle
6312              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6313              * @param {Button} this
6314              * @param {Boolean} pressed
6315              */
6316             "toggle" : true,
6317         /**
6318              * @event mouseover
6319              * Fires when the mouse hovers over the button
6320              * @param {Button} this
6321              * @param {Event} e The event object
6322              */
6323         'mouseover' : true,
6324         /**
6325              * @event mouseout
6326              * Fires when the mouse exits the button
6327              * @param {Button} this
6328              * @param {Event} e The event object
6329              */
6330         'mouseout': true,
6331          /**
6332              * @event render
6333              * Fires when the button is rendered
6334              * @param {Button} this
6335              */
6336         'render': true
6337     });
6338     if(this.menu){
6339         this.menu = Roo.menu.MenuMgr.get(this.menu);
6340     }
6341     // register listeners first!!  - so render can be captured..
6342     Roo.util.Observable.call(this);
6343     if(renderTo){
6344         this.render(renderTo);
6345     }
6346     
6347   
6348 };
6349
6350 Roo.extend(Roo.Button, Roo.util.Observable, {
6351     /**
6352      * 
6353      */
6354     
6355     /**
6356      * Read-only. True if this button is hidden
6357      * @type Boolean
6358      */
6359     hidden : false,
6360     /**
6361      * Read-only. True if this button is disabled
6362      * @type Boolean
6363      */
6364     disabled : false,
6365     /**
6366      * Read-only. True if this button is pressed (only if enableToggle = true)
6367      * @type Boolean
6368      */
6369     pressed : false,
6370
6371     /**
6372      * @cfg {Number} tabIndex 
6373      * The DOM tabIndex for this button (defaults to undefined)
6374      */
6375     tabIndex : undefined,
6376
6377     /**
6378      * @cfg {Boolean} enableToggle
6379      * True to enable pressed/not pressed toggling (defaults to false)
6380      */
6381     enableToggle: false,
6382     /**
6383      * @cfg {Roo.menu.Menu} menu
6384      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6385      */
6386     menu : undefined,
6387     /**
6388      * @cfg {String} menuAlign
6389      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6390      */
6391     menuAlign : "tl-bl?",
6392
6393     /**
6394      * @cfg {String} iconCls
6395      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6396      */
6397     iconCls : undefined,
6398     /**
6399      * @cfg {String} type
6400      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6401      */
6402     type : 'button',
6403
6404     // private
6405     menuClassTarget: 'tr',
6406
6407     /**
6408      * @cfg {String} clickEvent
6409      * The type of event to map to the button's event handler (defaults to 'click')
6410      */
6411     clickEvent : 'click',
6412
6413     /**
6414      * @cfg {Boolean} handleMouseEvents
6415      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6416      */
6417     handleMouseEvents : true,
6418
6419     /**
6420      * @cfg {String} tooltipType
6421      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6422      */
6423     tooltipType : 'qtip',
6424
6425     /**
6426      * @cfg {String} cls
6427      * A CSS class to apply to the button's main element.
6428      */
6429     
6430     /**
6431      * @cfg {Roo.Template} template (Optional)
6432      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6433      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6434      * require code modifications if required elements (e.g. a button) aren't present.
6435      */
6436
6437     // private
6438     render : function(renderTo){
6439         var btn;
6440         if(this.hideParent){
6441             this.parentEl = Roo.get(renderTo);
6442         }
6443         if(!this.dhconfig){
6444             if(!this.template){
6445                 if(!Roo.Button.buttonTemplate){
6446                     // hideous table template
6447                     Roo.Button.buttonTemplate = new Roo.Template(
6448                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6449                         '<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>',
6450                         "</tr></tbody></table>");
6451                 }
6452                 this.template = Roo.Button.buttonTemplate;
6453             }
6454             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6455             var btnEl = btn.child("button:first");
6456             btnEl.on('focus', this.onFocus, this);
6457             btnEl.on('blur', this.onBlur, this);
6458             if(this.cls){
6459                 btn.addClass(this.cls);
6460             }
6461             if(this.icon){
6462                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6463             }
6464             if(this.iconCls){
6465                 btnEl.addClass(this.iconCls);
6466                 if(!this.cls){
6467                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6468                 }
6469             }
6470             if(this.tabIndex !== undefined){
6471                 btnEl.dom.tabIndex = this.tabIndex;
6472             }
6473             if(this.tooltip){
6474                 if(typeof this.tooltip == 'object'){
6475                     Roo.QuickTips.tips(Roo.apply({
6476                           target: btnEl.id
6477                     }, this.tooltip));
6478                 } else {
6479                     btnEl.dom[this.tooltipType] = this.tooltip;
6480                 }
6481             }
6482         }else{
6483             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6484         }
6485         this.el = btn;
6486         if(this.id){
6487             this.el.dom.id = this.el.id = this.id;
6488         }
6489         if(this.menu){
6490             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6491             this.menu.on("show", this.onMenuShow, this);
6492             this.menu.on("hide", this.onMenuHide, this);
6493         }
6494         btn.addClass("x-btn");
6495         if(Roo.isIE && !Roo.isIE7){
6496             this.autoWidth.defer(1, this);
6497         }else{
6498             this.autoWidth();
6499         }
6500         if(this.handleMouseEvents){
6501             btn.on("mouseover", this.onMouseOver, this);
6502             btn.on("mouseout", this.onMouseOut, this);
6503             btn.on("mousedown", this.onMouseDown, this);
6504         }
6505         btn.on(this.clickEvent, this.onClick, this);
6506         //btn.on("mouseup", this.onMouseUp, this);
6507         if(this.hidden){
6508             this.hide();
6509         }
6510         if(this.disabled){
6511             this.disable();
6512         }
6513         Roo.ButtonToggleMgr.register(this);
6514         if(this.pressed){
6515             this.el.addClass("x-btn-pressed");
6516         }
6517         if(this.repeat){
6518             var repeater = new Roo.util.ClickRepeater(btn,
6519                 typeof this.repeat == "object" ? this.repeat : {}
6520             );
6521             repeater.on("click", this.onClick,  this);
6522         }
6523         
6524         this.fireEvent('render', this);
6525         
6526     },
6527     /**
6528      * Returns the button's underlying element
6529      * @return {Roo.Element} The element
6530      */
6531     getEl : function(){
6532         return this.el;  
6533     },
6534     
6535     /**
6536      * Destroys this Button and removes any listeners.
6537      */
6538     destroy : function(){
6539         Roo.ButtonToggleMgr.unregister(this);
6540         this.el.removeAllListeners();
6541         this.purgeListeners();
6542         this.el.remove();
6543     },
6544
6545     // private
6546     autoWidth : function(){
6547         if(this.el){
6548             this.el.setWidth("auto");
6549             if(Roo.isIE7 && Roo.isStrict){
6550                 var ib = this.el.child('button');
6551                 if(ib && ib.getWidth() > 20){
6552                     ib.clip();
6553                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6554                 }
6555             }
6556             if(this.minWidth){
6557                 if(this.hidden){
6558                     this.el.beginMeasure();
6559                 }
6560                 if(this.el.getWidth() < this.minWidth){
6561                     this.el.setWidth(this.minWidth);
6562                 }
6563                 if(this.hidden){
6564                     this.el.endMeasure();
6565                 }
6566             }
6567         }
6568     },
6569
6570     /**
6571      * Assigns this button's click handler
6572      * @param {Function} handler The function to call when the button is clicked
6573      * @param {Object} scope (optional) Scope for the function passed in
6574      */
6575     setHandler : function(handler, scope){
6576         this.handler = handler;
6577         this.scope = scope;  
6578     },
6579     
6580     /**
6581      * Sets this button's text
6582      * @param {String} text The button text
6583      */
6584     setText : function(text){
6585         this.text = text;
6586         if(this.el){
6587             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6588         }
6589         this.autoWidth();
6590     },
6591     
6592     /**
6593      * Gets the text for this button
6594      * @return {String} The button text
6595      */
6596     getText : function(){
6597         return this.text;  
6598     },
6599     
6600     /**
6601      * Show this button
6602      */
6603     show: function(){
6604         this.hidden = false;
6605         if(this.el){
6606             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6607         }
6608     },
6609     
6610     /**
6611      * Hide this button
6612      */
6613     hide: function(){
6614         this.hidden = true;
6615         if(this.el){
6616             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6617         }
6618     },
6619     
6620     /**
6621      * Convenience function for boolean show/hide
6622      * @param {Boolean} visible True to show, false to hide
6623      */
6624     setVisible: function(visible){
6625         if(visible) {
6626             this.show();
6627         }else{
6628             this.hide();
6629         }
6630     },
6631     
6632     /**
6633      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6634      * @param {Boolean} state (optional) Force a particular state
6635      */
6636     toggle : function(state){
6637         state = state === undefined ? !this.pressed : state;
6638         if(state != this.pressed){
6639             if(state){
6640                 this.el.addClass("x-btn-pressed");
6641                 this.pressed = true;
6642                 this.fireEvent("toggle", this, true);
6643             }else{
6644                 this.el.removeClass("x-btn-pressed");
6645                 this.pressed = false;
6646                 this.fireEvent("toggle", this, false);
6647             }
6648             if(this.toggleHandler){
6649                 this.toggleHandler.call(this.scope || this, this, state);
6650             }
6651         }
6652     },
6653     
6654     /**
6655      * Focus the button
6656      */
6657     focus : function(){
6658         this.el.child('button:first').focus();
6659     },
6660     
6661     /**
6662      * Disable this button
6663      */
6664     disable : function(){
6665         if(this.el){
6666             this.el.addClass("x-btn-disabled");
6667         }
6668         this.disabled = true;
6669     },
6670     
6671     /**
6672      * Enable this button
6673      */
6674     enable : function(){
6675         if(this.el){
6676             this.el.removeClass("x-btn-disabled");
6677         }
6678         this.disabled = false;
6679     },
6680
6681     /**
6682      * Convenience function for boolean enable/disable
6683      * @param {Boolean} enabled True to enable, false to disable
6684      */
6685     setDisabled : function(v){
6686         this[v !== true ? "enable" : "disable"]();
6687     },
6688
6689     // private
6690     onClick : function(e)
6691     {
6692         if(e){
6693             e.preventDefault();
6694         }
6695         if(e.button != 0){
6696             return;
6697         }
6698         if(!this.disabled){
6699             if(this.enableToggle){
6700                 this.toggle();
6701             }
6702             if(this.menu && !this.menu.isVisible()){
6703                 this.menu.show(this.el, this.menuAlign);
6704             }
6705             this.fireEvent("click", this, e);
6706             if(this.handler){
6707                 this.el.removeClass("x-btn-over");
6708                 this.handler.call(this.scope || this, this, e);
6709             }
6710         }
6711     },
6712     // private
6713     onMouseOver : function(e){
6714         if(!this.disabled){
6715             this.el.addClass("x-btn-over");
6716             this.fireEvent('mouseover', this, e);
6717         }
6718     },
6719     // private
6720     onMouseOut : function(e){
6721         if(!e.within(this.el,  true)){
6722             this.el.removeClass("x-btn-over");
6723             this.fireEvent('mouseout', this, e);
6724         }
6725     },
6726     // private
6727     onFocus : function(e){
6728         if(!this.disabled){
6729             this.el.addClass("x-btn-focus");
6730         }
6731     },
6732     // private
6733     onBlur : function(e){
6734         this.el.removeClass("x-btn-focus");
6735     },
6736     // private
6737     onMouseDown : function(e){
6738         if(!this.disabled && e.button == 0){
6739             this.el.addClass("x-btn-click");
6740             Roo.get(document).on('mouseup', this.onMouseUp, this);
6741         }
6742     },
6743     // private
6744     onMouseUp : function(e){
6745         if(e.button == 0){
6746             this.el.removeClass("x-btn-click");
6747             Roo.get(document).un('mouseup', this.onMouseUp, this);
6748         }
6749     },
6750     // private
6751     onMenuShow : function(e){
6752         this.el.addClass("x-btn-menu-active");
6753     },
6754     // private
6755     onMenuHide : function(e){
6756         this.el.removeClass("x-btn-menu-active");
6757     }   
6758 });
6759
6760 // Private utility class used by Button
6761 Roo.ButtonToggleMgr = function(){
6762    var groups = {};
6763    
6764    function toggleGroup(btn, state){
6765        if(state){
6766            var g = groups[btn.toggleGroup];
6767            for(var i = 0, l = g.length; i < l; i++){
6768                if(g[i] != btn){
6769                    g[i].toggle(false);
6770                }
6771            }
6772        }
6773    }
6774    
6775    return {
6776        register : function(btn){
6777            if(!btn.toggleGroup){
6778                return;
6779            }
6780            var g = groups[btn.toggleGroup];
6781            if(!g){
6782                g = groups[btn.toggleGroup] = [];
6783            }
6784            g.push(btn);
6785            btn.on("toggle", toggleGroup);
6786        },
6787        
6788        unregister : function(btn){
6789            if(!btn.toggleGroup){
6790                return;
6791            }
6792            var g = groups[btn.toggleGroup];
6793            if(g){
6794                g.remove(btn);
6795                btn.un("toggle", toggleGroup);
6796            }
6797        }
6798    };
6799 }();/*
6800  * Based on:
6801  * Ext JS Library 1.1.1
6802  * Copyright(c) 2006-2007, Ext JS, LLC.
6803  *
6804  * Originally Released Under LGPL - original licence link has changed is not relivant.
6805  *
6806  * Fork - LGPL
6807  * <script type="text/javascript">
6808  */
6809  
6810 /**
6811  * @class Roo.SplitButton
6812  * @extends Roo.Button
6813  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6814  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6815  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6816  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6817  * @cfg {String} arrowTooltip The title attribute of the arrow
6818  * @constructor
6819  * Create a new menu button
6820  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6821  * @param {Object} config The config object
6822  */
6823 Roo.SplitButton = function(renderTo, config){
6824     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6825     /**
6826      * @event arrowclick
6827      * Fires when this button's arrow is clicked
6828      * @param {SplitButton} this
6829      * @param {EventObject} e The click event
6830      */
6831     this.addEvents({"arrowclick":true});
6832 };
6833
6834 Roo.extend(Roo.SplitButton, Roo.Button, {
6835     render : function(renderTo){
6836         // this is one sweet looking template!
6837         var tpl = new Roo.Template(
6838             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6839             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6840             '<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>',
6841             "</tbody></table></td><td>",
6842             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6843             '<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>',
6844             "</tbody></table></td></tr></table>"
6845         );
6846         var btn = tpl.append(renderTo, [this.text, this.type], true);
6847         var btnEl = btn.child("button");
6848         if(this.cls){
6849             btn.addClass(this.cls);
6850         }
6851         if(this.icon){
6852             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6853         }
6854         if(this.iconCls){
6855             btnEl.addClass(this.iconCls);
6856             if(!this.cls){
6857                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6858             }
6859         }
6860         this.el = btn;
6861         if(this.handleMouseEvents){
6862             btn.on("mouseover", this.onMouseOver, this);
6863             btn.on("mouseout", this.onMouseOut, this);
6864             btn.on("mousedown", this.onMouseDown, this);
6865             btn.on("mouseup", this.onMouseUp, this);
6866         }
6867         btn.on(this.clickEvent, this.onClick, this);
6868         if(this.tooltip){
6869             if(typeof this.tooltip == 'object'){
6870                 Roo.QuickTips.tips(Roo.apply({
6871                       target: btnEl.id
6872                 }, this.tooltip));
6873             } else {
6874                 btnEl.dom[this.tooltipType] = this.tooltip;
6875             }
6876         }
6877         if(this.arrowTooltip){
6878             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6879         }
6880         if(this.hidden){
6881             this.hide();
6882         }
6883         if(this.disabled){
6884             this.disable();
6885         }
6886         if(this.pressed){
6887             this.el.addClass("x-btn-pressed");
6888         }
6889         if(Roo.isIE && !Roo.isIE7){
6890             this.autoWidth.defer(1, this);
6891         }else{
6892             this.autoWidth();
6893         }
6894         if(this.menu){
6895             this.menu.on("show", this.onMenuShow, this);
6896             this.menu.on("hide", this.onMenuHide, this);
6897         }
6898         this.fireEvent('render', this);
6899     },
6900
6901     // private
6902     autoWidth : function(){
6903         if(this.el){
6904             var tbl = this.el.child("table:first");
6905             var tbl2 = this.el.child("table:last");
6906             this.el.setWidth("auto");
6907             tbl.setWidth("auto");
6908             if(Roo.isIE7 && Roo.isStrict){
6909                 var ib = this.el.child('button:first');
6910                 if(ib && ib.getWidth() > 20){
6911                     ib.clip();
6912                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6913                 }
6914             }
6915             if(this.minWidth){
6916                 if(this.hidden){
6917                     this.el.beginMeasure();
6918                 }
6919                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6920                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6921                 }
6922                 if(this.hidden){
6923                     this.el.endMeasure();
6924                 }
6925             }
6926             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6927         } 
6928     },
6929     /**
6930      * Sets this button's click handler
6931      * @param {Function} handler The function to call when the button is clicked
6932      * @param {Object} scope (optional) Scope for the function passed above
6933      */
6934     setHandler : function(handler, scope){
6935         this.handler = handler;
6936         this.scope = scope;  
6937     },
6938     
6939     /**
6940      * Sets this button's arrow click handler
6941      * @param {Function} handler The function to call when the arrow is clicked
6942      * @param {Object} scope (optional) Scope for the function passed above
6943      */
6944     setArrowHandler : function(handler, scope){
6945         this.arrowHandler = handler;
6946         this.scope = scope;  
6947     },
6948     
6949     /**
6950      * Focus the button
6951      */
6952     focus : function(){
6953         if(this.el){
6954             this.el.child("button:first").focus();
6955         }
6956     },
6957
6958     // private
6959     onClick : function(e){
6960         e.preventDefault();
6961         if(!this.disabled){
6962             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6963                 if(this.menu && !this.menu.isVisible()){
6964                     this.menu.show(this.el, this.menuAlign);
6965                 }
6966                 this.fireEvent("arrowclick", this, e);
6967                 if(this.arrowHandler){
6968                     this.arrowHandler.call(this.scope || this, this, e);
6969                 }
6970             }else{
6971                 this.fireEvent("click", this, e);
6972                 if(this.handler){
6973                     this.handler.call(this.scope || this, this, e);
6974                 }
6975             }
6976         }
6977     },
6978     // private
6979     onMouseDown : function(e){
6980         if(!this.disabled){
6981             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6982         }
6983     },
6984     // private
6985     onMouseUp : function(e){
6986         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6987     }   
6988 });
6989
6990
6991 // backwards compat
6992 Roo.MenuButton = Roo.SplitButton;/*
6993  * Based on:
6994  * Ext JS Library 1.1.1
6995  * Copyright(c) 2006-2007, Ext JS, LLC.
6996  *
6997  * Originally Released Under LGPL - original licence link has changed is not relivant.
6998  *
6999  * Fork - LGPL
7000  * <script type="text/javascript">
7001  */
7002
7003 /**
7004  * @class Roo.Toolbar
7005  * @children   Roo.Toolbar.Item Roo.form.Field
7006  * Basic Toolbar class.
7007  * @constructor
7008  * Creates a new Toolbar
7009  * @param {Object} container The config object
7010  */ 
7011 Roo.Toolbar = function(container, buttons, config)
7012 {
7013     /// old consturctor format still supported..
7014     if(container instanceof Array){ // omit the container for later rendering
7015         buttons = container;
7016         config = buttons;
7017         container = null;
7018     }
7019     if (typeof(container) == 'object' && container.xtype) {
7020         config = container;
7021         container = config.container;
7022         buttons = config.buttons || []; // not really - use items!!
7023     }
7024     var xitems = [];
7025     if (config && config.items) {
7026         xitems = config.items;
7027         delete config.items;
7028     }
7029     Roo.apply(this, config);
7030     this.buttons = buttons;
7031     
7032     if(container){
7033         this.render(container);
7034     }
7035     this.xitems = xitems;
7036     Roo.each(xitems, function(b) {
7037         this.add(b);
7038     }, this);
7039     
7040 };
7041
7042 Roo.Toolbar.prototype = {
7043     /**
7044      * @cfg {Array} items
7045      * array of button configs or elements to add (will be converted to a MixedCollection)
7046      */
7047     items: false,
7048     /**
7049      * @cfg {String/HTMLElement/Element} container
7050      * The id or element that will contain the toolbar
7051      */
7052     // private
7053     render : function(ct){
7054         this.el = Roo.get(ct);
7055         if(this.cls){
7056             this.el.addClass(this.cls);
7057         }
7058         // using a table allows for vertical alignment
7059         // 100% width is needed by Safari...
7060         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7061         this.tr = this.el.child("tr", true);
7062         var autoId = 0;
7063         this.items = new Roo.util.MixedCollection(false, function(o){
7064             return o.id || ("item" + (++autoId));
7065         });
7066         if(this.buttons){
7067             this.add.apply(this, this.buttons);
7068             delete this.buttons;
7069         }
7070     },
7071
7072     /**
7073      * Adds element(s) to the toolbar -- this function takes a variable number of 
7074      * arguments of mixed type and adds them to the toolbar.
7075      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7076      * <ul>
7077      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7078      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7079      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7080      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7081      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7082      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7083      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7084      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7085      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7086      * </ul>
7087      * @param {Mixed} arg2
7088      * @param {Mixed} etc.
7089      */
7090     add : function(){
7091         var a = arguments, l = a.length;
7092         for(var i = 0; i < l; i++){
7093             this._add(a[i]);
7094         }
7095     },
7096     // private..
7097     _add : function(el) {
7098         
7099         if (el.xtype) {
7100             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7101         }
7102         
7103         if (el.applyTo){ // some kind of form field
7104             return this.addField(el);
7105         } 
7106         if (el.render){ // some kind of Toolbar.Item
7107             return this.addItem(el);
7108         }
7109         if (typeof el == "string"){ // string
7110             if(el == "separator" || el == "-"){
7111                 return this.addSeparator();
7112             }
7113             if (el == " "){
7114                 return this.addSpacer();
7115             }
7116             if(el == "->"){
7117                 return this.addFill();
7118             }
7119             return this.addText(el);
7120             
7121         }
7122         if(el.tagName){ // element
7123             return this.addElement(el);
7124         }
7125         if(typeof el == "object"){ // must be button config?
7126             return this.addButton(el);
7127         }
7128         // and now what?!?!
7129         return false;
7130         
7131     },
7132     
7133     /**
7134      * Add an Xtype element
7135      * @param {Object} xtype Xtype Object
7136      * @return {Object} created Object
7137      */
7138     addxtype : function(e){
7139         return this.add(e);  
7140     },
7141     
7142     /**
7143      * Returns the Element for this toolbar.
7144      * @return {Roo.Element}
7145      */
7146     getEl : function(){
7147         return this.el;  
7148     },
7149     
7150     /**
7151      * Adds a separator
7152      * @return {Roo.Toolbar.Item} The separator item
7153      */
7154     addSeparator : function(){
7155         return this.addItem(new Roo.Toolbar.Separator());
7156     },
7157
7158     /**
7159      * Adds a spacer element
7160      * @return {Roo.Toolbar.Spacer} The spacer item
7161      */
7162     addSpacer : function(){
7163         return this.addItem(new Roo.Toolbar.Spacer());
7164     },
7165
7166     /**
7167      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7168      * @return {Roo.Toolbar.Fill} The fill item
7169      */
7170     addFill : function(){
7171         return this.addItem(new Roo.Toolbar.Fill());
7172     },
7173
7174     /**
7175      * Adds any standard HTML element to the toolbar
7176      * @param {String/HTMLElement/Element} el The element or id of the element to add
7177      * @return {Roo.Toolbar.Item} The element's item
7178      */
7179     addElement : function(el){
7180         return this.addItem(new Roo.Toolbar.Item(el));
7181     },
7182     /**
7183      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7184      * @type Roo.util.MixedCollection  
7185      */
7186     items : false,
7187      
7188     /**
7189      * Adds any Toolbar.Item or subclass
7190      * @param {Roo.Toolbar.Item} item
7191      * @return {Roo.Toolbar.Item} The item
7192      */
7193     addItem : function(item){
7194         var td = this.nextBlock();
7195         item.render(td);
7196         this.items.add(item);
7197         return item;
7198     },
7199     
7200     /**
7201      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7202      * @param {Object/Array} config A button config or array of configs
7203      * @return {Roo.Toolbar.Button/Array}
7204      */
7205     addButton : function(config){
7206         if(config instanceof Array){
7207             var buttons = [];
7208             for(var i = 0, len = config.length; i < len; i++) {
7209                 buttons.push(this.addButton(config[i]));
7210             }
7211             return buttons;
7212         }
7213         var b = config;
7214         if(!(config instanceof Roo.Toolbar.Button)){
7215             b = config.split ?
7216                 new Roo.Toolbar.SplitButton(config) :
7217                 new Roo.Toolbar.Button(config);
7218         }
7219         var td = this.nextBlock();
7220         b.render(td);
7221         this.items.add(b);
7222         return b;
7223     },
7224     
7225     /**
7226      * Adds text to the toolbar
7227      * @param {String} text The text to add
7228      * @return {Roo.Toolbar.Item} The element's item
7229      */
7230     addText : function(text){
7231         return this.addItem(new Roo.Toolbar.TextItem(text));
7232     },
7233     
7234     /**
7235      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7236      * @param {Number} index The index where the item is to be inserted
7237      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7238      * @return {Roo.Toolbar.Button/Item}
7239      */
7240     insertButton : function(index, item){
7241         if(item instanceof Array){
7242             var buttons = [];
7243             for(var i = 0, len = item.length; i < len; i++) {
7244                buttons.push(this.insertButton(index + i, item[i]));
7245             }
7246             return buttons;
7247         }
7248         if (!(item instanceof Roo.Toolbar.Button)){
7249            item = new Roo.Toolbar.Button(item);
7250         }
7251         var td = document.createElement("td");
7252         this.tr.insertBefore(td, this.tr.childNodes[index]);
7253         item.render(td);
7254         this.items.insert(index, item);
7255         return item;
7256     },
7257     
7258     /**
7259      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7260      * @param {Object} config
7261      * @return {Roo.Toolbar.Item} The element's item
7262      */
7263     addDom : function(config, returnEl){
7264         var td = this.nextBlock();
7265         Roo.DomHelper.overwrite(td, config);
7266         var ti = new Roo.Toolbar.Item(td.firstChild);
7267         ti.render(td);
7268         this.items.add(ti);
7269         return ti;
7270     },
7271
7272     /**
7273      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7274      * @type Roo.util.MixedCollection  
7275      */
7276     fields : false,
7277     
7278     /**
7279      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7280      * Note: the field should not have been rendered yet. For a field that has already been
7281      * rendered, use {@link #addElement}.
7282      * @param {Roo.form.Field} field
7283      * @return {Roo.ToolbarItem}
7284      */
7285      
7286       
7287     addField : function(field) {
7288         if (!this.fields) {
7289             var autoId = 0;
7290             this.fields = new Roo.util.MixedCollection(false, function(o){
7291                 return o.id || ("item" + (++autoId));
7292             });
7293
7294         }
7295         
7296         var td = this.nextBlock();
7297         field.render(td);
7298         var ti = new Roo.Toolbar.Item(td.firstChild);
7299         ti.render(td);
7300         this.items.add(ti);
7301         this.fields.add(field);
7302         return ti;
7303     },
7304     /**
7305      * Hide the toolbar
7306      * @method hide
7307      */
7308      
7309       
7310     hide : function()
7311     {
7312         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7313         this.el.child('div').hide();
7314     },
7315     /**
7316      * Show the toolbar
7317      * @method show
7318      */
7319     show : function()
7320     {
7321         this.el.child('div').show();
7322     },
7323       
7324     // private
7325     nextBlock : function(){
7326         var td = document.createElement("td");
7327         this.tr.appendChild(td);
7328         return td;
7329     },
7330
7331     // private
7332     destroy : function(){
7333         if(this.items){ // rendered?
7334             Roo.destroy.apply(Roo, this.items.items);
7335         }
7336         if(this.fields){ // rendered?
7337             Roo.destroy.apply(Roo, this.fields.items);
7338         }
7339         Roo.Element.uncache(this.el, this.tr);
7340     }
7341 };
7342
7343 /**
7344  * @class Roo.Toolbar.Item
7345  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7346  * @constructor
7347  * Creates a new Item
7348  * @param {HTMLElement} el 
7349  */
7350 Roo.Toolbar.Item = function(el){
7351     var cfg = {};
7352     if (typeof (el.xtype) != 'undefined') {
7353         cfg = el;
7354         el = cfg.el;
7355     }
7356     
7357     this.el = Roo.getDom(el);
7358     this.id = Roo.id(this.el);
7359     this.hidden = false;
7360     
7361     this.addEvents({
7362          /**
7363              * @event render
7364              * Fires when the button is rendered
7365              * @param {Button} this
7366              */
7367         'render': true
7368     });
7369     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7370 };
7371 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7372 //Roo.Toolbar.Item.prototype = {
7373     
7374     /**
7375      * Get this item's HTML Element
7376      * @return {HTMLElement}
7377      */
7378     getEl : function(){
7379        return this.el;  
7380     },
7381
7382     // private
7383     render : function(td){
7384         
7385          this.td = td;
7386         td.appendChild(this.el);
7387         
7388         this.fireEvent('render', this);
7389     },
7390     
7391     /**
7392      * Removes and destroys this item.
7393      */
7394     destroy : function(){
7395         this.td.parentNode.removeChild(this.td);
7396     },
7397     
7398     /**
7399      * Shows this item.
7400      */
7401     show: function(){
7402         this.hidden = false;
7403         this.td.style.display = "";
7404     },
7405     
7406     /**
7407      * Hides this item.
7408      */
7409     hide: function(){
7410         this.hidden = true;
7411         this.td.style.display = "none";
7412     },
7413     
7414     /**
7415      * Convenience function for boolean show/hide.
7416      * @param {Boolean} visible true to show/false to hide
7417      */
7418     setVisible: function(visible){
7419         if(visible) {
7420             this.show();
7421         }else{
7422             this.hide();
7423         }
7424     },
7425     
7426     /**
7427      * Try to focus this item.
7428      */
7429     focus : function(){
7430         Roo.fly(this.el).focus();
7431     },
7432     
7433     /**
7434      * Disables this item.
7435      */
7436     disable : function(){
7437         Roo.fly(this.td).addClass("x-item-disabled");
7438         this.disabled = true;
7439         this.el.disabled = true;
7440     },
7441     
7442     /**
7443      * Enables this item.
7444      */
7445     enable : function(){
7446         Roo.fly(this.td).removeClass("x-item-disabled");
7447         this.disabled = false;
7448         this.el.disabled = false;
7449     }
7450 });
7451
7452
7453 /**
7454  * @class Roo.Toolbar.Separator
7455  * @extends Roo.Toolbar.Item
7456  * A simple toolbar separator class
7457  * @constructor
7458  * Creates a new Separator
7459  */
7460 Roo.Toolbar.Separator = function(cfg){
7461     
7462     var s = document.createElement("span");
7463     s.className = "ytb-sep";
7464     if (cfg) {
7465         cfg.el = s;
7466     }
7467     
7468     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7469 };
7470 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7471     enable:Roo.emptyFn,
7472     disable:Roo.emptyFn,
7473     focus:Roo.emptyFn
7474 });
7475
7476 /**
7477  * @class Roo.Toolbar.Spacer
7478  * @extends Roo.Toolbar.Item
7479  * A simple element that adds extra horizontal space to a toolbar.
7480  * @constructor
7481  * Creates a new Spacer
7482  */
7483 Roo.Toolbar.Spacer = function(cfg){
7484     var s = document.createElement("div");
7485     s.className = "ytb-spacer";
7486     if (cfg) {
7487         cfg.el = s;
7488     }
7489     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7490 };
7491 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7492     enable:Roo.emptyFn,
7493     disable:Roo.emptyFn,
7494     focus:Roo.emptyFn
7495 });
7496
7497 /**
7498  * @class Roo.Toolbar.Fill
7499  * @extends Roo.Toolbar.Spacer
7500  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7501  * @constructor
7502  * Creates a new Spacer
7503  */
7504 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7505     // private
7506     render : function(td){
7507         td.style.width = '100%';
7508         Roo.Toolbar.Fill.superclass.render.call(this, td);
7509     }
7510 });
7511
7512 /**
7513  * @class Roo.Toolbar.TextItem
7514  * @extends Roo.Toolbar.Item
7515  * A simple class that renders text directly into a toolbar.
7516  * @constructor
7517  * Creates a new TextItem
7518  * @cfg {string} text 
7519  */
7520 Roo.Toolbar.TextItem = function(cfg){
7521     var  text = cfg || "";
7522     if (typeof(cfg) == 'object') {
7523         text = cfg.text || "";
7524     }  else {
7525         cfg = null;
7526     }
7527     var s = document.createElement("span");
7528     s.className = "ytb-text";
7529     s.innerHTML = text;
7530     if (cfg) {
7531         cfg.el  = s;
7532     }
7533     
7534     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7535 };
7536 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7537     
7538      
7539     enable:Roo.emptyFn,
7540     disable:Roo.emptyFn,
7541     focus:Roo.emptyFn
7542 });
7543
7544 /**
7545  * @class Roo.Toolbar.Button
7546  * @extends Roo.Button
7547  * A button that renders into a toolbar.
7548  * @constructor
7549  * Creates a new Button
7550  * @param {Object} config A standard {@link Roo.Button} config object
7551  */
7552 Roo.Toolbar.Button = function(config){
7553     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7554 };
7555 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7556 {
7557     
7558     
7559     render : function(td){
7560         this.td = td;
7561         Roo.Toolbar.Button.superclass.render.call(this, td);
7562     },
7563     
7564     /**
7565      * Removes and destroys this button
7566      */
7567     destroy : function(){
7568         Roo.Toolbar.Button.superclass.destroy.call(this);
7569         this.td.parentNode.removeChild(this.td);
7570     },
7571     
7572     /**
7573      * Shows this button
7574      */
7575     show: function(){
7576         this.hidden = false;
7577         this.td.style.display = "";
7578     },
7579     
7580     /**
7581      * Hides this button
7582      */
7583     hide: function(){
7584         this.hidden = true;
7585         this.td.style.display = "none";
7586     },
7587
7588     /**
7589      * Disables this item
7590      */
7591     disable : function(){
7592         Roo.fly(this.td).addClass("x-item-disabled");
7593         this.disabled = true;
7594     },
7595
7596     /**
7597      * Enables this item
7598      */
7599     enable : function(){
7600         Roo.fly(this.td).removeClass("x-item-disabled");
7601         this.disabled = false;
7602     }
7603 });
7604 // backwards compat
7605 Roo.ToolbarButton = Roo.Toolbar.Button;
7606
7607 /**
7608  * @class Roo.Toolbar.SplitButton
7609  * @extends Roo.SplitButton
7610  * A menu button that renders into a toolbar.
7611  * @constructor
7612  * Creates a new SplitButton
7613  * @param {Object} config A standard {@link Roo.SplitButton} config object
7614  */
7615 Roo.Toolbar.SplitButton = function(config){
7616     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7617 };
7618 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7619     render : function(td){
7620         this.td = td;
7621         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7622     },
7623     
7624     /**
7625      * Removes and destroys this button
7626      */
7627     destroy : function(){
7628         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7629         this.td.parentNode.removeChild(this.td);
7630     },
7631     
7632     /**
7633      * Shows this button
7634      */
7635     show: function(){
7636         this.hidden = false;
7637         this.td.style.display = "";
7638     },
7639     
7640     /**
7641      * Hides this button
7642      */
7643     hide: function(){
7644         this.hidden = true;
7645         this.td.style.display = "none";
7646     }
7647 });
7648
7649 // backwards compat
7650 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7651  * Based on:
7652  * Ext JS Library 1.1.1
7653  * Copyright(c) 2006-2007, Ext JS, LLC.
7654  *
7655  * Originally Released Under LGPL - original licence link has changed is not relivant.
7656  *
7657  * Fork - LGPL
7658  * <script type="text/javascript">
7659  */
7660  
7661 /**
7662  * @class Roo.PagingToolbar
7663  * @extends Roo.Toolbar
7664  * @children   Roo.Toolbar.Item Roo.form.Field
7665  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7666  * @constructor
7667  * Create a new PagingToolbar
7668  * @param {Object} config The config object
7669  */
7670 Roo.PagingToolbar = function(el, ds, config)
7671 {
7672     // old args format still supported... - xtype is prefered..
7673     if (typeof(el) == 'object' && el.xtype) {
7674         // created from xtype...
7675         config = el;
7676         ds = el.dataSource;
7677         el = config.container;
7678     }
7679     var items = [];
7680     if (config.items) {
7681         items = config.items;
7682         config.items = [];
7683     }
7684     
7685     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7686     this.ds = ds;
7687     this.cursor = 0;
7688     this.renderButtons(this.el);
7689     this.bind(ds);
7690     
7691     // supprot items array.
7692    
7693     Roo.each(items, function(e) {
7694         this.add(Roo.factory(e));
7695     },this);
7696     
7697 };
7698
7699 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7700    
7701     /**
7702      * @cfg {String/HTMLElement/Element} container
7703      * container The id or element that will contain the toolbar
7704      */
7705     /**
7706      * @cfg {Boolean} displayInfo
7707      * True to display the displayMsg (defaults to false)
7708      */
7709     
7710     
7711     /**
7712      * @cfg {Number} pageSize
7713      * The number of records to display per page (defaults to 20)
7714      */
7715     pageSize: 20,
7716     /**
7717      * @cfg {String} displayMsg
7718      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7719      */
7720     displayMsg : 'Displaying {0} - {1} of {2}',
7721     /**
7722      * @cfg {String} emptyMsg
7723      * The message to display when no records are found (defaults to "No data to display")
7724      */
7725     emptyMsg : 'No data to display',
7726     /**
7727      * Customizable piece of the default paging text (defaults to "Page")
7728      * @type String
7729      */
7730     beforePageText : "Page",
7731     /**
7732      * Customizable piece of the default paging text (defaults to "of %0")
7733      * @type String
7734      */
7735     afterPageText : "of {0}",
7736     /**
7737      * Customizable piece of the default paging text (defaults to "First Page")
7738      * @type String
7739      */
7740     firstText : "First Page",
7741     /**
7742      * Customizable piece of the default paging text (defaults to "Previous Page")
7743      * @type String
7744      */
7745     prevText : "Previous Page",
7746     /**
7747      * Customizable piece of the default paging text (defaults to "Next Page")
7748      * @type String
7749      */
7750     nextText : "Next Page",
7751     /**
7752      * Customizable piece of the default paging text (defaults to "Last Page")
7753      * @type String
7754      */
7755     lastText : "Last Page",
7756     /**
7757      * Customizable piece of the default paging text (defaults to "Refresh")
7758      * @type String
7759      */
7760     refreshText : "Refresh",
7761
7762     // private
7763     renderButtons : function(el){
7764         Roo.PagingToolbar.superclass.render.call(this, el);
7765         this.first = this.addButton({
7766             tooltip: this.firstText,
7767             cls: "x-btn-icon x-grid-page-first",
7768             disabled: true,
7769             handler: this.onClick.createDelegate(this, ["first"])
7770         });
7771         this.prev = this.addButton({
7772             tooltip: this.prevText,
7773             cls: "x-btn-icon x-grid-page-prev",
7774             disabled: true,
7775             handler: this.onClick.createDelegate(this, ["prev"])
7776         });
7777         //this.addSeparator();
7778         this.add(this.beforePageText);
7779         this.field = Roo.get(this.addDom({
7780            tag: "input",
7781            type: "text",
7782            size: "3",
7783            value: "1",
7784            cls: "x-grid-page-number"
7785         }).el);
7786         this.field.on("keydown", this.onPagingKeydown, this);
7787         this.field.on("focus", function(){this.dom.select();});
7788         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7789         this.field.setHeight(18);
7790         //this.addSeparator();
7791         this.next = this.addButton({
7792             tooltip: this.nextText,
7793             cls: "x-btn-icon x-grid-page-next",
7794             disabled: true,
7795             handler: this.onClick.createDelegate(this, ["next"])
7796         });
7797         this.last = this.addButton({
7798             tooltip: this.lastText,
7799             cls: "x-btn-icon x-grid-page-last",
7800             disabled: true,
7801             handler: this.onClick.createDelegate(this, ["last"])
7802         });
7803         //this.addSeparator();
7804         this.loading = this.addButton({
7805             tooltip: this.refreshText,
7806             cls: "x-btn-icon x-grid-loading",
7807             handler: this.onClick.createDelegate(this, ["refresh"])
7808         });
7809
7810         if(this.displayInfo){
7811             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7812         }
7813     },
7814
7815     // private
7816     updateInfo : function(){
7817         if(this.displayEl){
7818             var count = this.ds.getCount();
7819             var msg = count == 0 ?
7820                 this.emptyMsg :
7821                 String.format(
7822                     this.displayMsg,
7823                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7824                 );
7825             this.displayEl.update(msg);
7826         }
7827     },
7828
7829     // private
7830     onLoad : function(ds, r, o){
7831        this.cursor = o.params ? o.params.start : 0;
7832        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7833
7834        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7835        this.field.dom.value = ap;
7836        this.first.setDisabled(ap == 1);
7837        this.prev.setDisabled(ap == 1);
7838        this.next.setDisabled(ap == ps);
7839        this.last.setDisabled(ap == ps);
7840        this.loading.enable();
7841        this.updateInfo();
7842     },
7843
7844     // private
7845     getPageData : function(){
7846         var total = this.ds.getTotalCount();
7847         return {
7848             total : total,
7849             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7850             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7851         };
7852     },
7853
7854     // private
7855     onLoadError : function(){
7856         this.loading.enable();
7857     },
7858
7859     // private
7860     onPagingKeydown : function(e){
7861         var k = e.getKey();
7862         var d = this.getPageData();
7863         if(k == e.RETURN){
7864             var v = this.field.dom.value, pageNum;
7865             if(!v || isNaN(pageNum = parseInt(v, 10))){
7866                 this.field.dom.value = d.activePage;
7867                 return;
7868             }
7869             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7870             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7871             e.stopEvent();
7872         }
7873         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))
7874         {
7875           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7876           this.field.dom.value = pageNum;
7877           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7878           e.stopEvent();
7879         }
7880         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7881         {
7882           var v = this.field.dom.value, pageNum; 
7883           var increment = (e.shiftKey) ? 10 : 1;
7884           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7885             increment *= -1;
7886           }
7887           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7888             this.field.dom.value = d.activePage;
7889             return;
7890           }
7891           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7892           {
7893             this.field.dom.value = parseInt(v, 10) + increment;
7894             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7895             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7896           }
7897           e.stopEvent();
7898         }
7899     },
7900
7901     // private
7902     beforeLoad : function(){
7903         if(this.loading){
7904             this.loading.disable();
7905         }
7906     },
7907
7908     // private
7909     onClick : function(which){
7910         var ds = this.ds;
7911         switch(which){
7912             case "first":
7913                 ds.load({params:{start: 0, limit: this.pageSize}});
7914             break;
7915             case "prev":
7916                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7917             break;
7918             case "next":
7919                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7920             break;
7921             case "last":
7922                 var total = ds.getTotalCount();
7923                 var extra = total % this.pageSize;
7924                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7925                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7926             break;
7927             case "refresh":
7928                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7929             break;
7930         }
7931     },
7932
7933     /**
7934      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7935      * @param {Roo.data.Store} store The data store to unbind
7936      */
7937     unbind : function(ds){
7938         ds.un("beforeload", this.beforeLoad, this);
7939         ds.un("load", this.onLoad, this);
7940         ds.un("loadexception", this.onLoadError, this);
7941         ds.un("remove", this.updateInfo, this);
7942         ds.un("add", this.updateInfo, this);
7943         this.ds = undefined;
7944     },
7945
7946     /**
7947      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7948      * @param {Roo.data.Store} store The data store to bind
7949      */
7950     bind : function(ds){
7951         ds.on("beforeload", this.beforeLoad, this);
7952         ds.on("load", this.onLoad, this);
7953         ds.on("loadexception", this.onLoadError, this);
7954         ds.on("remove", this.updateInfo, this);
7955         ds.on("add", this.updateInfo, this);
7956         this.ds = ds;
7957     }
7958 });/*
7959  * Based on:
7960  * Ext JS Library 1.1.1
7961  * Copyright(c) 2006-2007, Ext JS, LLC.
7962  *
7963  * Originally Released Under LGPL - original licence link has changed is not relivant.
7964  *
7965  * Fork - LGPL
7966  * <script type="text/javascript">
7967  */
7968
7969 /**
7970  * @class Roo.Resizable
7971  * @extends Roo.util.Observable
7972  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7973  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7974  * 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
7975  * the element will be wrapped for you automatically.</p>
7976  * <p>Here is the list of valid resize handles:</p>
7977  * <pre>
7978 Value   Description
7979 ------  -------------------
7980  'n'     north
7981  's'     south
7982  'e'     east
7983  'w'     west
7984  'nw'    northwest
7985  'sw'    southwest
7986  'se'    southeast
7987  'ne'    northeast
7988  'hd'    horizontal drag
7989  'all'   all
7990 </pre>
7991  * <p>Here's an example showing the creation of a typical Resizable:</p>
7992  * <pre><code>
7993 var resizer = new Roo.Resizable("element-id", {
7994     handles: 'all',
7995     minWidth: 200,
7996     minHeight: 100,
7997     maxWidth: 500,
7998     maxHeight: 400,
7999     pinned: true
8000 });
8001 resizer.on("resize", myHandler);
8002 </code></pre>
8003  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8004  * resizer.east.setDisplayed(false);</p>
8005  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8006  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8007  * resize operation's new size (defaults to [0, 0])
8008  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8009  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8010  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8011  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8012  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8013  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8014  * @cfg {Number} width The width of the element in pixels (defaults to null)
8015  * @cfg {Number} height The height of the element in pixels (defaults to null)
8016  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8017  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8018  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8019  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8020  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8021  * in favor of the handles config option (defaults to false)
8022  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8023  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8024  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8025  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8026  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8027  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8028  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8029  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8030  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8031  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8032  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8033  * @constructor
8034  * Create a new resizable component
8035  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8036  * @param {Object} config configuration options
8037   */
8038 Roo.Resizable = function(el, config)
8039 {
8040     this.el = Roo.get(el);
8041
8042     if(config && config.wrap){
8043         config.resizeChild = this.el;
8044         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8045         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8046         this.el.setStyle("overflow", "hidden");
8047         this.el.setPositioning(config.resizeChild.getPositioning());
8048         config.resizeChild.clearPositioning();
8049         if(!config.width || !config.height){
8050             var csize = config.resizeChild.getSize();
8051             this.el.setSize(csize.width, csize.height);
8052         }
8053         if(config.pinned && !config.adjustments){
8054             config.adjustments = "auto";
8055         }
8056     }
8057
8058     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8059     this.proxy.unselectable();
8060     this.proxy.enableDisplayMode('block');
8061
8062     Roo.apply(this, config);
8063
8064     if(this.pinned){
8065         this.disableTrackOver = true;
8066         this.el.addClass("x-resizable-pinned");
8067     }
8068     // if the element isn't positioned, make it relative
8069     var position = this.el.getStyle("position");
8070     if(position != "absolute" && position != "fixed"){
8071         this.el.setStyle("position", "relative");
8072     }
8073     if(!this.handles){ // no handles passed, must be legacy style
8074         this.handles = 's,e,se';
8075         if(this.multiDirectional){
8076             this.handles += ',n,w';
8077         }
8078     }
8079     if(this.handles == "all"){
8080         this.handles = "n s e w ne nw se sw";
8081     }
8082     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8083     var ps = Roo.Resizable.positions;
8084     for(var i = 0, len = hs.length; i < len; i++){
8085         if(hs[i] && ps[hs[i]]){
8086             var pos = ps[hs[i]];
8087             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8088         }
8089     }
8090     // legacy
8091     this.corner = this.southeast;
8092     
8093     // updateBox = the box can move..
8094     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8095         this.updateBox = true;
8096     }
8097
8098     this.activeHandle = null;
8099
8100     if(this.resizeChild){
8101         if(typeof this.resizeChild == "boolean"){
8102             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8103         }else{
8104             this.resizeChild = Roo.get(this.resizeChild, true);
8105         }
8106     }
8107     
8108     if(this.adjustments == "auto"){
8109         var rc = this.resizeChild;
8110         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8111         if(rc && (hw || hn)){
8112             rc.position("relative");
8113             rc.setLeft(hw ? hw.el.getWidth() : 0);
8114             rc.setTop(hn ? hn.el.getHeight() : 0);
8115         }
8116         this.adjustments = [
8117             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8118             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8119         ];
8120     }
8121
8122     if(this.draggable){
8123         this.dd = this.dynamic ?
8124             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8125         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8126     }
8127
8128     // public events
8129     this.addEvents({
8130         /**
8131          * @event beforeresize
8132          * Fired before resize is allowed. Set enabled to false to cancel resize.
8133          * @param {Roo.Resizable} this
8134          * @param {Roo.EventObject} e The mousedown event
8135          */
8136         "beforeresize" : true,
8137         /**
8138          * @event resizing
8139          * Fired a resizing.
8140          * @param {Roo.Resizable} this
8141          * @param {Number} x The new x position
8142          * @param {Number} y The new y position
8143          * @param {Number} w The new w width
8144          * @param {Number} h The new h hight
8145          * @param {Roo.EventObject} e The mouseup event
8146          */
8147         "resizing" : true,
8148         /**
8149          * @event resize
8150          * Fired after a resize.
8151          * @param {Roo.Resizable} this
8152          * @param {Number} width The new width
8153          * @param {Number} height The new height
8154          * @param {Roo.EventObject} e The mouseup event
8155          */
8156         "resize" : true
8157     });
8158
8159     if(this.width !== null && this.height !== null){
8160         this.resizeTo(this.width, this.height);
8161     }else{
8162         this.updateChildSize();
8163     }
8164     if(Roo.isIE){
8165         this.el.dom.style.zoom = 1;
8166     }
8167     Roo.Resizable.superclass.constructor.call(this);
8168 };
8169
8170 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8171         resizeChild : false,
8172         adjustments : [0, 0],
8173         minWidth : 5,
8174         minHeight : 5,
8175         maxWidth : 10000,
8176         maxHeight : 10000,
8177         enabled : true,
8178         animate : false,
8179         duration : .35,
8180         dynamic : false,
8181         handles : false,
8182         multiDirectional : false,
8183         disableTrackOver : false,
8184         easing : 'easeOutStrong',
8185         widthIncrement : 0,
8186         heightIncrement : 0,
8187         pinned : false,
8188         width : null,
8189         height : null,
8190         preserveRatio : false,
8191         transparent: false,
8192         minX: 0,
8193         minY: 0,
8194         draggable: false,
8195
8196         /**
8197          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8198          */
8199         constrainTo: undefined,
8200         /**
8201          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8202          */
8203         resizeRegion: undefined,
8204
8205
8206     /**
8207      * Perform a manual resize
8208      * @param {Number} width
8209      * @param {Number} height
8210      */
8211     resizeTo : function(width, height){
8212         this.el.setSize(width, height);
8213         this.updateChildSize();
8214         this.fireEvent("resize", this, width, height, null);
8215     },
8216
8217     // private
8218     startSizing : function(e, handle){
8219         this.fireEvent("beforeresize", this, e);
8220         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8221
8222             if(!this.overlay){
8223                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8224                 this.overlay.unselectable();
8225                 this.overlay.enableDisplayMode("block");
8226                 this.overlay.on("mousemove", this.onMouseMove, this);
8227                 this.overlay.on("mouseup", this.onMouseUp, this);
8228             }
8229             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8230
8231             this.resizing = true;
8232             this.startBox = this.el.getBox();
8233             this.startPoint = e.getXY();
8234             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8235                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8236
8237             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8238             this.overlay.show();
8239
8240             if(this.constrainTo) {
8241                 var ct = Roo.get(this.constrainTo);
8242                 this.resizeRegion = ct.getRegion().adjust(
8243                     ct.getFrameWidth('t'),
8244                     ct.getFrameWidth('l'),
8245                     -ct.getFrameWidth('b'),
8246                     -ct.getFrameWidth('r')
8247                 );
8248             }
8249
8250             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8251             this.proxy.show();
8252             this.proxy.setBox(this.startBox);
8253             if(!this.dynamic){
8254                 this.proxy.setStyle('visibility', 'visible');
8255             }
8256         }
8257     },
8258
8259     // private
8260     onMouseDown : function(handle, e){
8261         if(this.enabled){
8262             e.stopEvent();
8263             this.activeHandle = handle;
8264             this.startSizing(e, handle);
8265         }
8266     },
8267
8268     // private
8269     onMouseUp : function(e){
8270         var size = this.resizeElement();
8271         this.resizing = false;
8272         this.handleOut();
8273         this.overlay.hide();
8274         this.proxy.hide();
8275         this.fireEvent("resize", this, size.width, size.height, e);
8276     },
8277
8278     // private
8279     updateChildSize : function(){
8280         
8281         if(this.resizeChild){
8282             var el = this.el;
8283             var child = this.resizeChild;
8284             var adj = this.adjustments;
8285             if(el.dom.offsetWidth){
8286                 var b = el.getSize(true);
8287                 child.setSize(b.width+adj[0], b.height+adj[1]);
8288             }
8289             // Second call here for IE
8290             // The first call enables instant resizing and
8291             // the second call corrects scroll bars if they
8292             // exist
8293             if(Roo.isIE){
8294                 setTimeout(function(){
8295                     if(el.dom.offsetWidth){
8296                         var b = el.getSize(true);
8297                         child.setSize(b.width+adj[0], b.height+adj[1]);
8298                     }
8299                 }, 10);
8300             }
8301         }
8302     },
8303
8304     // private
8305     snap : function(value, inc, min){
8306         if(!inc || !value) {
8307             return value;
8308         }
8309         var newValue = value;
8310         var m = value % inc;
8311         if(m > 0){
8312             if(m > (inc/2)){
8313                 newValue = value + (inc-m);
8314             }else{
8315                 newValue = value - m;
8316             }
8317         }
8318         return Math.max(min, newValue);
8319     },
8320
8321     // private
8322     resizeElement : function(){
8323         var box = this.proxy.getBox();
8324         if(this.updateBox){
8325             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8326         }else{
8327             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8328         }
8329         this.updateChildSize();
8330         if(!this.dynamic){
8331             this.proxy.hide();
8332         }
8333         return box;
8334     },
8335
8336     // private
8337     constrain : function(v, diff, m, mx){
8338         if(v - diff < m){
8339             diff = v - m;
8340         }else if(v - diff > mx){
8341             diff = mx - v;
8342         }
8343         return diff;
8344     },
8345
8346     // private
8347     onMouseMove : function(e){
8348         
8349         if(this.enabled){
8350             try{// try catch so if something goes wrong the user doesn't get hung
8351
8352             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8353                 return;
8354             }
8355
8356             //var curXY = this.startPoint;
8357             var curSize = this.curSize || this.startBox;
8358             var x = this.startBox.x, y = this.startBox.y;
8359             var ox = x, oy = y;
8360             var w = curSize.width, h = curSize.height;
8361             var ow = w, oh = h;
8362             var mw = this.minWidth, mh = this.minHeight;
8363             var mxw = this.maxWidth, mxh = this.maxHeight;
8364             var wi = this.widthIncrement;
8365             var hi = this.heightIncrement;
8366
8367             var eventXY = e.getXY();
8368             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8369             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8370
8371             var pos = this.activeHandle.position;
8372
8373             switch(pos){
8374                 case "east":
8375                     w += diffX;
8376                     w = Math.min(Math.max(mw, w), mxw);
8377                     break;
8378              
8379                 case "south":
8380                     h += diffY;
8381                     h = Math.min(Math.max(mh, h), mxh);
8382                     break;
8383                 case "southeast":
8384                     w += diffX;
8385                     h += diffY;
8386                     w = Math.min(Math.max(mw, w), mxw);
8387                     h = Math.min(Math.max(mh, h), mxh);
8388                     break;
8389                 case "north":
8390                     diffY = this.constrain(h, diffY, mh, mxh);
8391                     y += diffY;
8392                     h -= diffY;
8393                     break;
8394                 case "hdrag":
8395                     
8396                     if (wi) {
8397                         var adiffX = Math.abs(diffX);
8398                         var sub = (adiffX % wi); // how much 
8399                         if (sub > (wi/2)) { // far enough to snap
8400                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8401                         } else {
8402                             // remove difference.. 
8403                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8404                         }
8405                     }
8406                     x += diffX;
8407                     x = Math.max(this.minX, x);
8408                     break;
8409                 case "west":
8410                     diffX = this.constrain(w, diffX, mw, mxw);
8411                     x += diffX;
8412                     w -= diffX;
8413                     break;
8414                 case "northeast":
8415                     w += diffX;
8416                     w = Math.min(Math.max(mw, w), mxw);
8417                     diffY = this.constrain(h, diffY, mh, mxh);
8418                     y += diffY;
8419                     h -= diffY;
8420                     break;
8421                 case "northwest":
8422                     diffX = this.constrain(w, diffX, mw, mxw);
8423                     diffY = this.constrain(h, diffY, mh, mxh);
8424                     y += diffY;
8425                     h -= diffY;
8426                     x += diffX;
8427                     w -= diffX;
8428                     break;
8429                case "southwest":
8430                     diffX = this.constrain(w, diffX, mw, mxw);
8431                     h += diffY;
8432                     h = Math.min(Math.max(mh, h), mxh);
8433                     x += diffX;
8434                     w -= diffX;
8435                     break;
8436             }
8437
8438             var sw = this.snap(w, wi, mw);
8439             var sh = this.snap(h, hi, mh);
8440             if(sw != w || sh != h){
8441                 switch(pos){
8442                     case "northeast":
8443                         y -= sh - h;
8444                     break;
8445                     case "north":
8446                         y -= sh - h;
8447                         break;
8448                     case "southwest":
8449                         x -= sw - w;
8450                     break;
8451                     case "west":
8452                         x -= sw - w;
8453                         break;
8454                     case "northwest":
8455                         x -= sw - w;
8456                         y -= sh - h;
8457                     break;
8458                 }
8459                 w = sw;
8460                 h = sh;
8461             }
8462
8463             if(this.preserveRatio){
8464                 switch(pos){
8465                     case "southeast":
8466                     case "east":
8467                         h = oh * (w/ow);
8468                         h = Math.min(Math.max(mh, h), mxh);
8469                         w = ow * (h/oh);
8470                        break;
8471                     case "south":
8472                         w = ow * (h/oh);
8473                         w = Math.min(Math.max(mw, w), mxw);
8474                         h = oh * (w/ow);
8475                         break;
8476                     case "northeast":
8477                         w = ow * (h/oh);
8478                         w = Math.min(Math.max(mw, w), mxw);
8479                         h = oh * (w/ow);
8480                     break;
8481                     case "north":
8482                         var tw = w;
8483                         w = ow * (h/oh);
8484                         w = Math.min(Math.max(mw, w), mxw);
8485                         h = oh * (w/ow);
8486                         x += (tw - w) / 2;
8487                         break;
8488                     case "southwest":
8489                         h = oh * (w/ow);
8490                         h = Math.min(Math.max(mh, h), mxh);
8491                         var tw = w;
8492                         w = ow * (h/oh);
8493                         x += tw - w;
8494                         break;
8495                     case "west":
8496                         var th = h;
8497                         h = oh * (w/ow);
8498                         h = Math.min(Math.max(mh, h), mxh);
8499                         y += (th - h) / 2;
8500                         var tw = w;
8501                         w = ow * (h/oh);
8502                         x += tw - w;
8503                        break;
8504                     case "northwest":
8505                         var tw = w;
8506                         var th = h;
8507                         h = oh * (w/ow);
8508                         h = Math.min(Math.max(mh, h), mxh);
8509                         w = ow * (h/oh);
8510                         y += th - h;
8511                         x += tw - w;
8512                        break;
8513
8514                 }
8515             }
8516             if (pos == 'hdrag') {
8517                 w = ow;
8518             }
8519             this.proxy.setBounds(x, y, w, h);
8520             if(this.dynamic){
8521                 this.resizeElement();
8522             }
8523             }catch(e){}
8524         }
8525         this.fireEvent("resizing", this, x, y, w, h, e);
8526     },
8527
8528     // private
8529     handleOver : function(){
8530         if(this.enabled){
8531             this.el.addClass("x-resizable-over");
8532         }
8533     },
8534
8535     // private
8536     handleOut : function(){
8537         if(!this.resizing){
8538             this.el.removeClass("x-resizable-over");
8539         }
8540     },
8541
8542     /**
8543      * Returns the element this component is bound to.
8544      * @return {Roo.Element}
8545      */
8546     getEl : function(){
8547         return this.el;
8548     },
8549
8550     /**
8551      * Returns the resizeChild element (or null).
8552      * @return {Roo.Element}
8553      */
8554     getResizeChild : function(){
8555         return this.resizeChild;
8556     },
8557     groupHandler : function()
8558     {
8559         
8560     },
8561     /**
8562      * Destroys this resizable. If the element was wrapped and
8563      * removeEl is not true then the element remains.
8564      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8565      */
8566     destroy : function(removeEl){
8567         this.proxy.remove();
8568         if(this.overlay){
8569             this.overlay.removeAllListeners();
8570             this.overlay.remove();
8571         }
8572         var ps = Roo.Resizable.positions;
8573         for(var k in ps){
8574             if(typeof ps[k] != "function" && this[ps[k]]){
8575                 var h = this[ps[k]];
8576                 h.el.removeAllListeners();
8577                 h.el.remove();
8578             }
8579         }
8580         if(removeEl){
8581             this.el.update("");
8582             this.el.remove();
8583         }
8584     }
8585 });
8586
8587 // private
8588 // hash to map config positions to true positions
8589 Roo.Resizable.positions = {
8590     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8591     hd: "hdrag"
8592 };
8593
8594 // private
8595 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8596     if(!this.tpl){
8597         // only initialize the template if resizable is used
8598         var tpl = Roo.DomHelper.createTemplate(
8599             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8600         );
8601         tpl.compile();
8602         Roo.Resizable.Handle.prototype.tpl = tpl;
8603     }
8604     this.position = pos;
8605     this.rz = rz;
8606     // show north drag fro topdra
8607     var handlepos = pos == 'hdrag' ? 'north' : pos;
8608     
8609     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8610     if (pos == 'hdrag') {
8611         this.el.setStyle('cursor', 'pointer');
8612     }
8613     this.el.unselectable();
8614     if(transparent){
8615         this.el.setOpacity(0);
8616     }
8617     this.el.on("mousedown", this.onMouseDown, this);
8618     if(!disableTrackOver){
8619         this.el.on("mouseover", this.onMouseOver, this);
8620         this.el.on("mouseout", this.onMouseOut, this);
8621     }
8622 };
8623
8624 // private
8625 Roo.Resizable.Handle.prototype = {
8626     afterResize : function(rz){
8627         Roo.log('after?');
8628         // do nothing
8629     },
8630     // private
8631     onMouseDown : function(e){
8632         this.rz.onMouseDown(this, e);
8633     },
8634     // private
8635     onMouseOver : function(e){
8636         this.rz.handleOver(this, e);
8637     },
8638     // private
8639     onMouseOut : function(e){
8640         this.rz.handleOut(this, e);
8641     }
8642 };/*
8643  * Based on:
8644  * Ext JS Library 1.1.1
8645  * Copyright(c) 2006-2007, Ext JS, LLC.
8646  *
8647  * Originally Released Under LGPL - original licence link has changed is not relivant.
8648  *
8649  * Fork - LGPL
8650  * <script type="text/javascript">
8651  */
8652
8653 /**
8654  * @class Roo.Editor
8655  * @extends Roo.Component
8656  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8657  * @constructor
8658  * Create a new Editor
8659  * @param {Roo.form.Field} field The Field object (or descendant)
8660  * @param {Object} config The config object
8661  */
8662 Roo.Editor = function(field, config){
8663     Roo.Editor.superclass.constructor.call(this, config);
8664     this.field = field;
8665     this.addEvents({
8666         /**
8667              * @event beforestartedit
8668              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8669              * false from the handler of this event.
8670              * @param {Editor} this
8671              * @param {Roo.Element} boundEl The underlying element bound to this editor
8672              * @param {Mixed} value The field value being set
8673              */
8674         "beforestartedit" : true,
8675         /**
8676              * @event startedit
8677              * Fires when this editor is displayed
8678              * @param {Roo.Element} boundEl The underlying element bound to this editor
8679              * @param {Mixed} value The starting field value
8680              */
8681         "startedit" : true,
8682         /**
8683              * @event beforecomplete
8684              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8685              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8686              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8687              * event will not fire since no edit actually occurred.
8688              * @param {Editor} this
8689              * @param {Mixed} value The current field value
8690              * @param {Mixed} startValue The original field value
8691              */
8692         "beforecomplete" : true,
8693         /**
8694              * @event complete
8695              * Fires after editing is complete and any changed value has been written to the underlying field.
8696              * @param {Editor} this
8697              * @param {Mixed} value The current field value
8698              * @param {Mixed} startValue The original field value
8699              */
8700         "complete" : true,
8701         /**
8702          * @event specialkey
8703          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8704          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8705          * @param {Roo.form.Field} this
8706          * @param {Roo.EventObject} e The event object
8707          */
8708         "specialkey" : true
8709     });
8710 };
8711
8712 Roo.extend(Roo.Editor, Roo.Component, {
8713     /**
8714      * @cfg {Boolean/String} autosize
8715      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8716      * or "height" to adopt the height only (defaults to false)
8717      */
8718     /**
8719      * @cfg {Boolean} revertInvalid
8720      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8721      * validation fails (defaults to true)
8722      */
8723     /**
8724      * @cfg {Boolean} ignoreNoChange
8725      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8726      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8727      * will never be ignored.
8728      */
8729     /**
8730      * @cfg {Boolean} hideEl
8731      * False to keep the bound element visible while the editor is displayed (defaults to true)
8732      */
8733     /**
8734      * @cfg {Mixed} value
8735      * The data value of the underlying field (defaults to "")
8736      */
8737     value : "",
8738     /**
8739      * @cfg {String} alignment
8740      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8741      */
8742     alignment: "c-c?",
8743     /**
8744      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8745      * for bottom-right shadow (defaults to "frame")
8746      */
8747     shadow : "frame",
8748     /**
8749      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8750      */
8751     constrain : false,
8752     /**
8753      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8754      */
8755     completeOnEnter : false,
8756     /**
8757      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8758      */
8759     cancelOnEsc : false,
8760     /**
8761      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8762      */
8763     updateEl : false,
8764
8765     // private
8766     onRender : function(ct, position){
8767         this.el = new Roo.Layer({
8768             shadow: this.shadow,
8769             cls: "x-editor",
8770             parentEl : ct,
8771             shim : this.shim,
8772             shadowOffset:4,
8773             id: this.id,
8774             constrain: this.constrain
8775         });
8776         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8777         if(this.field.msgTarget != 'title'){
8778             this.field.msgTarget = 'qtip';
8779         }
8780         this.field.render(this.el);
8781         if(Roo.isGecko){
8782             this.field.el.dom.setAttribute('autocomplete', 'off');
8783         }
8784         this.field.on("specialkey", this.onSpecialKey, this);
8785         if(this.swallowKeys){
8786             this.field.el.swallowEvent(['keydown','keypress']);
8787         }
8788         this.field.show();
8789         this.field.on("blur", this.onBlur, this);
8790         if(this.field.grow){
8791             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8792         }
8793     },
8794
8795     onSpecialKey : function(field, e)
8796     {
8797         //Roo.log('editor onSpecialKey');
8798         if(this.completeOnEnter && e.getKey() == e.ENTER){
8799             e.stopEvent();
8800             this.completeEdit();
8801             return;
8802         }
8803         // do not fire special key otherwise it might hide close the editor...
8804         if(e.getKey() == e.ENTER){    
8805             return;
8806         }
8807         if(this.cancelOnEsc && e.getKey() == e.ESC){
8808             this.cancelEdit();
8809             return;
8810         } 
8811         this.fireEvent('specialkey', field, e);
8812     
8813     },
8814
8815     /**
8816      * Starts the editing process and shows the editor.
8817      * @param {String/HTMLElement/Element} el The element to edit
8818      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8819       * to the innerHTML of el.
8820      */
8821     startEdit : function(el, value){
8822         if(this.editing){
8823             this.completeEdit();
8824         }
8825         this.boundEl = Roo.get(el);
8826         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8827         if(!this.rendered){
8828             this.render(this.parentEl || document.body);
8829         }
8830         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8831             return;
8832         }
8833         this.startValue = v;
8834         this.field.setValue(v);
8835         if(this.autoSize){
8836             var sz = this.boundEl.getSize();
8837             switch(this.autoSize){
8838                 case "width":
8839                 this.setSize(sz.width,  "");
8840                 break;
8841                 case "height":
8842                 this.setSize("",  sz.height);
8843                 break;
8844                 default:
8845                 this.setSize(sz.width,  sz.height);
8846             }
8847         }
8848         this.el.alignTo(this.boundEl, this.alignment);
8849         this.editing = true;
8850         if(Roo.QuickTips){
8851             Roo.QuickTips.disable();
8852         }
8853         this.show();
8854     },
8855
8856     /**
8857      * Sets the height and width of this editor.
8858      * @param {Number} width The new width
8859      * @param {Number} height The new height
8860      */
8861     setSize : function(w, h){
8862         this.field.setSize(w, h);
8863         if(this.el){
8864             this.el.sync();
8865         }
8866     },
8867
8868     /**
8869      * Realigns the editor to the bound field based on the current alignment config value.
8870      */
8871     realign : function(){
8872         this.el.alignTo(this.boundEl, this.alignment);
8873     },
8874
8875     /**
8876      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8877      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8878      */
8879     completeEdit : function(remainVisible){
8880         if(!this.editing){
8881             return;
8882         }
8883         var v = this.getValue();
8884         if(this.revertInvalid !== false && !this.field.isValid()){
8885             v = this.startValue;
8886             this.cancelEdit(true);
8887         }
8888         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8889             this.editing = false;
8890             this.hide();
8891             return;
8892         }
8893         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8894             this.editing = false;
8895             if(this.updateEl && this.boundEl){
8896                 this.boundEl.update(v);
8897             }
8898             if(remainVisible !== true){
8899                 this.hide();
8900             }
8901             this.fireEvent("complete", this, v, this.startValue);
8902         }
8903     },
8904
8905     // private
8906     onShow : function(){
8907         this.el.show();
8908         if(this.hideEl !== false){
8909             this.boundEl.hide();
8910         }
8911         this.field.show();
8912         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8913             this.fixIEFocus = true;
8914             this.deferredFocus.defer(50, this);
8915         }else{
8916             this.field.focus();
8917         }
8918         this.fireEvent("startedit", this.boundEl, this.startValue);
8919     },
8920
8921     deferredFocus : function(){
8922         if(this.editing){
8923             this.field.focus();
8924         }
8925     },
8926
8927     /**
8928      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8929      * reverted to the original starting value.
8930      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8931      * cancel (defaults to false)
8932      */
8933     cancelEdit : function(remainVisible){
8934         if(this.editing){
8935             this.setValue(this.startValue);
8936             if(remainVisible !== true){
8937                 this.hide();
8938             }
8939         }
8940     },
8941
8942     // private
8943     onBlur : function(){
8944         if(this.allowBlur !== true && this.editing){
8945             this.completeEdit();
8946         }
8947     },
8948
8949     // private
8950     onHide : function(){
8951         if(this.editing){
8952             this.completeEdit();
8953             return;
8954         }
8955         this.field.blur();
8956         if(this.field.collapse){
8957             this.field.collapse();
8958         }
8959         this.el.hide();
8960         if(this.hideEl !== false){
8961             this.boundEl.show();
8962         }
8963         if(Roo.QuickTips){
8964             Roo.QuickTips.enable();
8965         }
8966     },
8967
8968     /**
8969      * Sets the data value of the editor
8970      * @param {Mixed} value Any valid value supported by the underlying field
8971      */
8972     setValue : function(v){
8973         this.field.setValue(v);
8974     },
8975
8976     /**
8977      * Gets the data value of the editor
8978      * @return {Mixed} The data value
8979      */
8980     getValue : function(){
8981         return this.field.getValue();
8982     }
8983 });/*
8984  * Based on:
8985  * Ext JS Library 1.1.1
8986  * Copyright(c) 2006-2007, Ext JS, LLC.
8987  *
8988  * Originally Released Under LGPL - original licence link has changed is not relivant.
8989  *
8990  * Fork - LGPL
8991  * <script type="text/javascript">
8992  */
8993  
8994 /**
8995  * @class Roo.BasicDialog
8996  * @extends Roo.util.Observable
8997  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8998  * <pre><code>
8999 var dlg = new Roo.BasicDialog("my-dlg", {
9000     height: 200,
9001     width: 300,
9002     minHeight: 100,
9003     minWidth: 150,
9004     modal: true,
9005     proxyDrag: true,
9006     shadow: true
9007 });
9008 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9009 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9010 dlg.addButton('Cancel', dlg.hide, dlg);
9011 dlg.show();
9012 </code></pre>
9013   <b>A Dialog should always be a direct child of the body element.</b>
9014  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9015  * @cfg {String} title Default text to display in the title bar (defaults to null)
9016  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9017  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9018  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9019  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9020  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9021  * (defaults to null with no animation)
9022  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9023  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9024  * property for valid values (defaults to 'all')
9025  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9026  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9027  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9028  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9029  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9030  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9031  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9032  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9033  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9034  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9035  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9036  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9037  * draggable = true (defaults to false)
9038  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9039  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9040  * shadow (defaults to false)
9041  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9042  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9043  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9044  * @cfg {Array} buttons Array of buttons
9045  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9046  * @constructor
9047  * Create a new BasicDialog.
9048  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9049  * @param {Object} config Configuration options
9050  */
9051 Roo.BasicDialog = function(el, config){
9052     this.el = Roo.get(el);
9053     var dh = Roo.DomHelper;
9054     if(!this.el && config && config.autoCreate){
9055         if(typeof config.autoCreate == "object"){
9056             if(!config.autoCreate.id){
9057                 config.autoCreate.id = el;
9058             }
9059             this.el = dh.append(document.body,
9060                         config.autoCreate, true);
9061         }else{
9062             this.el = dh.append(document.body,
9063                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9064         }
9065     }
9066     el = this.el;
9067     el.setDisplayed(true);
9068     el.hide = this.hideAction;
9069     this.id = el.id;
9070     el.addClass("x-dlg");
9071
9072     Roo.apply(this, config);
9073
9074     this.proxy = el.createProxy("x-dlg-proxy");
9075     this.proxy.hide = this.hideAction;
9076     this.proxy.setOpacity(.5);
9077     this.proxy.hide();
9078
9079     if(config.width){
9080         el.setWidth(config.width);
9081     }
9082     if(config.height){
9083         el.setHeight(config.height);
9084     }
9085     this.size = el.getSize();
9086     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9087         this.xy = [config.x,config.y];
9088     }else{
9089         this.xy = el.getCenterXY(true);
9090     }
9091     /** The header element @type Roo.Element */
9092     this.header = el.child("> .x-dlg-hd");
9093     /** The body element @type Roo.Element */
9094     this.body = el.child("> .x-dlg-bd");
9095     /** The footer element @type Roo.Element */
9096     this.footer = el.child("> .x-dlg-ft");
9097
9098     if(!this.header){
9099         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9100     }
9101     if(!this.body){
9102         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9103     }
9104
9105     this.header.unselectable();
9106     if(this.title){
9107         this.header.update(this.title);
9108     }
9109     // this element allows the dialog to be focused for keyboard event
9110     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9111     this.focusEl.swallowEvent("click", true);
9112
9113     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9114
9115     // wrap the body and footer for special rendering
9116     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9117     if(this.footer){
9118         this.bwrap.dom.appendChild(this.footer.dom);
9119     }
9120
9121     this.bg = this.el.createChild({
9122         tag: "div", cls:"x-dlg-bg",
9123         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9124     });
9125     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9126
9127
9128     if(this.autoScroll !== false && !this.autoTabs){
9129         this.body.setStyle("overflow", "auto");
9130     }
9131
9132     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9133
9134     if(this.closable !== false){
9135         this.el.addClass("x-dlg-closable");
9136         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9137         this.close.on("click", this.closeClick, this);
9138         this.close.addClassOnOver("x-dlg-close-over");
9139     }
9140     if(this.collapsible !== false){
9141         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9142         this.collapseBtn.on("click", this.collapseClick, this);
9143         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9144         this.header.on("dblclick", this.collapseClick, this);
9145     }
9146     if(this.resizable !== false){
9147         this.el.addClass("x-dlg-resizable");
9148         this.resizer = new Roo.Resizable(el, {
9149             minWidth: this.minWidth || 80,
9150             minHeight:this.minHeight || 80,
9151             handles: this.resizeHandles || "all",
9152             pinned: true
9153         });
9154         this.resizer.on("beforeresize", this.beforeResize, this);
9155         this.resizer.on("resize", this.onResize, this);
9156     }
9157     if(this.draggable !== false){
9158         el.addClass("x-dlg-draggable");
9159         if (!this.proxyDrag) {
9160             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9161         }
9162         else {
9163             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9164         }
9165         dd.setHandleElId(this.header.id);
9166         dd.endDrag = this.endMove.createDelegate(this);
9167         dd.startDrag = this.startMove.createDelegate(this);
9168         dd.onDrag = this.onDrag.createDelegate(this);
9169         dd.scroll = false;
9170         this.dd = dd;
9171     }
9172     if(this.modal){
9173         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9174         this.mask.enableDisplayMode("block");
9175         this.mask.hide();
9176         this.el.addClass("x-dlg-modal");
9177     }
9178     if(this.shadow){
9179         this.shadow = new Roo.Shadow({
9180             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9181             offset : this.shadowOffset
9182         });
9183     }else{
9184         this.shadowOffset = 0;
9185     }
9186     if(Roo.useShims && this.shim !== false){
9187         this.shim = this.el.createShim();
9188         this.shim.hide = this.hideAction;
9189         this.shim.hide();
9190     }else{
9191         this.shim = false;
9192     }
9193     if(this.autoTabs){
9194         this.initTabs();
9195     }
9196     if (this.buttons) { 
9197         var bts= this.buttons;
9198         this.buttons = [];
9199         Roo.each(bts, function(b) {
9200             this.addButton(b);
9201         }, this);
9202     }
9203     
9204     
9205     this.addEvents({
9206         /**
9207          * @event keydown
9208          * Fires when a key is pressed
9209          * @param {Roo.BasicDialog} this
9210          * @param {Roo.EventObject} e
9211          */
9212         "keydown" : true,
9213         /**
9214          * @event move
9215          * Fires when this dialog is moved by the user.
9216          * @param {Roo.BasicDialog} this
9217          * @param {Number} x The new page X
9218          * @param {Number} y The new page Y
9219          */
9220         "move" : true,
9221         /**
9222          * @event resize
9223          * Fires when this dialog is resized by the user.
9224          * @param {Roo.BasicDialog} this
9225          * @param {Number} width The new width
9226          * @param {Number} height The new height
9227          */
9228         "resize" : true,
9229         /**
9230          * @event beforehide
9231          * Fires before this dialog is hidden.
9232          * @param {Roo.BasicDialog} this
9233          */
9234         "beforehide" : true,
9235         /**
9236          * @event hide
9237          * Fires when this dialog is hidden.
9238          * @param {Roo.BasicDialog} this
9239          */
9240         "hide" : true,
9241         /**
9242          * @event beforeshow
9243          * Fires before this dialog is shown.
9244          * @param {Roo.BasicDialog} this
9245          */
9246         "beforeshow" : true,
9247         /**
9248          * @event show
9249          * Fires when this dialog is shown.
9250          * @param {Roo.BasicDialog} this
9251          */
9252         "show" : true
9253     });
9254     el.on("keydown", this.onKeyDown, this);
9255     el.on("mousedown", this.toFront, this);
9256     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9257     this.el.hide();
9258     Roo.DialogManager.register(this);
9259     Roo.BasicDialog.superclass.constructor.call(this);
9260 };
9261
9262 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9263     shadowOffset: Roo.isIE ? 6 : 5,
9264     minHeight: 80,
9265     minWidth: 200,
9266     minButtonWidth: 75,
9267     defaultButton: null,
9268     buttonAlign: "right",
9269     tabTag: 'div',
9270     firstShow: true,
9271
9272     /**
9273      * Sets the dialog title text
9274      * @param {String} text The title text to display
9275      * @return {Roo.BasicDialog} this
9276      */
9277     setTitle : function(text){
9278         this.header.update(text);
9279         return this;
9280     },
9281
9282     // private
9283     closeClick : function(){
9284         this.hide();
9285     },
9286
9287     // private
9288     collapseClick : function(){
9289         this[this.collapsed ? "expand" : "collapse"]();
9290     },
9291
9292     /**
9293      * Collapses the dialog to its minimized state (only the title bar is visible).
9294      * Equivalent to the user clicking the collapse dialog button.
9295      */
9296     collapse : function(){
9297         if(!this.collapsed){
9298             this.collapsed = true;
9299             this.el.addClass("x-dlg-collapsed");
9300             this.restoreHeight = this.el.getHeight();
9301             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9302         }
9303     },
9304
9305     /**
9306      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9307      * clicking the expand dialog button.
9308      */
9309     expand : function(){
9310         if(this.collapsed){
9311             this.collapsed = false;
9312             this.el.removeClass("x-dlg-collapsed");
9313             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9314         }
9315     },
9316
9317     /**
9318      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9319      * @return {Roo.TabPanel} The tabs component
9320      */
9321     initTabs : function(){
9322         var tabs = this.getTabs();
9323         while(tabs.getTab(0)){
9324             tabs.removeTab(0);
9325         }
9326         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9327             var dom = el.dom;
9328             tabs.addTab(Roo.id(dom), dom.title);
9329             dom.title = "";
9330         });
9331         tabs.activate(0);
9332         return tabs;
9333     },
9334
9335     // private
9336     beforeResize : function(){
9337         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9338     },
9339
9340     // private
9341     onResize : function(){
9342         this.refreshSize();
9343         this.syncBodyHeight();
9344         this.adjustAssets();
9345         this.focus();
9346         this.fireEvent("resize", this, this.size.width, this.size.height);
9347     },
9348
9349     // private
9350     onKeyDown : function(e){
9351         if(this.isVisible()){
9352             this.fireEvent("keydown", this, e);
9353         }
9354     },
9355
9356     /**
9357      * Resizes the dialog.
9358      * @param {Number} width
9359      * @param {Number} height
9360      * @return {Roo.BasicDialog} this
9361      */
9362     resizeTo : function(width, height){
9363         this.el.setSize(width, height);
9364         this.size = {width: width, height: height};
9365         this.syncBodyHeight();
9366         if(this.fixedcenter){
9367             this.center();
9368         }
9369         if(this.isVisible()){
9370             this.constrainXY();
9371             this.adjustAssets();
9372         }
9373         this.fireEvent("resize", this, width, height);
9374         return this;
9375     },
9376
9377
9378     /**
9379      * Resizes the dialog to fit the specified content size.
9380      * @param {Number} width
9381      * @param {Number} height
9382      * @return {Roo.BasicDialog} this
9383      */
9384     setContentSize : function(w, h){
9385         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9386         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9387         //if(!this.el.isBorderBox()){
9388             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9389             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9390         //}
9391         if(this.tabs){
9392             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9393             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9394         }
9395         this.resizeTo(w, h);
9396         return this;
9397     },
9398
9399     /**
9400      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9401      * executed in response to a particular key being pressed while the dialog is active.
9402      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9403      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9404      * @param {Function} fn The function to call
9405      * @param {Object} scope (optional) The scope of the function
9406      * @return {Roo.BasicDialog} this
9407      */
9408     addKeyListener : function(key, fn, scope){
9409         var keyCode, shift, ctrl, alt;
9410         if(typeof key == "object" && !(key instanceof Array)){
9411             keyCode = key["key"];
9412             shift = key["shift"];
9413             ctrl = key["ctrl"];
9414             alt = key["alt"];
9415         }else{
9416             keyCode = key;
9417         }
9418         var handler = function(dlg, e){
9419             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9420                 var k = e.getKey();
9421                 if(keyCode instanceof Array){
9422                     for(var i = 0, len = keyCode.length; i < len; i++){
9423                         if(keyCode[i] == k){
9424                           fn.call(scope || window, dlg, k, e);
9425                           return;
9426                         }
9427                     }
9428                 }else{
9429                     if(k == keyCode){
9430                         fn.call(scope || window, dlg, k, e);
9431                     }
9432                 }
9433             }
9434         };
9435         this.on("keydown", handler);
9436         return this;
9437     },
9438
9439     /**
9440      * Returns the TabPanel component (creates it if it doesn't exist).
9441      * Note: If you wish to simply check for the existence of tabs without creating them,
9442      * check for a null 'tabs' property.
9443      * @return {Roo.TabPanel} The tabs component
9444      */
9445     getTabs : function(){
9446         if(!this.tabs){
9447             this.el.addClass("x-dlg-auto-tabs");
9448             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9449             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9450         }
9451         return this.tabs;
9452     },
9453
9454     /**
9455      * Adds a button to the footer section of the dialog.
9456      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9457      * object or a valid Roo.DomHelper element config
9458      * @param {Function} handler The function called when the button is clicked
9459      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9460      * @return {Roo.Button} The new button
9461      */
9462     addButton : function(config, handler, scope){
9463         var dh = Roo.DomHelper;
9464         if(!this.footer){
9465             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9466         }
9467         if(!this.btnContainer){
9468             var tb = this.footer.createChild({
9469
9470                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9471                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9472             }, null, true);
9473             this.btnContainer = tb.firstChild.firstChild.firstChild;
9474         }
9475         var bconfig = {
9476             handler: handler,
9477             scope: scope,
9478             minWidth: this.minButtonWidth,
9479             hideParent:true
9480         };
9481         if(typeof config == "string"){
9482             bconfig.text = config;
9483         }else{
9484             if(config.tag){
9485                 bconfig.dhconfig = config;
9486             }else{
9487                 Roo.apply(bconfig, config);
9488             }
9489         }
9490         var fc = false;
9491         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9492             bconfig.position = Math.max(0, bconfig.position);
9493             fc = this.btnContainer.childNodes[bconfig.position];
9494         }
9495          
9496         var btn = new Roo.Button(
9497             fc ? 
9498                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9499                 : this.btnContainer.appendChild(document.createElement("td")),
9500             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9501             bconfig
9502         );
9503         this.syncBodyHeight();
9504         if(!this.buttons){
9505             /**
9506              * Array of all the buttons that have been added to this dialog via addButton
9507              * @type Array
9508              */
9509             this.buttons = [];
9510         }
9511         this.buttons.push(btn);
9512         return btn;
9513     },
9514
9515     /**
9516      * Sets the default button to be focused when the dialog is displayed.
9517      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9518      * @return {Roo.BasicDialog} this
9519      */
9520     setDefaultButton : function(btn){
9521         this.defaultButton = btn;
9522         return this;
9523     },
9524
9525     // private
9526     getHeaderFooterHeight : function(safe){
9527         var height = 0;
9528         if(this.header){
9529            height += this.header.getHeight();
9530         }
9531         if(this.footer){
9532            var fm = this.footer.getMargins();
9533             height += (this.footer.getHeight()+fm.top+fm.bottom);
9534         }
9535         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9536         height += this.centerBg.getPadding("tb");
9537         return height;
9538     },
9539
9540     // private
9541     syncBodyHeight : function()
9542     {
9543         var bd = this.body, // the text
9544             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9545             bw = this.bwrap;
9546         var height = this.size.height - this.getHeaderFooterHeight(false);
9547         bd.setHeight(height-bd.getMargins("tb"));
9548         var hh = this.header.getHeight();
9549         var h = this.size.height-hh;
9550         cb.setHeight(h);
9551         
9552         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9553         bw.setHeight(h-cb.getPadding("tb"));
9554         
9555         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9556         bd.setWidth(bw.getWidth(true));
9557         if(this.tabs){
9558             this.tabs.syncHeight();
9559             if(Roo.isIE){
9560                 this.tabs.el.repaint();
9561             }
9562         }
9563     },
9564
9565     /**
9566      * Restores the previous state of the dialog if Roo.state is configured.
9567      * @return {Roo.BasicDialog} this
9568      */
9569     restoreState : function(){
9570         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9571         if(box && box.width){
9572             this.xy = [box.x, box.y];
9573             this.resizeTo(box.width, box.height);
9574         }
9575         return this;
9576     },
9577
9578     // private
9579     beforeShow : function(){
9580         this.expand();
9581         if(this.fixedcenter){
9582             this.xy = this.el.getCenterXY(true);
9583         }
9584         if(this.modal){
9585             Roo.get(document.body).addClass("x-body-masked");
9586             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9587             this.mask.show();
9588         }
9589         this.constrainXY();
9590     },
9591
9592     // private
9593     animShow : function(){
9594         var b = Roo.get(this.animateTarget).getBox();
9595         this.proxy.setSize(b.width, b.height);
9596         this.proxy.setLocation(b.x, b.y);
9597         this.proxy.show();
9598         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9599                     true, .35, this.showEl.createDelegate(this));
9600     },
9601
9602     /**
9603      * Shows the dialog.
9604      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9605      * @return {Roo.BasicDialog} this
9606      */
9607     show : function(animateTarget){
9608         if (this.fireEvent("beforeshow", this) === false){
9609             return;
9610         }
9611         if(this.syncHeightBeforeShow){
9612             this.syncBodyHeight();
9613         }else if(this.firstShow){
9614             this.firstShow = false;
9615             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9616         }
9617         this.animateTarget = animateTarget || this.animateTarget;
9618         if(!this.el.isVisible()){
9619             this.beforeShow();
9620             if(this.animateTarget && Roo.get(this.animateTarget)){
9621                 this.animShow();
9622             }else{
9623                 this.showEl();
9624             }
9625         }
9626         return this;
9627     },
9628
9629     // private
9630     showEl : function(){
9631         this.proxy.hide();
9632         this.el.setXY(this.xy);
9633         this.el.show();
9634         this.adjustAssets(true);
9635         this.toFront();
9636         this.focus();
9637         // IE peekaboo bug - fix found by Dave Fenwick
9638         if(Roo.isIE){
9639             this.el.repaint();
9640         }
9641         this.fireEvent("show", this);
9642     },
9643
9644     /**
9645      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9646      * dialog itself will receive focus.
9647      */
9648     focus : function(){
9649         if(this.defaultButton){
9650             this.defaultButton.focus();
9651         }else{
9652             this.focusEl.focus();
9653         }
9654     },
9655
9656     // private
9657     constrainXY : function(){
9658         if(this.constraintoviewport !== false){
9659             if(!this.viewSize){
9660                 if(this.container){
9661                     var s = this.container.getSize();
9662                     this.viewSize = [s.width, s.height];
9663                 }else{
9664                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9665                 }
9666             }
9667             var s = Roo.get(this.container||document).getScroll();
9668
9669             var x = this.xy[0], y = this.xy[1];
9670             var w = this.size.width, h = this.size.height;
9671             var vw = this.viewSize[0], vh = this.viewSize[1];
9672             // only move it if it needs it
9673             var moved = false;
9674             // first validate right/bottom
9675             if(x + w > vw+s.left){
9676                 x = vw - w;
9677                 moved = true;
9678             }
9679             if(y + h > vh+s.top){
9680                 y = vh - h;
9681                 moved = true;
9682             }
9683             // then make sure top/left isn't negative
9684             if(x < s.left){
9685                 x = s.left;
9686                 moved = true;
9687             }
9688             if(y < s.top){
9689                 y = s.top;
9690                 moved = true;
9691             }
9692             if(moved){
9693                 // cache xy
9694                 this.xy = [x, y];
9695                 if(this.isVisible()){
9696                     this.el.setLocation(x, y);
9697                     this.adjustAssets();
9698                 }
9699             }
9700         }
9701     },
9702
9703     // private
9704     onDrag : function(){
9705         if(!this.proxyDrag){
9706             this.xy = this.el.getXY();
9707             this.adjustAssets();
9708         }
9709     },
9710
9711     // private
9712     adjustAssets : function(doShow){
9713         var x = this.xy[0], y = this.xy[1];
9714         var w = this.size.width, h = this.size.height;
9715         if(doShow === true){
9716             if(this.shadow){
9717                 this.shadow.show(this.el);
9718             }
9719             if(this.shim){
9720                 this.shim.show();
9721             }
9722         }
9723         if(this.shadow && this.shadow.isVisible()){
9724             this.shadow.show(this.el);
9725         }
9726         if(this.shim && this.shim.isVisible()){
9727             this.shim.setBounds(x, y, w, h);
9728         }
9729     },
9730
9731     // private
9732     adjustViewport : function(w, h){
9733         if(!w || !h){
9734             w = Roo.lib.Dom.getViewWidth();
9735             h = Roo.lib.Dom.getViewHeight();
9736         }
9737         // cache the size
9738         this.viewSize = [w, h];
9739         if(this.modal && this.mask.isVisible()){
9740             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9741             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9742         }
9743         if(this.isVisible()){
9744             this.constrainXY();
9745         }
9746     },
9747
9748     /**
9749      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9750      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9751      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9752      */
9753     destroy : function(removeEl){
9754         if(this.isVisible()){
9755             this.animateTarget = null;
9756             this.hide();
9757         }
9758         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9759         if(this.tabs){
9760             this.tabs.destroy(removeEl);
9761         }
9762         Roo.destroy(
9763              this.shim,
9764              this.proxy,
9765              this.resizer,
9766              this.close,
9767              this.mask
9768         );
9769         if(this.dd){
9770             this.dd.unreg();
9771         }
9772         if(this.buttons){
9773            for(var i = 0, len = this.buttons.length; i < len; i++){
9774                this.buttons[i].destroy();
9775            }
9776         }
9777         this.el.removeAllListeners();
9778         if(removeEl === true){
9779             this.el.update("");
9780             this.el.remove();
9781         }
9782         Roo.DialogManager.unregister(this);
9783     },
9784
9785     // private
9786     startMove : function(){
9787         if(this.proxyDrag){
9788             this.proxy.show();
9789         }
9790         if(this.constraintoviewport !== false){
9791             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9792         }
9793     },
9794
9795     // private
9796     endMove : function(){
9797         if(!this.proxyDrag){
9798             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9799         }else{
9800             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9801             this.proxy.hide();
9802         }
9803         this.refreshSize();
9804         this.adjustAssets();
9805         this.focus();
9806         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9807     },
9808
9809     /**
9810      * Brings this dialog to the front of any other visible dialogs
9811      * @return {Roo.BasicDialog} this
9812      */
9813     toFront : function(){
9814         Roo.DialogManager.bringToFront(this);
9815         return this;
9816     },
9817
9818     /**
9819      * Sends this dialog to the back (under) of any other visible dialogs
9820      * @return {Roo.BasicDialog} this
9821      */
9822     toBack : function(){
9823         Roo.DialogManager.sendToBack(this);
9824         return this;
9825     },
9826
9827     /**
9828      * Centers this dialog in the viewport
9829      * @return {Roo.BasicDialog} this
9830      */
9831     center : function(){
9832         var xy = this.el.getCenterXY(true);
9833         this.moveTo(xy[0], xy[1]);
9834         return this;
9835     },
9836
9837     /**
9838      * Moves the dialog's top-left corner to the specified point
9839      * @param {Number} x
9840      * @param {Number} y
9841      * @return {Roo.BasicDialog} this
9842      */
9843     moveTo : function(x, y){
9844         this.xy = [x,y];
9845         if(this.isVisible()){
9846             this.el.setXY(this.xy);
9847             this.adjustAssets();
9848         }
9849         return this;
9850     },
9851
9852     /**
9853      * Aligns the dialog to the specified element
9854      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9855      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9856      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9857      * @return {Roo.BasicDialog} this
9858      */
9859     alignTo : function(element, position, offsets){
9860         this.xy = this.el.getAlignToXY(element, position, offsets);
9861         if(this.isVisible()){
9862             this.el.setXY(this.xy);
9863             this.adjustAssets();
9864         }
9865         return this;
9866     },
9867
9868     /**
9869      * Anchors an element to another element and realigns it when the window is resized.
9870      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9871      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9872      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9873      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9874      * is a number, it is used as the buffer delay (defaults to 50ms).
9875      * @return {Roo.BasicDialog} this
9876      */
9877     anchorTo : function(el, alignment, offsets, monitorScroll){
9878         var action = function(){
9879             this.alignTo(el, alignment, offsets);
9880         };
9881         Roo.EventManager.onWindowResize(action, this);
9882         var tm = typeof monitorScroll;
9883         if(tm != 'undefined'){
9884             Roo.EventManager.on(window, 'scroll', action, this,
9885                 {buffer: tm == 'number' ? monitorScroll : 50});
9886         }
9887         action.call(this);
9888         return this;
9889     },
9890
9891     /**
9892      * Returns true if the dialog is visible
9893      * @return {Boolean}
9894      */
9895     isVisible : function(){
9896         return this.el.isVisible();
9897     },
9898
9899     // private
9900     animHide : function(callback){
9901         var b = Roo.get(this.animateTarget).getBox();
9902         this.proxy.show();
9903         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9904         this.el.hide();
9905         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9906                     this.hideEl.createDelegate(this, [callback]));
9907     },
9908
9909     /**
9910      * Hides the dialog.
9911      * @param {Function} callback (optional) Function to call when the dialog is hidden
9912      * @return {Roo.BasicDialog} this
9913      */
9914     hide : function(callback){
9915         if (this.fireEvent("beforehide", this) === false){
9916             return;
9917         }
9918         if(this.shadow){
9919             this.shadow.hide();
9920         }
9921         if(this.shim) {
9922           this.shim.hide();
9923         }
9924         // sometimes animateTarget seems to get set.. causing problems...
9925         // this just double checks..
9926         if(this.animateTarget && Roo.get(this.animateTarget)) {
9927            this.animHide(callback);
9928         }else{
9929             this.el.hide();
9930             this.hideEl(callback);
9931         }
9932         return this;
9933     },
9934
9935     // private
9936     hideEl : function(callback){
9937         this.proxy.hide();
9938         if(this.modal){
9939             this.mask.hide();
9940             Roo.get(document.body).removeClass("x-body-masked");
9941         }
9942         this.fireEvent("hide", this);
9943         if(typeof callback == "function"){
9944             callback();
9945         }
9946     },
9947
9948     // private
9949     hideAction : function(){
9950         this.setLeft("-10000px");
9951         this.setTop("-10000px");
9952         this.setStyle("visibility", "hidden");
9953     },
9954
9955     // private
9956     refreshSize : function(){
9957         this.size = this.el.getSize();
9958         this.xy = this.el.getXY();
9959         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9960     },
9961
9962     // private
9963     // z-index is managed by the DialogManager and may be overwritten at any time
9964     setZIndex : function(index){
9965         if(this.modal){
9966             this.mask.setStyle("z-index", index);
9967         }
9968         if(this.shim){
9969             this.shim.setStyle("z-index", ++index);
9970         }
9971         if(this.shadow){
9972             this.shadow.setZIndex(++index);
9973         }
9974         this.el.setStyle("z-index", ++index);
9975         if(this.proxy){
9976             this.proxy.setStyle("z-index", ++index);
9977         }
9978         if(this.resizer){
9979             this.resizer.proxy.setStyle("z-index", ++index);
9980         }
9981
9982         this.lastZIndex = index;
9983     },
9984
9985     /**
9986      * Returns the element for this dialog
9987      * @return {Roo.Element} The underlying dialog Element
9988      */
9989     getEl : function(){
9990         return this.el;
9991     }
9992 });
9993
9994 /**
9995  * @class Roo.DialogManager
9996  * Provides global access to BasicDialogs that have been created and
9997  * support for z-indexing (layering) multiple open dialogs.
9998  */
9999 Roo.DialogManager = function(){
10000     var list = {};
10001     var accessList = [];
10002     var front = null;
10003
10004     // private
10005     var sortDialogs = function(d1, d2){
10006         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10007     };
10008
10009     // private
10010     var orderDialogs = function(){
10011         accessList.sort(sortDialogs);
10012         var seed = Roo.DialogManager.zseed;
10013         for(var i = 0, len = accessList.length; i < len; i++){
10014             var dlg = accessList[i];
10015             if(dlg){
10016                 dlg.setZIndex(seed + (i*10));
10017             }
10018         }
10019     };
10020
10021     return {
10022         /**
10023          * The starting z-index for BasicDialogs (defaults to 9000)
10024          * @type Number The z-index value
10025          */
10026         zseed : 9000,
10027
10028         // private
10029         register : function(dlg){
10030             list[dlg.id] = dlg;
10031             accessList.push(dlg);
10032         },
10033
10034         // private
10035         unregister : function(dlg){
10036             delete list[dlg.id];
10037             var i=0;
10038             var len=0;
10039             if(!accessList.indexOf){
10040                 for(  i = 0, len = accessList.length; i < len; i++){
10041                     if(accessList[i] == dlg){
10042                         accessList.splice(i, 1);
10043                         return;
10044                     }
10045                 }
10046             }else{
10047                  i = accessList.indexOf(dlg);
10048                 if(i != -1){
10049                     accessList.splice(i, 1);
10050                 }
10051             }
10052         },
10053
10054         /**
10055          * Gets a registered dialog by id
10056          * @param {String/Object} id The id of the dialog or a dialog
10057          * @return {Roo.BasicDialog} this
10058          */
10059         get : function(id){
10060             return typeof id == "object" ? id : list[id];
10061         },
10062
10063         /**
10064          * Brings the specified dialog to the front
10065          * @param {String/Object} dlg The id of the dialog or a dialog
10066          * @return {Roo.BasicDialog} this
10067          */
10068         bringToFront : function(dlg){
10069             dlg = this.get(dlg);
10070             if(dlg != front){
10071                 front = dlg;
10072                 dlg._lastAccess = new Date().getTime();
10073                 orderDialogs();
10074             }
10075             return dlg;
10076         },
10077
10078         /**
10079          * Sends the specified dialog to the back
10080          * @param {String/Object} dlg The id of the dialog or a dialog
10081          * @return {Roo.BasicDialog} this
10082          */
10083         sendToBack : function(dlg){
10084             dlg = this.get(dlg);
10085             dlg._lastAccess = -(new Date().getTime());
10086             orderDialogs();
10087             return dlg;
10088         },
10089
10090         /**
10091          * Hides all dialogs
10092          */
10093         hideAll : function(){
10094             for(var id in list){
10095                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10096                     list[id].hide();
10097                 }
10098             }
10099         }
10100     };
10101 }();
10102
10103 /**
10104  * @class Roo.LayoutDialog
10105  * @extends Roo.BasicDialog
10106  * @children Roo.ContentPanel
10107  * @parent builder none
10108  * Dialog which provides adjustments for working with a layout in a Dialog.
10109  * Add your necessary layout config options to the dialog's config.<br>
10110  * Example usage (including a nested layout):
10111  * <pre><code>
10112 if(!dialog){
10113     dialog = new Roo.LayoutDialog("download-dlg", {
10114         modal: true,
10115         width:600,
10116         height:450,
10117         shadow:true,
10118         minWidth:500,
10119         minHeight:350,
10120         autoTabs:true,
10121         proxyDrag:true,
10122         // layout config merges with the dialog config
10123         center:{
10124             tabPosition: "top",
10125             alwaysShowTabs: true
10126         }
10127     });
10128     dialog.addKeyListener(27, dialog.hide, dialog);
10129     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10130     dialog.addButton("Build It!", this.getDownload, this);
10131
10132     // we can even add nested layouts
10133     var innerLayout = new Roo.BorderLayout("dl-inner", {
10134         east: {
10135             initialSize: 200,
10136             autoScroll:true,
10137             split:true
10138         },
10139         center: {
10140             autoScroll:true
10141         }
10142     });
10143     innerLayout.beginUpdate();
10144     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10145     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10146     innerLayout.endUpdate(true);
10147
10148     var layout = dialog.getLayout();
10149     layout.beginUpdate();
10150     layout.add("center", new Roo.ContentPanel("standard-panel",
10151                         {title: "Download the Source", fitToFrame:true}));
10152     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10153                {title: "Build your own roo.js"}));
10154     layout.getRegion("center").showPanel(sp);
10155     layout.endUpdate();
10156 }
10157 </code></pre>
10158     * @constructor
10159     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10160     * @param {Object} config configuration options
10161   */
10162 Roo.LayoutDialog = function(el, cfg){
10163     
10164     var config=  cfg;
10165     if (typeof(cfg) == 'undefined') {
10166         config = Roo.apply({}, el);
10167         // not sure why we use documentElement here.. - it should always be body.
10168         // IE7 borks horribly if we use documentElement.
10169         // webkit also does not like documentElement - it creates a body element...
10170         el = Roo.get( document.body || document.documentElement ).createChild();
10171         //config.autoCreate = true;
10172     }
10173     
10174     
10175     config.autoTabs = false;
10176     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10177     this.body.setStyle({overflow:"hidden", position:"relative"});
10178     this.layout = new Roo.BorderLayout(this.body.dom, config);
10179     this.layout.monitorWindowResize = false;
10180     this.el.addClass("x-dlg-auto-layout");
10181     // fix case when center region overwrites center function
10182     this.center = Roo.BasicDialog.prototype.center;
10183     this.on("show", this.layout.layout, this.layout, true);
10184     if (config.items) {
10185         var xitems = config.items;
10186         delete config.items;
10187         Roo.each(xitems, this.addxtype, this);
10188     }
10189     
10190     
10191 };
10192 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10193     
10194     
10195     /**
10196      * @cfg {Roo.LayoutRegion} east  
10197      */
10198     /**
10199      * @cfg {Roo.LayoutRegion} west
10200      */
10201     /**
10202      * @cfg {Roo.LayoutRegion} south
10203      */
10204     /**
10205      * @cfg {Roo.LayoutRegion} north
10206      */
10207     /**
10208      * @cfg {Roo.LayoutRegion} center
10209      */
10210     /**
10211      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10212      */
10213     
10214     
10215     /**
10216      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10217      * @deprecated
10218      */
10219     endUpdate : function(){
10220         this.layout.endUpdate();
10221     },
10222
10223     /**
10224      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10225      *  @deprecated
10226      */
10227     beginUpdate : function(){
10228         this.layout.beginUpdate();
10229     },
10230
10231     /**
10232      * Get the BorderLayout for this dialog
10233      * @return {Roo.BorderLayout}
10234      */
10235     getLayout : function(){
10236         return this.layout;
10237     },
10238
10239     showEl : function(){
10240         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10241         if(Roo.isIE7){
10242             this.layout.layout();
10243         }
10244     },
10245
10246     // private
10247     // Use the syncHeightBeforeShow config option to control this automatically
10248     syncBodyHeight : function(){
10249         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10250         if(this.layout){this.layout.layout();}
10251     },
10252     
10253       /**
10254      * Add an xtype element (actually adds to the layout.)
10255      * @return {Object} xdata xtype object data.
10256      */
10257     
10258     addxtype : function(c) {
10259         return this.layout.addxtype(c);
10260     }
10261 });/*
10262  * Based on:
10263  * Ext JS Library 1.1.1
10264  * Copyright(c) 2006-2007, Ext JS, LLC.
10265  *
10266  * Originally Released Under LGPL - original licence link has changed is not relivant.
10267  *
10268  * Fork - LGPL
10269  * <script type="text/javascript">
10270  */
10271  
10272 /**
10273  * @class Roo.MessageBox
10274  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10275  * Example usage:
10276  *<pre><code>
10277 // Basic alert:
10278 Roo.Msg.alert('Status', 'Changes saved successfully.');
10279
10280 // Prompt for user data:
10281 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10282     if (btn == 'ok'){
10283         // process text value...
10284     }
10285 });
10286
10287 // Show a dialog using config options:
10288 Roo.Msg.show({
10289    title:'Save Changes?',
10290    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10291    buttons: Roo.Msg.YESNOCANCEL,
10292    fn: processResult,
10293    animEl: 'elId'
10294 });
10295 </code></pre>
10296  * @static
10297  */
10298 Roo.MessageBox = function(){
10299     var dlg, opt, mask, waitTimer;
10300     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10301     var buttons, activeTextEl, bwidth;
10302
10303     // private
10304     var handleButton = function(button){
10305         dlg.hide();
10306         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10307     };
10308
10309     // private
10310     var handleHide = function(){
10311         if(opt && opt.cls){
10312             dlg.el.removeClass(opt.cls);
10313         }
10314         if(waitTimer){
10315             Roo.TaskMgr.stop(waitTimer);
10316             waitTimer = null;
10317         }
10318     };
10319
10320     // private
10321     var updateButtons = function(b){
10322         var width = 0;
10323         if(!b){
10324             buttons["ok"].hide();
10325             buttons["cancel"].hide();
10326             buttons["yes"].hide();
10327             buttons["no"].hide();
10328             dlg.footer.dom.style.display = 'none';
10329             return width;
10330         }
10331         dlg.footer.dom.style.display = '';
10332         for(var k in buttons){
10333             if(typeof buttons[k] != "function"){
10334                 if(b[k]){
10335                     buttons[k].show();
10336                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10337                     width += buttons[k].el.getWidth()+15;
10338                 }else{
10339                     buttons[k].hide();
10340                 }
10341             }
10342         }
10343         return width;
10344     };
10345
10346     // private
10347     var handleEsc = function(d, k, e){
10348         if(opt && opt.closable !== false){
10349             dlg.hide();
10350         }
10351         if(e){
10352             e.stopEvent();
10353         }
10354     };
10355
10356     return {
10357         /**
10358          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10359          * @return {Roo.BasicDialog} The BasicDialog element
10360          */
10361         getDialog : function(){
10362            if(!dlg){
10363                 dlg = new Roo.BasicDialog("x-msg-box", {
10364                     autoCreate : true,
10365                     shadow: true,
10366                     draggable: true,
10367                     resizable:false,
10368                     constraintoviewport:false,
10369                     fixedcenter:true,
10370                     collapsible : false,
10371                     shim:true,
10372                     modal: true,
10373                     width:400, height:100,
10374                     buttonAlign:"center",
10375                     closeClick : function(){
10376                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10377                             handleButton("no");
10378                         }else{
10379                             handleButton("cancel");
10380                         }
10381                     }
10382                 });
10383                 dlg.on("hide", handleHide);
10384                 mask = dlg.mask;
10385                 dlg.addKeyListener(27, handleEsc);
10386                 buttons = {};
10387                 var bt = this.buttonText;
10388                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10389                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10390                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10391                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10392                 bodyEl = dlg.body.createChild({
10393
10394                     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>'
10395                 });
10396                 msgEl = bodyEl.dom.firstChild;
10397                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10398                 textboxEl.enableDisplayMode();
10399                 textboxEl.addKeyListener([10,13], function(){
10400                     if(dlg.isVisible() && opt && opt.buttons){
10401                         if(opt.buttons.ok){
10402                             handleButton("ok");
10403                         }else if(opt.buttons.yes){
10404                             handleButton("yes");
10405                         }
10406                     }
10407                 });
10408                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10409                 textareaEl.enableDisplayMode();
10410                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10411                 progressEl.enableDisplayMode();
10412                 var pf = progressEl.dom.firstChild;
10413                 if (pf) {
10414                     pp = Roo.get(pf.firstChild);
10415                     pp.setHeight(pf.offsetHeight);
10416                 }
10417                 
10418             }
10419             return dlg;
10420         },
10421
10422         /**
10423          * Updates the message box body text
10424          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10425          * the XHTML-compliant non-breaking space character '&amp;#160;')
10426          * @return {Roo.MessageBox} This message box
10427          */
10428         updateText : function(text){
10429             if(!dlg.isVisible() && !opt.width){
10430                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10431             }
10432             msgEl.innerHTML = text || '&#160;';
10433       
10434             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10435             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10436             var w = Math.max(
10437                     Math.min(opt.width || cw , this.maxWidth), 
10438                     Math.max(opt.minWidth || this.minWidth, bwidth)
10439             );
10440             if(opt.prompt){
10441                 activeTextEl.setWidth(w);
10442             }
10443             if(dlg.isVisible()){
10444                 dlg.fixedcenter = false;
10445             }
10446             // to big, make it scroll. = But as usual stupid IE does not support
10447             // !important..
10448             
10449             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10450                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10451                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10452             } else {
10453                 bodyEl.dom.style.height = '';
10454                 bodyEl.dom.style.overflowY = '';
10455             }
10456             if (cw > w) {
10457                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10458             } else {
10459                 bodyEl.dom.style.overflowX = '';
10460             }
10461             
10462             dlg.setContentSize(w, bodyEl.getHeight());
10463             if(dlg.isVisible()){
10464                 dlg.fixedcenter = true;
10465             }
10466             return this;
10467         },
10468
10469         /**
10470          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10471          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10472          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10473          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10474          * @return {Roo.MessageBox} This message box
10475          */
10476         updateProgress : function(value, text){
10477             if(text){
10478                 this.updateText(text);
10479             }
10480             if (pp) { // weird bug on my firefox - for some reason this is not defined
10481                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10482             }
10483             return this;
10484         },        
10485
10486         /**
10487          * Returns true if the message box is currently displayed
10488          * @return {Boolean} True if the message box is visible, else false
10489          */
10490         isVisible : function(){
10491             return dlg && dlg.isVisible();  
10492         },
10493
10494         /**
10495          * Hides the message box if it is displayed
10496          */
10497         hide : function(){
10498             if(this.isVisible()){
10499                 dlg.hide();
10500             }  
10501         },
10502
10503         /**
10504          * Displays a new message box, or reinitializes an existing message box, based on the config options
10505          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10506          * The following config object properties are supported:
10507          * <pre>
10508 Property    Type             Description
10509 ----------  ---------------  ------------------------------------------------------------------------------------
10510 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10511                                    closes (defaults to undefined)
10512 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10513                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10514 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10515                                    progress and wait dialogs will ignore this property and always hide the
10516                                    close button as they can only be closed programmatically.
10517 cls               String           A custom CSS class to apply to the message box element
10518 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10519                                    displayed (defaults to 75)
10520 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10521                                    function will be btn (the name of the button that was clicked, if applicable,
10522                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10523                                    Progress and wait dialogs will ignore this option since they do not respond to
10524                                    user actions and can only be closed programmatically, so any required function
10525                                    should be called by the same code after it closes the dialog.
10526 icon              String           A CSS class that provides a background image to be used as an icon for
10527                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10528 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10529 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10530 modal             Boolean          False to allow user interaction with the page while the message box is
10531                                    displayed (defaults to true)
10532 msg               String           A string that will replace the existing message box body text (defaults
10533                                    to the XHTML-compliant non-breaking space character '&#160;')
10534 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10535 progress          Boolean          True to display a progress bar (defaults to false)
10536 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10537 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10538 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10539 title             String           The title text
10540 value             String           The string value to set into the active textbox element if displayed
10541 wait              Boolean          True to display a progress bar (defaults to false)
10542 width             Number           The width of the dialog in pixels
10543 </pre>
10544          *
10545          * Example usage:
10546          * <pre><code>
10547 Roo.Msg.show({
10548    title: 'Address',
10549    msg: 'Please enter your address:',
10550    width: 300,
10551    buttons: Roo.MessageBox.OKCANCEL,
10552    multiline: true,
10553    fn: saveAddress,
10554    animEl: 'addAddressBtn'
10555 });
10556 </code></pre>
10557          * @param {Object} config Configuration options
10558          * @return {Roo.MessageBox} This message box
10559          */
10560         show : function(options)
10561         {
10562             
10563             // this causes nightmares if you show one dialog after another
10564             // especially on callbacks..
10565              
10566             if(this.isVisible()){
10567                 
10568                 this.hide();
10569                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10570                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10571                 Roo.log("New Dialog Message:" +  options.msg )
10572                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10573                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10574                 
10575             }
10576             var d = this.getDialog();
10577             opt = options;
10578             d.setTitle(opt.title || "&#160;");
10579             d.close.setDisplayed(opt.closable !== false);
10580             activeTextEl = textboxEl;
10581             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10582             if(opt.prompt){
10583                 if(opt.multiline){
10584                     textboxEl.hide();
10585                     textareaEl.show();
10586                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10587                         opt.multiline : this.defaultTextHeight);
10588                     activeTextEl = textareaEl;
10589                 }else{
10590                     textboxEl.show();
10591                     textareaEl.hide();
10592                 }
10593             }else{
10594                 textboxEl.hide();
10595                 textareaEl.hide();
10596             }
10597             progressEl.setDisplayed(opt.progress === true);
10598             this.updateProgress(0);
10599             activeTextEl.dom.value = opt.value || "";
10600             if(opt.prompt){
10601                 dlg.setDefaultButton(activeTextEl);
10602             }else{
10603                 var bs = opt.buttons;
10604                 var db = null;
10605                 if(bs && bs.ok){
10606                     db = buttons["ok"];
10607                 }else if(bs && bs.yes){
10608                     db = buttons["yes"];
10609                 }
10610                 dlg.setDefaultButton(db);
10611             }
10612             bwidth = updateButtons(opt.buttons);
10613             this.updateText(opt.msg);
10614             if(opt.cls){
10615                 d.el.addClass(opt.cls);
10616             }
10617             d.proxyDrag = opt.proxyDrag === true;
10618             d.modal = opt.modal !== false;
10619             d.mask = opt.modal !== false ? mask : false;
10620             if(!d.isVisible()){
10621                 // force it to the end of the z-index stack so it gets a cursor in FF
10622                 document.body.appendChild(dlg.el.dom);
10623                 d.animateTarget = null;
10624                 d.show(options.animEl);
10625             }
10626             return this;
10627         },
10628
10629         /**
10630          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10631          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10632          * and closing the message box when the process is complete.
10633          * @param {String} title The title bar text
10634          * @param {String} msg The message box body text
10635          * @return {Roo.MessageBox} This message box
10636          */
10637         progress : function(title, msg){
10638             this.show({
10639                 title : title,
10640                 msg : msg,
10641                 buttons: false,
10642                 progress:true,
10643                 closable:false,
10644                 minWidth: this.minProgressWidth,
10645                 modal : true
10646             });
10647             return this;
10648         },
10649
10650         /**
10651          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10652          * If a callback function is passed it will be called after the user clicks the button, and the
10653          * id of the button that was clicked will be passed as the only parameter to the callback
10654          * (could also be the top-right close button).
10655          * @param {String} title The title bar text
10656          * @param {String} msg The message box body text
10657          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10658          * @param {Object} scope (optional) The scope of the callback function
10659          * @return {Roo.MessageBox} This message box
10660          */
10661         alert : function(title, msg, fn, scope){
10662             this.show({
10663                 title : title,
10664                 msg : msg,
10665                 buttons: this.OK,
10666                 fn: fn,
10667                 scope : scope,
10668                 modal : true
10669             });
10670             return this;
10671         },
10672
10673         /**
10674          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10675          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10676          * You are responsible for closing the message box when the process is complete.
10677          * @param {String} msg The message box body text
10678          * @param {String} title (optional) The title bar text
10679          * @return {Roo.MessageBox} This message box
10680          */
10681         wait : function(msg, title){
10682             this.show({
10683                 title : title,
10684                 msg : msg,
10685                 buttons: false,
10686                 closable:false,
10687                 progress:true,
10688                 modal:true,
10689                 width:300,
10690                 wait:true
10691             });
10692             waitTimer = Roo.TaskMgr.start({
10693                 run: function(i){
10694                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10695                 },
10696                 interval: 1000
10697             });
10698             return this;
10699         },
10700
10701         /**
10702          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10703          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10704          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10705          * @param {String} title The title bar text
10706          * @param {String} msg The message box body text
10707          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10708          * @param {Object} scope (optional) The scope of the callback function
10709          * @return {Roo.MessageBox} This message box
10710          */
10711         confirm : function(title, msg, fn, scope){
10712             this.show({
10713                 title : title,
10714                 msg : msg,
10715                 buttons: this.YESNO,
10716                 fn: fn,
10717                 scope : scope,
10718                 modal : true
10719             });
10720             return this;
10721         },
10722
10723         /**
10724          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10725          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10726          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10727          * (could also be the top-right close button) and the text that was entered will be passed as the two
10728          * parameters to the callback.
10729          * @param {String} title The title bar text
10730          * @param {String} msg The message box body text
10731          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10732          * @param {Object} scope (optional) The scope of the callback function
10733          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10734          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10735          * @return {Roo.MessageBox} This message box
10736          */
10737         prompt : function(title, msg, fn, scope, multiline){
10738             this.show({
10739                 title : title,
10740                 msg : msg,
10741                 buttons: this.OKCANCEL,
10742                 fn: fn,
10743                 minWidth:250,
10744                 scope : scope,
10745                 prompt:true,
10746                 multiline: multiline,
10747                 modal : true
10748             });
10749             return this;
10750         },
10751
10752         /**
10753          * Button config that displays a single OK button
10754          * @type Object
10755          */
10756         OK : {ok:true},
10757         /**
10758          * Button config that displays Yes and No buttons
10759          * @type Object
10760          */
10761         YESNO : {yes:true, no:true},
10762         /**
10763          * Button config that displays OK and Cancel buttons
10764          * @type Object
10765          */
10766         OKCANCEL : {ok:true, cancel:true},
10767         /**
10768          * Button config that displays Yes, No and Cancel buttons
10769          * @type Object
10770          */
10771         YESNOCANCEL : {yes:true, no:true, cancel:true},
10772
10773         /**
10774          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10775          * @type Number
10776          */
10777         defaultTextHeight : 75,
10778         /**
10779          * The maximum width in pixels of the message box (defaults to 600)
10780          * @type Number
10781          */
10782         maxWidth : 600,
10783         /**
10784          * The minimum width in pixels of the message box (defaults to 100)
10785          * @type Number
10786          */
10787         minWidth : 100,
10788         /**
10789          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10790          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10791          * @type Number
10792          */
10793         minProgressWidth : 250,
10794         /**
10795          * An object containing the default button text strings that can be overriden for localized language support.
10796          * Supported properties are: ok, cancel, yes and no.
10797          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10798          * @type Object
10799          */
10800         buttonText : {
10801             ok : "OK",
10802             cancel : "Cancel",
10803             yes : "Yes",
10804             no : "No"
10805         }
10806     };
10807 }();
10808
10809 /**
10810  * Shorthand for {@link Roo.MessageBox}
10811  */
10812 Roo.Msg = Roo.MessageBox;/*
10813  * Based on:
10814  * Ext JS Library 1.1.1
10815  * Copyright(c) 2006-2007, Ext JS, LLC.
10816  *
10817  * Originally Released Under LGPL - original licence link has changed is not relivant.
10818  *
10819  * Fork - LGPL
10820  * <script type="text/javascript">
10821  */
10822 /**
10823  * @class Roo.QuickTips
10824  * Provides attractive and customizable tooltips for any element.
10825  * @static
10826  */
10827 Roo.QuickTips = function(){
10828     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10829     var ce, bd, xy, dd;
10830     var visible = false, disabled = true, inited = false;
10831     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10832     
10833     var onOver = function(e){
10834         if(disabled){
10835             return;
10836         }
10837         var t = e.getTarget();
10838         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10839             return;
10840         }
10841         if(ce && t == ce.el){
10842             clearTimeout(hideProc);
10843             return;
10844         }
10845         if(t && tagEls[t.id]){
10846             tagEls[t.id].el = t;
10847             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10848             return;
10849         }
10850         var ttp, et = Roo.fly(t);
10851         var ns = cfg.namespace;
10852         if(tm.interceptTitles && t.title){
10853             ttp = t.title;
10854             t.qtip = ttp;
10855             t.removeAttribute("title");
10856             e.preventDefault();
10857         }else{
10858             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10859         }
10860         if(ttp){
10861             showProc = show.defer(tm.showDelay, tm, [{
10862                 el: t, 
10863                 text: ttp.replace(/\\n/g,'<br/>'),
10864                 width: et.getAttributeNS(ns, cfg.width),
10865                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10866                 title: et.getAttributeNS(ns, cfg.title),
10867                     cls: et.getAttributeNS(ns, cfg.cls)
10868             }]);
10869         }
10870     };
10871     
10872     var onOut = function(e){
10873         clearTimeout(showProc);
10874         var t = e.getTarget();
10875         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10876             hideProc = setTimeout(hide, tm.hideDelay);
10877         }
10878     };
10879     
10880     var onMove = function(e){
10881         if(disabled){
10882             return;
10883         }
10884         xy = e.getXY();
10885         xy[1] += 18;
10886         if(tm.trackMouse && ce){
10887             el.setXY(xy);
10888         }
10889     };
10890     
10891     var onDown = function(e){
10892         clearTimeout(showProc);
10893         clearTimeout(hideProc);
10894         if(!e.within(el)){
10895             if(tm.hideOnClick){
10896                 hide();
10897                 tm.disable();
10898                 tm.enable.defer(100, tm);
10899             }
10900         }
10901     };
10902     
10903     var getPad = function(){
10904         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10905     };
10906
10907     var show = function(o){
10908         if(disabled){
10909             return;
10910         }
10911         clearTimeout(dismissProc);
10912         ce = o;
10913         if(removeCls){ // in case manually hidden
10914             el.removeClass(removeCls);
10915             removeCls = null;
10916         }
10917         if(ce.cls){
10918             el.addClass(ce.cls);
10919             removeCls = ce.cls;
10920         }
10921         if(ce.title){
10922             tipTitle.update(ce.title);
10923             tipTitle.show();
10924         }else{
10925             tipTitle.update('');
10926             tipTitle.hide();
10927         }
10928         el.dom.style.width  = tm.maxWidth+'px';
10929         //tipBody.dom.style.width = '';
10930         tipBodyText.update(o.text);
10931         var p = getPad(), w = ce.width;
10932         if(!w){
10933             var td = tipBodyText.dom;
10934             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10935             if(aw > tm.maxWidth){
10936                 w = tm.maxWidth;
10937             }else if(aw < tm.minWidth){
10938                 w = tm.minWidth;
10939             }else{
10940                 w = aw;
10941             }
10942         }
10943         //tipBody.setWidth(w);
10944         el.setWidth(parseInt(w, 10) + p);
10945         if(ce.autoHide === false){
10946             close.setDisplayed(true);
10947             if(dd){
10948                 dd.unlock();
10949             }
10950         }else{
10951             close.setDisplayed(false);
10952             if(dd){
10953                 dd.lock();
10954             }
10955         }
10956         if(xy){
10957             el.avoidY = xy[1]-18;
10958             el.setXY(xy);
10959         }
10960         if(tm.animate){
10961             el.setOpacity(.1);
10962             el.setStyle("visibility", "visible");
10963             el.fadeIn({callback: afterShow});
10964         }else{
10965             afterShow();
10966         }
10967     };
10968     
10969     var afterShow = function(){
10970         if(ce){
10971             el.show();
10972             esc.enable();
10973             if(tm.autoDismiss && ce.autoHide !== false){
10974                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10975             }
10976         }
10977     };
10978     
10979     var hide = function(noanim){
10980         clearTimeout(dismissProc);
10981         clearTimeout(hideProc);
10982         ce = null;
10983         if(el.isVisible()){
10984             esc.disable();
10985             if(noanim !== true && tm.animate){
10986                 el.fadeOut({callback: afterHide});
10987             }else{
10988                 afterHide();
10989             } 
10990         }
10991     };
10992     
10993     var afterHide = function(){
10994         el.hide();
10995         if(removeCls){
10996             el.removeClass(removeCls);
10997             removeCls = null;
10998         }
10999     };
11000     
11001     return {
11002         /**
11003         * @cfg {Number} minWidth
11004         * The minimum width of the quick tip (defaults to 40)
11005         */
11006        minWidth : 40,
11007         /**
11008         * @cfg {Number} maxWidth
11009         * The maximum width of the quick tip (defaults to 300)
11010         */
11011        maxWidth : 300,
11012         /**
11013         * @cfg {Boolean} interceptTitles
11014         * True to automatically use the element's DOM title value if available (defaults to false)
11015         */
11016        interceptTitles : false,
11017         /**
11018         * @cfg {Boolean} trackMouse
11019         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11020         */
11021        trackMouse : false,
11022         /**
11023         * @cfg {Boolean} hideOnClick
11024         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11025         */
11026        hideOnClick : true,
11027         /**
11028         * @cfg {Number} showDelay
11029         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11030         */
11031        showDelay : 500,
11032         /**
11033         * @cfg {Number} hideDelay
11034         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11035         */
11036        hideDelay : 200,
11037         /**
11038         * @cfg {Boolean} autoHide
11039         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11040         * Used in conjunction with hideDelay.
11041         */
11042        autoHide : true,
11043         /**
11044         * @cfg {Boolean}
11045         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11046         * (defaults to true).  Used in conjunction with autoDismissDelay.
11047         */
11048        autoDismiss : true,
11049         /**
11050         * @cfg {Number}
11051         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11052         */
11053        autoDismissDelay : 5000,
11054        /**
11055         * @cfg {Boolean} animate
11056         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11057         */
11058        animate : false,
11059
11060        /**
11061         * @cfg {String} title
11062         * Title text to display (defaults to '').  This can be any valid HTML markup.
11063         */
11064         title: '',
11065        /**
11066         * @cfg {String} text
11067         * Body text to display (defaults to '').  This can be any valid HTML markup.
11068         */
11069         text : '',
11070        /**
11071         * @cfg {String} cls
11072         * A CSS class to apply to the base quick tip element (defaults to '').
11073         */
11074         cls : '',
11075        /**
11076         * @cfg {Number} width
11077         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11078         * minWidth or maxWidth.
11079         */
11080         width : null,
11081
11082     /**
11083      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11084      * or display QuickTips in a page.
11085      */
11086        init : function(){
11087           tm = Roo.QuickTips;
11088           cfg = tm.tagConfig;
11089           if(!inited){
11090               if(!Roo.isReady){ // allow calling of init() before onReady
11091                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11092                   return;
11093               }
11094               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11095               el.fxDefaults = {stopFx: true};
11096               // maximum custom styling
11097               //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>');
11098               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>');              
11099               tipTitle = el.child('h3');
11100               tipTitle.enableDisplayMode("block");
11101               tipBody = el.child('div.x-tip-bd');
11102               tipBodyText = el.child('div.x-tip-bd-inner');
11103               //bdLeft = el.child('div.x-tip-bd-left');
11104               //bdRight = el.child('div.x-tip-bd-right');
11105               close = el.child('div.x-tip-close');
11106               close.enableDisplayMode("block");
11107               close.on("click", hide);
11108               var d = Roo.get(document);
11109               d.on("mousedown", onDown);
11110               d.on("mouseover", onOver);
11111               d.on("mouseout", onOut);
11112               d.on("mousemove", onMove);
11113               esc = d.addKeyListener(27, hide);
11114               esc.disable();
11115               if(Roo.dd.DD){
11116                   dd = el.initDD("default", null, {
11117                       onDrag : function(){
11118                           el.sync();  
11119                       }
11120                   });
11121                   dd.setHandleElId(tipTitle.id);
11122                   dd.lock();
11123               }
11124               inited = true;
11125           }
11126           this.enable(); 
11127        },
11128
11129     /**
11130      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11131      * are supported:
11132      * <pre>
11133 Property    Type                   Description
11134 ----------  ---------------------  ------------------------------------------------------------------------
11135 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11136      * </ul>
11137      * @param {Object} config The config object
11138      */
11139        register : function(config){
11140            var cs = config instanceof Array ? config : arguments;
11141            for(var i = 0, len = cs.length; i < len; i++) {
11142                var c = cs[i];
11143                var target = c.target;
11144                if(target){
11145                    if(target instanceof Array){
11146                        for(var j = 0, jlen = target.length; j < jlen; j++){
11147                            tagEls[target[j]] = c;
11148                        }
11149                    }else{
11150                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11151                    }
11152                }
11153            }
11154        },
11155
11156     /**
11157      * Removes this quick tip from its element and destroys it.
11158      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11159      */
11160        unregister : function(el){
11161            delete tagEls[Roo.id(el)];
11162        },
11163
11164     /**
11165      * Enable this quick tip.
11166      */
11167        enable : function(){
11168            if(inited && disabled){
11169                locks.pop();
11170                if(locks.length < 1){
11171                    disabled = false;
11172                }
11173            }
11174        },
11175
11176     /**
11177      * Disable this quick tip.
11178      */
11179        disable : function(){
11180           disabled = true;
11181           clearTimeout(showProc);
11182           clearTimeout(hideProc);
11183           clearTimeout(dismissProc);
11184           if(ce){
11185               hide(true);
11186           }
11187           locks.push(1);
11188        },
11189
11190     /**
11191      * Returns true if the quick tip is enabled, else false.
11192      */
11193        isEnabled : function(){
11194             return !disabled;
11195        },
11196
11197         // private
11198        tagConfig : {
11199            namespace : "roo", // was ext?? this may break..
11200            alt_namespace : "ext",
11201            attribute : "qtip",
11202            width : "width",
11203            target : "target",
11204            title : "qtitle",
11205            hide : "hide",
11206            cls : "qclass"
11207        }
11208    };
11209 }();
11210
11211 // backwards compat
11212 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11213  * Based on:
11214  * Ext JS Library 1.1.1
11215  * Copyright(c) 2006-2007, Ext JS, LLC.
11216  *
11217  * Originally Released Under LGPL - original licence link has changed is not relivant.
11218  *
11219  * Fork - LGPL
11220  * <script type="text/javascript">
11221  */
11222  
11223
11224 /**
11225  * @class Roo.tree.TreePanel
11226  * @extends Roo.data.Tree
11227  * @cfg {Roo.tree.TreeNode} root The root node
11228  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11229  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11230  * @cfg {Boolean} enableDD true to enable drag and drop
11231  * @cfg {Boolean} enableDrag true to enable just drag
11232  * @cfg {Boolean} enableDrop true to enable just drop
11233  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11234  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11235  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11236  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11237  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11238  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11239  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11240  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11241  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11242  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11243  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11244  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11245  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11246  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11247  * @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>
11248  * @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>
11249  * 
11250  * @constructor
11251  * @param {String/HTMLElement/Element} el The container element
11252  * @param {Object} config
11253  */
11254 Roo.tree.TreePanel = function(el, config){
11255     var root = false;
11256     var loader = false;
11257     if (config.root) {
11258         root = config.root;
11259         delete config.root;
11260     }
11261     if (config.loader) {
11262         loader = config.loader;
11263         delete config.loader;
11264     }
11265     
11266     Roo.apply(this, config);
11267     Roo.tree.TreePanel.superclass.constructor.call(this);
11268     this.el = Roo.get(el);
11269     this.el.addClass('x-tree');
11270     //console.log(root);
11271     if (root) {
11272         this.setRootNode( Roo.factory(root, Roo.tree));
11273     }
11274     if (loader) {
11275         this.loader = Roo.factory(loader, Roo.tree);
11276     }
11277    /**
11278     * Read-only. The id of the container element becomes this TreePanel's id.
11279     */
11280     this.id = this.el.id;
11281     this.addEvents({
11282         /**
11283         * @event beforeload
11284         * Fires before a node is loaded, return false to cancel
11285         * @param {Node} node The node being loaded
11286         */
11287         "beforeload" : true,
11288         /**
11289         * @event load
11290         * Fires when a node is loaded
11291         * @param {Node} node The node that was loaded
11292         */
11293         "load" : true,
11294         /**
11295         * @event textchange
11296         * Fires when the text for a node is changed
11297         * @param {Node} node The node
11298         * @param {String} text The new text
11299         * @param {String} oldText The old text
11300         */
11301         "textchange" : true,
11302         /**
11303         * @event beforeexpand
11304         * Fires before a node is expanded, return false to cancel.
11305         * @param {Node} node The node
11306         * @param {Boolean} deep
11307         * @param {Boolean} anim
11308         */
11309         "beforeexpand" : true,
11310         /**
11311         * @event beforecollapse
11312         * Fires before a node is collapsed, return false to cancel.
11313         * @param {Node} node The node
11314         * @param {Boolean} deep
11315         * @param {Boolean} anim
11316         */
11317         "beforecollapse" : true,
11318         /**
11319         * @event expand
11320         * Fires when a node is expanded
11321         * @param {Node} node The node
11322         */
11323         "expand" : true,
11324         /**
11325         * @event disabledchange
11326         * Fires when the disabled status of a node changes
11327         * @param {Node} node The node
11328         * @param {Boolean} disabled
11329         */
11330         "disabledchange" : true,
11331         /**
11332         * @event collapse
11333         * Fires when a node is collapsed
11334         * @param {Node} node The node
11335         */
11336         "collapse" : true,
11337         /**
11338         * @event beforeclick
11339         * Fires before click processing on a node. Return false to cancel the default action.
11340         * @param {Node} node The node
11341         * @param {Roo.EventObject} e The event object
11342         */
11343         "beforeclick":true,
11344         /**
11345         * @event checkchange
11346         * Fires when a node with a checkbox's checked property changes
11347         * @param {Node} this This node
11348         * @param {Boolean} checked
11349         */
11350         "checkchange":true,
11351         /**
11352         * @event click
11353         * Fires when a node is clicked
11354         * @param {Node} node The node
11355         * @param {Roo.EventObject} e The event object
11356         */
11357         "click":true,
11358         /**
11359         * @event dblclick
11360         * Fires when a node is double clicked
11361         * @param {Node} node The node
11362         * @param {Roo.EventObject} e The event object
11363         */
11364         "dblclick":true,
11365         /**
11366         * @event contextmenu
11367         * Fires when a node is right clicked
11368         * @param {Node} node The node
11369         * @param {Roo.EventObject} e The event object
11370         */
11371         "contextmenu":true,
11372         /**
11373         * @event beforechildrenrendered
11374         * Fires right before the child nodes for a node are rendered
11375         * @param {Node} node The node
11376         */
11377         "beforechildrenrendered":true,
11378         /**
11379         * @event startdrag
11380         * Fires when a node starts being dragged
11381         * @param {Roo.tree.TreePanel} this
11382         * @param {Roo.tree.TreeNode} node
11383         * @param {event} e The raw browser event
11384         */ 
11385        "startdrag" : true,
11386        /**
11387         * @event enddrag
11388         * Fires when a drag operation is complete
11389         * @param {Roo.tree.TreePanel} this
11390         * @param {Roo.tree.TreeNode} node
11391         * @param {event} e The raw browser event
11392         */
11393        "enddrag" : true,
11394        /**
11395         * @event dragdrop
11396         * Fires when a dragged node is dropped on a valid DD target
11397         * @param {Roo.tree.TreePanel} this
11398         * @param {Roo.tree.TreeNode} node
11399         * @param {DD} dd The dd it was dropped on
11400         * @param {event} e The raw browser event
11401         */
11402        "dragdrop" : true,
11403        /**
11404         * @event beforenodedrop
11405         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11406         * passed to handlers has the following properties:<br />
11407         * <ul style="padding:5px;padding-left:16px;">
11408         * <li>tree - The TreePanel</li>
11409         * <li>target - The node being targeted for the drop</li>
11410         * <li>data - The drag data from the drag source</li>
11411         * <li>point - The point of the drop - append, above or below</li>
11412         * <li>source - The drag source</li>
11413         * <li>rawEvent - Raw mouse event</li>
11414         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11415         * to be inserted by setting them on this object.</li>
11416         * <li>cancel - Set this to true to cancel the drop.</li>
11417         * </ul>
11418         * @param {Object} dropEvent
11419         */
11420        "beforenodedrop" : true,
11421        /**
11422         * @event nodedrop
11423         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11424         * passed to handlers has the following properties:<br />
11425         * <ul style="padding:5px;padding-left:16px;">
11426         * <li>tree - The TreePanel</li>
11427         * <li>target - The node being targeted for the drop</li>
11428         * <li>data - The drag data from the drag source</li>
11429         * <li>point - The point of the drop - append, above or below</li>
11430         * <li>source - The drag source</li>
11431         * <li>rawEvent - Raw mouse event</li>
11432         * <li>dropNode - Dropped node(s).</li>
11433         * </ul>
11434         * @param {Object} dropEvent
11435         */
11436        "nodedrop" : true,
11437         /**
11438         * @event nodedragover
11439         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11440         * passed to handlers has the following properties:<br />
11441         * <ul style="padding:5px;padding-left:16px;">
11442         * <li>tree - The TreePanel</li>
11443         * <li>target - The node being targeted for the drop</li>
11444         * <li>data - The drag data from the drag source</li>
11445         * <li>point - The point of the drop - append, above or below</li>
11446         * <li>source - The drag source</li>
11447         * <li>rawEvent - Raw mouse event</li>
11448         * <li>dropNode - Drop node(s) provided by the source.</li>
11449         * <li>cancel - Set this to true to signal drop not allowed.</li>
11450         * </ul>
11451         * @param {Object} dragOverEvent
11452         */
11453        "nodedragover" : true,
11454        /**
11455         * @event appendnode
11456         * Fires when append node to the tree
11457         * @param {Roo.tree.TreePanel} this
11458         * @param {Roo.tree.TreeNode} node
11459         * @param {Number} index The index of the newly appended node
11460         */
11461        "appendnode" : true
11462         
11463     });
11464     if(this.singleExpand){
11465        this.on("beforeexpand", this.restrictExpand, this);
11466     }
11467     if (this.editor) {
11468         this.editor.tree = this;
11469         this.editor = Roo.factory(this.editor, Roo.tree);
11470     }
11471     
11472     if (this.selModel) {
11473         this.selModel = Roo.factory(this.selModel, Roo.tree);
11474     }
11475    
11476 };
11477 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11478     rootVisible : true,
11479     animate: Roo.enableFx,
11480     lines : true,
11481     enableDD : false,
11482     hlDrop : Roo.enableFx,
11483   
11484     renderer: false,
11485     
11486     rendererTip: false,
11487     // private
11488     restrictExpand : function(node){
11489         var p = node.parentNode;
11490         if(p){
11491             if(p.expandedChild && p.expandedChild.parentNode == p){
11492                 p.expandedChild.collapse();
11493             }
11494             p.expandedChild = node;
11495         }
11496     },
11497
11498     // private override
11499     setRootNode : function(node){
11500         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11501         if(!this.rootVisible){
11502             node.ui = new Roo.tree.RootTreeNodeUI(node);
11503         }
11504         return node;
11505     },
11506
11507     /**
11508      * Returns the container element for this TreePanel
11509      */
11510     getEl : function(){
11511         return this.el;
11512     },
11513
11514     /**
11515      * Returns the default TreeLoader for this TreePanel
11516      */
11517     getLoader : function(){
11518         return this.loader;
11519     },
11520
11521     /**
11522      * Expand all nodes
11523      */
11524     expandAll : function(){
11525         this.root.expand(true);
11526     },
11527
11528     /**
11529      * Collapse all nodes
11530      */
11531     collapseAll : function(){
11532         this.root.collapse(true);
11533     },
11534
11535     /**
11536      * Returns the selection model used by this TreePanel
11537      */
11538     getSelectionModel : function(){
11539         if(!this.selModel){
11540             this.selModel = new Roo.tree.DefaultSelectionModel();
11541         }
11542         return this.selModel;
11543     },
11544
11545     /**
11546      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11547      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11548      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11549      * @return {Array}
11550      */
11551     getChecked : function(a, startNode){
11552         startNode = startNode || this.root;
11553         var r = [];
11554         var f = function(){
11555             if(this.attributes.checked){
11556                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11557             }
11558         }
11559         startNode.cascade(f);
11560         return r;
11561     },
11562
11563     /**
11564      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11565      * @param {String} path
11566      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11567      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11568      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11569      */
11570     expandPath : function(path, attr, callback){
11571         attr = attr || "id";
11572         var keys = path.split(this.pathSeparator);
11573         var curNode = this.root;
11574         if(curNode.attributes[attr] != keys[1]){ // invalid root
11575             if(callback){
11576                 callback(false, null);
11577             }
11578             return;
11579         }
11580         var index = 1;
11581         var f = function(){
11582             if(++index == keys.length){
11583                 if(callback){
11584                     callback(true, curNode);
11585                 }
11586                 return;
11587             }
11588             var c = curNode.findChild(attr, keys[index]);
11589             if(!c){
11590                 if(callback){
11591                     callback(false, curNode);
11592                 }
11593                 return;
11594             }
11595             curNode = c;
11596             c.expand(false, false, f);
11597         };
11598         curNode.expand(false, false, f);
11599     },
11600
11601     /**
11602      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11603      * @param {String} path
11604      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11605      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11606      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11607      */
11608     selectPath : function(path, attr, callback){
11609         attr = attr || "id";
11610         var keys = path.split(this.pathSeparator);
11611         var v = keys.pop();
11612         if(keys.length > 0){
11613             var f = function(success, node){
11614                 if(success && node){
11615                     var n = node.findChild(attr, v);
11616                     if(n){
11617                         n.select();
11618                         if(callback){
11619                             callback(true, n);
11620                         }
11621                     }else if(callback){
11622                         callback(false, n);
11623                     }
11624                 }else{
11625                     if(callback){
11626                         callback(false, n);
11627                     }
11628                 }
11629             };
11630             this.expandPath(keys.join(this.pathSeparator), attr, f);
11631         }else{
11632             this.root.select();
11633             if(callback){
11634                 callback(true, this.root);
11635             }
11636         }
11637     },
11638
11639     getTreeEl : function(){
11640         return this.el;
11641     },
11642
11643     /**
11644      * Trigger rendering of this TreePanel
11645      */
11646     render : function(){
11647         if (this.innerCt) {
11648             return this; // stop it rendering more than once!!
11649         }
11650         
11651         this.innerCt = this.el.createChild({tag:"ul",
11652                cls:"x-tree-root-ct " +
11653                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11654
11655         if(this.containerScroll){
11656             Roo.dd.ScrollManager.register(this.el);
11657         }
11658         if((this.enableDD || this.enableDrop) && !this.dropZone){
11659            /**
11660             * The dropZone used by this tree if drop is enabled
11661             * @type Roo.tree.TreeDropZone
11662             */
11663              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11664                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11665            });
11666         }
11667         if((this.enableDD || this.enableDrag) && !this.dragZone){
11668            /**
11669             * The dragZone used by this tree if drag is enabled
11670             * @type Roo.tree.TreeDragZone
11671             */
11672             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11673                ddGroup: this.ddGroup || "TreeDD",
11674                scroll: this.ddScroll
11675            });
11676         }
11677         this.getSelectionModel().init(this);
11678         if (!this.root) {
11679             Roo.log("ROOT not set in tree");
11680             return this;
11681         }
11682         this.root.render();
11683         if(!this.rootVisible){
11684             this.root.renderChildren();
11685         }
11686         return this;
11687     }
11688 });/*
11689  * Based on:
11690  * Ext JS Library 1.1.1
11691  * Copyright(c) 2006-2007, Ext JS, LLC.
11692  *
11693  * Originally Released Under LGPL - original licence link has changed is not relivant.
11694  *
11695  * Fork - LGPL
11696  * <script type="text/javascript">
11697  */
11698  
11699
11700 /**
11701  * @class Roo.tree.DefaultSelectionModel
11702  * @extends Roo.util.Observable
11703  * The default single selection for a TreePanel.
11704  * @param {Object} cfg Configuration
11705  */
11706 Roo.tree.DefaultSelectionModel = function(cfg){
11707    this.selNode = null;
11708    
11709    
11710    
11711    this.addEvents({
11712        /**
11713         * @event selectionchange
11714         * Fires when the selected node changes
11715         * @param {DefaultSelectionModel} this
11716         * @param {TreeNode} node the new selection
11717         */
11718        "selectionchange" : true,
11719
11720        /**
11721         * @event beforeselect
11722         * Fires before the selected node changes, return false to cancel the change
11723         * @param {DefaultSelectionModel} this
11724         * @param {TreeNode} node the new selection
11725         * @param {TreeNode} node the old selection
11726         */
11727        "beforeselect" : true
11728    });
11729    
11730     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11731 };
11732
11733 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11734     init : function(tree){
11735         this.tree = tree;
11736         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11737         tree.on("click", this.onNodeClick, this);
11738     },
11739     
11740     onNodeClick : function(node, e){
11741         if (e.ctrlKey && this.selNode == node)  {
11742             this.unselect(node);
11743             return;
11744         }
11745         this.select(node);
11746     },
11747     
11748     /**
11749      * Select a node.
11750      * @param {TreeNode} node The node to select
11751      * @return {TreeNode} The selected node
11752      */
11753     select : function(node){
11754         var last = this.selNode;
11755         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11756             if(last){
11757                 last.ui.onSelectedChange(false);
11758             }
11759             this.selNode = node;
11760             node.ui.onSelectedChange(true);
11761             this.fireEvent("selectionchange", this, node, last);
11762         }
11763         return node;
11764     },
11765     
11766     /**
11767      * Deselect a node.
11768      * @param {TreeNode} node The node to unselect
11769      */
11770     unselect : function(node){
11771         if(this.selNode == node){
11772             this.clearSelections();
11773         }    
11774     },
11775     
11776     /**
11777      * Clear all selections
11778      */
11779     clearSelections : function(){
11780         var n = this.selNode;
11781         if(n){
11782             n.ui.onSelectedChange(false);
11783             this.selNode = null;
11784             this.fireEvent("selectionchange", this, null);
11785         }
11786         return n;
11787     },
11788     
11789     /**
11790      * Get the selected node
11791      * @return {TreeNode} The selected node
11792      */
11793     getSelectedNode : function(){
11794         return this.selNode;    
11795     },
11796     
11797     /**
11798      * Returns true if the node is selected
11799      * @param {TreeNode} node The node to check
11800      * @return {Boolean}
11801      */
11802     isSelected : function(node){
11803         return this.selNode == node;  
11804     },
11805
11806     /**
11807      * Selects the node above the selected node in the tree, intelligently walking the nodes
11808      * @return TreeNode The new selection
11809      */
11810     selectPrevious : function(){
11811         var s = this.selNode || this.lastSelNode;
11812         if(!s){
11813             return null;
11814         }
11815         var ps = s.previousSibling;
11816         if(ps){
11817             if(!ps.isExpanded() || ps.childNodes.length < 1){
11818                 return this.select(ps);
11819             } else{
11820                 var lc = ps.lastChild;
11821                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11822                     lc = lc.lastChild;
11823                 }
11824                 return this.select(lc);
11825             }
11826         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11827             return this.select(s.parentNode);
11828         }
11829         return null;
11830     },
11831
11832     /**
11833      * Selects the node above the selected node in the tree, intelligently walking the nodes
11834      * @return TreeNode The new selection
11835      */
11836     selectNext : function(){
11837         var s = this.selNode || this.lastSelNode;
11838         if(!s){
11839             return null;
11840         }
11841         if(s.firstChild && s.isExpanded()){
11842              return this.select(s.firstChild);
11843          }else if(s.nextSibling){
11844              return this.select(s.nextSibling);
11845          }else if(s.parentNode){
11846             var newS = null;
11847             s.parentNode.bubble(function(){
11848                 if(this.nextSibling){
11849                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11850                     return false;
11851                 }
11852             });
11853             return newS;
11854          }
11855         return null;
11856     },
11857
11858     onKeyDown : function(e){
11859         var s = this.selNode || this.lastSelNode;
11860         // undesirable, but required
11861         var sm = this;
11862         if(!s){
11863             return;
11864         }
11865         var k = e.getKey();
11866         switch(k){
11867              case e.DOWN:
11868                  e.stopEvent();
11869                  this.selectNext();
11870              break;
11871              case e.UP:
11872                  e.stopEvent();
11873                  this.selectPrevious();
11874              break;
11875              case e.RIGHT:
11876                  e.preventDefault();
11877                  if(s.hasChildNodes()){
11878                      if(!s.isExpanded()){
11879                          s.expand();
11880                      }else if(s.firstChild){
11881                          this.select(s.firstChild, e);
11882                      }
11883                  }
11884              break;
11885              case e.LEFT:
11886                  e.preventDefault();
11887                  if(s.hasChildNodes() && s.isExpanded()){
11888                      s.collapse();
11889                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11890                      this.select(s.parentNode, e);
11891                  }
11892              break;
11893         };
11894     }
11895 });
11896
11897 /**
11898  * @class Roo.tree.MultiSelectionModel
11899  * @extends Roo.util.Observable
11900  * Multi selection for a TreePanel.
11901  * @param {Object} cfg Configuration
11902  */
11903 Roo.tree.MultiSelectionModel = function(){
11904    this.selNodes = [];
11905    this.selMap = {};
11906    this.addEvents({
11907        /**
11908         * @event selectionchange
11909         * Fires when the selected nodes change
11910         * @param {MultiSelectionModel} this
11911         * @param {Array} nodes Array of the selected nodes
11912         */
11913        "selectionchange" : true
11914    });
11915    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11916    
11917 };
11918
11919 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11920     init : function(tree){
11921         this.tree = tree;
11922         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11923         tree.on("click", this.onNodeClick, this);
11924     },
11925     
11926     onNodeClick : function(node, e){
11927         this.select(node, e, e.ctrlKey);
11928     },
11929     
11930     /**
11931      * Select a node.
11932      * @param {TreeNode} node The node to select
11933      * @param {EventObject} e (optional) An event associated with the selection
11934      * @param {Boolean} keepExisting True to retain existing selections
11935      * @return {TreeNode} The selected node
11936      */
11937     select : function(node, e, keepExisting){
11938         if(keepExisting !== true){
11939             this.clearSelections(true);
11940         }
11941         if(this.isSelected(node)){
11942             this.lastSelNode = node;
11943             return node;
11944         }
11945         this.selNodes.push(node);
11946         this.selMap[node.id] = node;
11947         this.lastSelNode = node;
11948         node.ui.onSelectedChange(true);
11949         this.fireEvent("selectionchange", this, this.selNodes);
11950         return node;
11951     },
11952     
11953     /**
11954      * Deselect a node.
11955      * @param {TreeNode} node The node to unselect
11956      */
11957     unselect : function(node){
11958         if(this.selMap[node.id]){
11959             node.ui.onSelectedChange(false);
11960             var sn = this.selNodes;
11961             var index = -1;
11962             if(sn.indexOf){
11963                 index = sn.indexOf(node);
11964             }else{
11965                 for(var i = 0, len = sn.length; i < len; i++){
11966                     if(sn[i] == node){
11967                         index = i;
11968                         break;
11969                     }
11970                 }
11971             }
11972             if(index != -1){
11973                 this.selNodes.splice(index, 1);
11974             }
11975             delete this.selMap[node.id];
11976             this.fireEvent("selectionchange", this, this.selNodes);
11977         }
11978     },
11979     
11980     /**
11981      * Clear all selections
11982      */
11983     clearSelections : function(suppressEvent){
11984         var sn = this.selNodes;
11985         if(sn.length > 0){
11986             for(var i = 0, len = sn.length; i < len; i++){
11987                 sn[i].ui.onSelectedChange(false);
11988             }
11989             this.selNodes = [];
11990             this.selMap = {};
11991             if(suppressEvent !== true){
11992                 this.fireEvent("selectionchange", this, this.selNodes);
11993             }
11994         }
11995     },
11996     
11997     /**
11998      * Returns true if the node is selected
11999      * @param {TreeNode} node The node to check
12000      * @return {Boolean}
12001      */
12002     isSelected : function(node){
12003         return this.selMap[node.id] ? true : false;  
12004     },
12005     
12006     /**
12007      * Returns an array of the selected nodes
12008      * @return {Array}
12009      */
12010     getSelectedNodes : function(){
12011         return this.selNodes;    
12012     },
12013
12014     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12015
12016     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12017
12018     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12019 });/*
12020  * Based on:
12021  * Ext JS Library 1.1.1
12022  * Copyright(c) 2006-2007, Ext JS, LLC.
12023  *
12024  * Originally Released Under LGPL - original licence link has changed is not relivant.
12025  *
12026  * Fork - LGPL
12027  * <script type="text/javascript">
12028  */
12029  
12030 /**
12031  * @class Roo.tree.TreeNode
12032  * @extends Roo.data.Node
12033  * @cfg {String} text The text for this node
12034  * @cfg {Boolean} expanded true to start the node expanded
12035  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12036  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12037  * @cfg {Boolean} disabled true to start the node disabled
12038  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12039  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12040  * @cfg {String} cls A css class to be added to the node
12041  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12042  * @cfg {String} href URL of the link used for the node (defaults to #)
12043  * @cfg {String} hrefTarget target frame for the link
12044  * @cfg {String} qtip An Ext QuickTip for the node
12045  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12046  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12047  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12048  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12049  * (defaults to undefined with no checkbox rendered)
12050  * @constructor
12051  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12052  */
12053 Roo.tree.TreeNode = function(attributes){
12054     attributes = attributes || {};
12055     if(typeof attributes == "string"){
12056         attributes = {text: attributes};
12057     }
12058     this.childrenRendered = false;
12059     this.rendered = false;
12060     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12061     this.expanded = attributes.expanded === true;
12062     this.isTarget = attributes.isTarget !== false;
12063     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12064     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12065
12066     /**
12067      * Read-only. The text for this node. To change it use setText().
12068      * @type String
12069      */
12070     this.text = attributes.text;
12071     /**
12072      * True if this node is disabled.
12073      * @type Boolean
12074      */
12075     this.disabled = attributes.disabled === true;
12076
12077     this.addEvents({
12078         /**
12079         * @event textchange
12080         * Fires when the text for this node is changed
12081         * @param {Node} this This node
12082         * @param {String} text The new text
12083         * @param {String} oldText The old text
12084         */
12085         "textchange" : true,
12086         /**
12087         * @event beforeexpand
12088         * Fires before this node is expanded, return false to cancel.
12089         * @param {Node} this This node
12090         * @param {Boolean} deep
12091         * @param {Boolean} anim
12092         */
12093         "beforeexpand" : true,
12094         /**
12095         * @event beforecollapse
12096         * Fires before this node is collapsed, return false to cancel.
12097         * @param {Node} this This node
12098         * @param {Boolean} deep
12099         * @param {Boolean} anim
12100         */
12101         "beforecollapse" : true,
12102         /**
12103         * @event expand
12104         * Fires when this node is expanded
12105         * @param {Node} this This node
12106         */
12107         "expand" : true,
12108         /**
12109         * @event disabledchange
12110         * Fires when the disabled status of this node changes
12111         * @param {Node} this This node
12112         * @param {Boolean} disabled
12113         */
12114         "disabledchange" : true,
12115         /**
12116         * @event collapse
12117         * Fires when this node is collapsed
12118         * @param {Node} this This node
12119         */
12120         "collapse" : true,
12121         /**
12122         * @event beforeclick
12123         * Fires before click processing. Return false to cancel the default action.
12124         * @param {Node} this This node
12125         * @param {Roo.EventObject} e The event object
12126         */
12127         "beforeclick":true,
12128         /**
12129         * @event checkchange
12130         * Fires when a node with a checkbox's checked property changes
12131         * @param {Node} this This node
12132         * @param {Boolean} checked
12133         */
12134         "checkchange":true,
12135         /**
12136         * @event click
12137         * Fires when this node is clicked
12138         * @param {Node} this This node
12139         * @param {Roo.EventObject} e The event object
12140         */
12141         "click":true,
12142         /**
12143         * @event dblclick
12144         * Fires when this node is double clicked
12145         * @param {Node} this This node
12146         * @param {Roo.EventObject} e The event object
12147         */
12148         "dblclick":true,
12149         /**
12150         * @event contextmenu
12151         * Fires when this node is right clicked
12152         * @param {Node} this This node
12153         * @param {Roo.EventObject} e The event object
12154         */
12155         "contextmenu":true,
12156         /**
12157         * @event beforechildrenrendered
12158         * Fires right before the child nodes for this node are rendered
12159         * @param {Node} this This node
12160         */
12161         "beforechildrenrendered":true
12162     });
12163
12164     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12165
12166     /**
12167      * Read-only. The UI for this node
12168      * @type TreeNodeUI
12169      */
12170     this.ui = new uiClass(this);
12171     
12172     // finally support items[]
12173     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12174         return;
12175     }
12176     
12177     
12178     Roo.each(this.attributes.items, function(c) {
12179         this.appendChild(Roo.factory(c,Roo.Tree));
12180     }, this);
12181     delete this.attributes.items;
12182     
12183     
12184     
12185 };
12186 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12187     preventHScroll: true,
12188     /**
12189      * Returns true if this node is expanded
12190      * @return {Boolean}
12191      */
12192     isExpanded : function(){
12193         return this.expanded;
12194     },
12195
12196     /**
12197      * Returns the UI object for this node
12198      * @return {TreeNodeUI}
12199      */
12200     getUI : function(){
12201         return this.ui;
12202     },
12203
12204     // private override
12205     setFirstChild : function(node){
12206         var of = this.firstChild;
12207         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12208         if(this.childrenRendered && of && node != of){
12209             of.renderIndent(true, true);
12210         }
12211         if(this.rendered){
12212             this.renderIndent(true, true);
12213         }
12214     },
12215
12216     // private override
12217     setLastChild : function(node){
12218         var ol = this.lastChild;
12219         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12220         if(this.childrenRendered && ol && node != ol){
12221             ol.renderIndent(true, true);
12222         }
12223         if(this.rendered){
12224             this.renderIndent(true, true);
12225         }
12226     },
12227
12228     // these methods are overridden to provide lazy rendering support
12229     // private override
12230     appendChild : function()
12231     {
12232         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12233         if(node && this.childrenRendered){
12234             node.render();
12235         }
12236         this.ui.updateExpandIcon();
12237         return node;
12238     },
12239
12240     // private override
12241     removeChild : function(node){
12242         this.ownerTree.getSelectionModel().unselect(node);
12243         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12244         // if it's been rendered remove dom node
12245         if(this.childrenRendered){
12246             node.ui.remove();
12247         }
12248         if(this.childNodes.length < 1){
12249             this.collapse(false, false);
12250         }else{
12251             this.ui.updateExpandIcon();
12252         }
12253         if(!this.firstChild) {
12254             this.childrenRendered = false;
12255         }
12256         return node;
12257     },
12258
12259     // private override
12260     insertBefore : function(node, refNode){
12261         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12262         if(newNode && refNode && this.childrenRendered){
12263             node.render();
12264         }
12265         this.ui.updateExpandIcon();
12266         return newNode;
12267     },
12268
12269     /**
12270      * Sets the text for this node
12271      * @param {String} text
12272      */
12273     setText : function(text){
12274         var oldText = this.text;
12275         this.text = text;
12276         this.attributes.text = text;
12277         if(this.rendered){ // event without subscribing
12278             this.ui.onTextChange(this, text, oldText);
12279         }
12280         this.fireEvent("textchange", this, text, oldText);
12281     },
12282
12283     /**
12284      * Triggers selection of this node
12285      */
12286     select : function(){
12287         this.getOwnerTree().getSelectionModel().select(this);
12288     },
12289
12290     /**
12291      * Triggers deselection of this node
12292      */
12293     unselect : function(){
12294         this.getOwnerTree().getSelectionModel().unselect(this);
12295     },
12296
12297     /**
12298      * Returns true if this node is selected
12299      * @return {Boolean}
12300      */
12301     isSelected : function(){
12302         return this.getOwnerTree().getSelectionModel().isSelected(this);
12303     },
12304
12305     /**
12306      * Expand this node.
12307      * @param {Boolean} deep (optional) True to expand all children as well
12308      * @param {Boolean} anim (optional) false to cancel the default animation
12309      * @param {Function} callback (optional) A callback to be called when
12310      * expanding this node completes (does not wait for deep expand to complete).
12311      * Called with 1 parameter, this node.
12312      */
12313     expand : function(deep, anim, callback){
12314         if(!this.expanded){
12315             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12316                 return;
12317             }
12318             if(!this.childrenRendered){
12319                 this.renderChildren();
12320             }
12321             this.expanded = true;
12322             
12323             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12324                 this.ui.animExpand(function(){
12325                     this.fireEvent("expand", this);
12326                     if(typeof callback == "function"){
12327                         callback(this);
12328                     }
12329                     if(deep === true){
12330                         this.expandChildNodes(true);
12331                     }
12332                 }.createDelegate(this));
12333                 return;
12334             }else{
12335                 this.ui.expand();
12336                 this.fireEvent("expand", this);
12337                 if(typeof callback == "function"){
12338                     callback(this);
12339                 }
12340             }
12341         }else{
12342            if(typeof callback == "function"){
12343                callback(this);
12344            }
12345         }
12346         if(deep === true){
12347             this.expandChildNodes(true);
12348         }
12349     },
12350
12351     isHiddenRoot : function(){
12352         return this.isRoot && !this.getOwnerTree().rootVisible;
12353     },
12354
12355     /**
12356      * Collapse this node.
12357      * @param {Boolean} deep (optional) True to collapse all children as well
12358      * @param {Boolean} anim (optional) false to cancel the default animation
12359      */
12360     collapse : function(deep, anim){
12361         if(this.expanded && !this.isHiddenRoot()){
12362             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12363                 return;
12364             }
12365             this.expanded = false;
12366             if((this.getOwnerTree().animate && anim !== false) || anim){
12367                 this.ui.animCollapse(function(){
12368                     this.fireEvent("collapse", this);
12369                     if(deep === true){
12370                         this.collapseChildNodes(true);
12371                     }
12372                 }.createDelegate(this));
12373                 return;
12374             }else{
12375                 this.ui.collapse();
12376                 this.fireEvent("collapse", this);
12377             }
12378         }
12379         if(deep === true){
12380             var cs = this.childNodes;
12381             for(var i = 0, len = cs.length; i < len; i++) {
12382                 cs[i].collapse(true, false);
12383             }
12384         }
12385     },
12386
12387     // private
12388     delayedExpand : function(delay){
12389         if(!this.expandProcId){
12390             this.expandProcId = this.expand.defer(delay, this);
12391         }
12392     },
12393
12394     // private
12395     cancelExpand : function(){
12396         if(this.expandProcId){
12397             clearTimeout(this.expandProcId);
12398         }
12399         this.expandProcId = false;
12400     },
12401
12402     /**
12403      * Toggles expanded/collapsed state of the node
12404      */
12405     toggle : function(){
12406         if(this.expanded){
12407             this.collapse();
12408         }else{
12409             this.expand();
12410         }
12411     },
12412
12413     /**
12414      * Ensures all parent nodes are expanded
12415      */
12416     ensureVisible : function(callback){
12417         var tree = this.getOwnerTree();
12418         tree.expandPath(this.parentNode.getPath(), false, function(){
12419             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12420             Roo.callback(callback);
12421         }.createDelegate(this));
12422     },
12423
12424     /**
12425      * Expand all child nodes
12426      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12427      */
12428     expandChildNodes : function(deep){
12429         var cs = this.childNodes;
12430         for(var i = 0, len = cs.length; i < len; i++) {
12431                 cs[i].expand(deep);
12432         }
12433     },
12434
12435     /**
12436      * Collapse all child nodes
12437      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12438      */
12439     collapseChildNodes : function(deep){
12440         var cs = this.childNodes;
12441         for(var i = 0, len = cs.length; i < len; i++) {
12442                 cs[i].collapse(deep);
12443         }
12444     },
12445
12446     /**
12447      * Disables this node
12448      */
12449     disable : function(){
12450         this.disabled = true;
12451         this.unselect();
12452         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12453             this.ui.onDisableChange(this, true);
12454         }
12455         this.fireEvent("disabledchange", this, true);
12456     },
12457
12458     /**
12459      * Enables this node
12460      */
12461     enable : function(){
12462         this.disabled = false;
12463         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12464             this.ui.onDisableChange(this, false);
12465         }
12466         this.fireEvent("disabledchange", this, false);
12467     },
12468
12469     // private
12470     renderChildren : function(suppressEvent){
12471         if(suppressEvent !== false){
12472             this.fireEvent("beforechildrenrendered", this);
12473         }
12474         var cs = this.childNodes;
12475         for(var i = 0, len = cs.length; i < len; i++){
12476             cs[i].render(true);
12477         }
12478         this.childrenRendered = true;
12479     },
12480
12481     // private
12482     sort : function(fn, scope){
12483         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12484         if(this.childrenRendered){
12485             var cs = this.childNodes;
12486             for(var i = 0, len = cs.length; i < len; i++){
12487                 cs[i].render(true);
12488             }
12489         }
12490     },
12491
12492     // private
12493     render : function(bulkRender){
12494         this.ui.render(bulkRender);
12495         if(!this.rendered){
12496             this.rendered = true;
12497             if(this.expanded){
12498                 this.expanded = false;
12499                 this.expand(false, false);
12500             }
12501         }
12502     },
12503
12504     // private
12505     renderIndent : function(deep, refresh){
12506         if(refresh){
12507             this.ui.childIndent = null;
12508         }
12509         this.ui.renderIndent();
12510         if(deep === true && this.childrenRendered){
12511             var cs = this.childNodes;
12512             for(var i = 0, len = cs.length; i < len; i++){
12513                 cs[i].renderIndent(true, refresh);
12514             }
12515         }
12516     }
12517 });/*
12518  * Based on:
12519  * Ext JS Library 1.1.1
12520  * Copyright(c) 2006-2007, Ext JS, LLC.
12521  *
12522  * Originally Released Under LGPL - original licence link has changed is not relivant.
12523  *
12524  * Fork - LGPL
12525  * <script type="text/javascript">
12526  */
12527  
12528 /**
12529  * @class Roo.tree.AsyncTreeNode
12530  * @extends Roo.tree.TreeNode
12531  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12532  * @constructor
12533  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12534  */
12535  Roo.tree.AsyncTreeNode = function(config){
12536     this.loaded = false;
12537     this.loading = false;
12538     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12539     /**
12540     * @event beforeload
12541     * Fires before this node is loaded, return false to cancel
12542     * @param {Node} this This node
12543     */
12544     this.addEvents({'beforeload':true, 'load': true});
12545     /**
12546     * @event load
12547     * Fires when this node is loaded
12548     * @param {Node} this This node
12549     */
12550     /**
12551      * The loader used by this node (defaults to using the tree's defined loader)
12552      * @type TreeLoader
12553      * @property loader
12554      */
12555 };
12556 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12557     expand : function(deep, anim, callback){
12558         if(this.loading){ // if an async load is already running, waiting til it's done
12559             var timer;
12560             var f = function(){
12561                 if(!this.loading){ // done loading
12562                     clearInterval(timer);
12563                     this.expand(deep, anim, callback);
12564                 }
12565             }.createDelegate(this);
12566             timer = setInterval(f, 200);
12567             return;
12568         }
12569         if(!this.loaded){
12570             if(this.fireEvent("beforeload", this) === false){
12571                 return;
12572             }
12573             this.loading = true;
12574             this.ui.beforeLoad(this);
12575             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12576             if(loader){
12577                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12578                 return;
12579             }
12580         }
12581         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12582     },
12583     
12584     /**
12585      * Returns true if this node is currently loading
12586      * @return {Boolean}
12587      */
12588     isLoading : function(){
12589         return this.loading;  
12590     },
12591     
12592     loadComplete : function(deep, anim, callback){
12593         this.loading = false;
12594         this.loaded = true;
12595         this.ui.afterLoad(this);
12596         this.fireEvent("load", this);
12597         this.expand(deep, anim, callback);
12598     },
12599     
12600     /**
12601      * Returns true if this node has been loaded
12602      * @return {Boolean}
12603      */
12604     isLoaded : function(){
12605         return this.loaded;
12606     },
12607     
12608     hasChildNodes : function(){
12609         if(!this.isLeaf() && !this.loaded){
12610             return true;
12611         }else{
12612             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12613         }
12614     },
12615
12616     /**
12617      * Trigger a reload for this node
12618      * @param {Function} callback
12619      */
12620     reload : function(callback){
12621         this.collapse(false, false);
12622         while(this.firstChild){
12623             this.removeChild(this.firstChild);
12624         }
12625         this.childrenRendered = false;
12626         this.loaded = false;
12627         if(this.isHiddenRoot()){
12628             this.expanded = false;
12629         }
12630         this.expand(false, false, callback);
12631     }
12632 });/*
12633  * Based on:
12634  * Ext JS Library 1.1.1
12635  * Copyright(c) 2006-2007, Ext JS, LLC.
12636  *
12637  * Originally Released Under LGPL - original licence link has changed is not relivant.
12638  *
12639  * Fork - LGPL
12640  * <script type="text/javascript">
12641  */
12642  
12643 /**
12644  * @class Roo.tree.TreeNodeUI
12645  * @constructor
12646  * @param {Object} node The node to render
12647  * The TreeNode UI implementation is separate from the
12648  * tree implementation. Unless you are customizing the tree UI,
12649  * you should never have to use this directly.
12650  */
12651 Roo.tree.TreeNodeUI = function(node){
12652     this.node = node;
12653     this.rendered = false;
12654     this.animating = false;
12655     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12656 };
12657
12658 Roo.tree.TreeNodeUI.prototype = {
12659     removeChild : function(node){
12660         if(this.rendered){
12661             this.ctNode.removeChild(node.ui.getEl());
12662         }
12663     },
12664
12665     beforeLoad : function(){
12666          this.addClass("x-tree-node-loading");
12667     },
12668
12669     afterLoad : function(){
12670          this.removeClass("x-tree-node-loading");
12671     },
12672
12673     onTextChange : function(node, text, oldText){
12674         if(this.rendered){
12675             this.textNode.innerHTML = text;
12676         }
12677     },
12678
12679     onDisableChange : function(node, state){
12680         this.disabled = state;
12681         if(state){
12682             this.addClass("x-tree-node-disabled");
12683         }else{
12684             this.removeClass("x-tree-node-disabled");
12685         }
12686     },
12687
12688     onSelectedChange : function(state){
12689         if(state){
12690             this.focus();
12691             this.addClass("x-tree-selected");
12692         }else{
12693             //this.blur();
12694             this.removeClass("x-tree-selected");
12695         }
12696     },
12697
12698     onMove : function(tree, node, oldParent, newParent, index, refNode){
12699         this.childIndent = null;
12700         if(this.rendered){
12701             var targetNode = newParent.ui.getContainer();
12702             if(!targetNode){//target not rendered
12703                 this.holder = document.createElement("div");
12704                 this.holder.appendChild(this.wrap);
12705                 return;
12706             }
12707             var insertBefore = refNode ? refNode.ui.getEl() : null;
12708             if(insertBefore){
12709                 targetNode.insertBefore(this.wrap, insertBefore);
12710             }else{
12711                 targetNode.appendChild(this.wrap);
12712             }
12713             this.node.renderIndent(true);
12714         }
12715     },
12716
12717     addClass : function(cls){
12718         if(this.elNode){
12719             Roo.fly(this.elNode).addClass(cls);
12720         }
12721     },
12722
12723     removeClass : function(cls){
12724         if(this.elNode){
12725             Roo.fly(this.elNode).removeClass(cls);
12726         }
12727     },
12728
12729     remove : function(){
12730         if(this.rendered){
12731             this.holder = document.createElement("div");
12732             this.holder.appendChild(this.wrap);
12733         }
12734     },
12735
12736     fireEvent : function(){
12737         return this.node.fireEvent.apply(this.node, arguments);
12738     },
12739
12740     initEvents : function(){
12741         this.node.on("move", this.onMove, this);
12742         var E = Roo.EventManager;
12743         var a = this.anchor;
12744
12745         var el = Roo.fly(a, '_treeui');
12746
12747         if(Roo.isOpera){ // opera render bug ignores the CSS
12748             el.setStyle("text-decoration", "none");
12749         }
12750
12751         el.on("click", this.onClick, this);
12752         el.on("dblclick", this.onDblClick, this);
12753
12754         if(this.checkbox){
12755             Roo.EventManager.on(this.checkbox,
12756                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12757         }
12758
12759         el.on("contextmenu", this.onContextMenu, this);
12760
12761         var icon = Roo.fly(this.iconNode);
12762         icon.on("click", this.onClick, this);
12763         icon.on("dblclick", this.onDblClick, this);
12764         icon.on("contextmenu", this.onContextMenu, this);
12765         E.on(this.ecNode, "click", this.ecClick, this, true);
12766
12767         if(this.node.disabled){
12768             this.addClass("x-tree-node-disabled");
12769         }
12770         if(this.node.hidden){
12771             this.addClass("x-tree-node-disabled");
12772         }
12773         var ot = this.node.getOwnerTree();
12774         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12775         if(dd && (!this.node.isRoot || ot.rootVisible)){
12776             Roo.dd.Registry.register(this.elNode, {
12777                 node: this.node,
12778                 handles: this.getDDHandles(),
12779                 isHandle: false
12780             });
12781         }
12782     },
12783
12784     getDDHandles : function(){
12785         return [this.iconNode, this.textNode];
12786     },
12787
12788     hide : function(){
12789         if(this.rendered){
12790             this.wrap.style.display = "none";
12791         }
12792     },
12793
12794     show : function(){
12795         if(this.rendered){
12796             this.wrap.style.display = "";
12797         }
12798     },
12799
12800     onContextMenu : function(e){
12801         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12802             e.preventDefault();
12803             this.focus();
12804             this.fireEvent("contextmenu", this.node, e);
12805         }
12806     },
12807
12808     onClick : function(e){
12809         if(this.dropping){
12810             e.stopEvent();
12811             return;
12812         }
12813         if(this.fireEvent("beforeclick", this.node, e) !== false){
12814             if(!this.disabled && this.node.attributes.href){
12815                 this.fireEvent("click", this.node, e);
12816                 return;
12817             }
12818             e.preventDefault();
12819             if(this.disabled){
12820                 return;
12821             }
12822
12823             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12824                 this.node.toggle();
12825             }
12826
12827             this.fireEvent("click", this.node, e);
12828         }else{
12829             e.stopEvent();
12830         }
12831     },
12832
12833     onDblClick : function(e){
12834         e.preventDefault();
12835         if(this.disabled){
12836             return;
12837         }
12838         if(this.checkbox){
12839             this.toggleCheck();
12840         }
12841         if(!this.animating && this.node.hasChildNodes()){
12842             this.node.toggle();
12843         }
12844         this.fireEvent("dblclick", this.node, e);
12845     },
12846
12847     onCheckChange : function(){
12848         var checked = this.checkbox.checked;
12849         this.node.attributes.checked = checked;
12850         this.fireEvent('checkchange', this.node, checked);
12851     },
12852
12853     ecClick : function(e){
12854         if(!this.animating && this.node.hasChildNodes()){
12855             this.node.toggle();
12856         }
12857     },
12858
12859     startDrop : function(){
12860         this.dropping = true;
12861     },
12862
12863     // delayed drop so the click event doesn't get fired on a drop
12864     endDrop : function(){
12865        setTimeout(function(){
12866            this.dropping = false;
12867        }.createDelegate(this), 50);
12868     },
12869
12870     expand : function(){
12871         this.updateExpandIcon();
12872         this.ctNode.style.display = "";
12873     },
12874
12875     focus : function(){
12876         if(!this.node.preventHScroll){
12877             try{this.anchor.focus();
12878             }catch(e){}
12879         }else if(!Roo.isIE){
12880             try{
12881                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12882                 var l = noscroll.scrollLeft;
12883                 this.anchor.focus();
12884                 noscroll.scrollLeft = l;
12885             }catch(e){}
12886         }
12887     },
12888
12889     toggleCheck : function(value){
12890         var cb = this.checkbox;
12891         if(cb){
12892             cb.checked = (value === undefined ? !cb.checked : value);
12893         }
12894     },
12895
12896     blur : function(){
12897         try{
12898             this.anchor.blur();
12899         }catch(e){}
12900     },
12901
12902     animExpand : function(callback){
12903         var ct = Roo.get(this.ctNode);
12904         ct.stopFx();
12905         if(!this.node.hasChildNodes()){
12906             this.updateExpandIcon();
12907             this.ctNode.style.display = "";
12908             Roo.callback(callback);
12909             return;
12910         }
12911         this.animating = true;
12912         this.updateExpandIcon();
12913
12914         ct.slideIn('t', {
12915            callback : function(){
12916                this.animating = false;
12917                Roo.callback(callback);
12918             },
12919             scope: this,
12920             duration: this.node.ownerTree.duration || .25
12921         });
12922     },
12923
12924     highlight : function(){
12925         var tree = this.node.getOwnerTree();
12926         Roo.fly(this.wrap).highlight(
12927             tree.hlColor || "C3DAF9",
12928             {endColor: tree.hlBaseColor}
12929         );
12930     },
12931
12932     collapse : function(){
12933         this.updateExpandIcon();
12934         this.ctNode.style.display = "none";
12935     },
12936
12937     animCollapse : function(callback){
12938         var ct = Roo.get(this.ctNode);
12939         ct.enableDisplayMode('block');
12940         ct.stopFx();
12941
12942         this.animating = true;
12943         this.updateExpandIcon();
12944
12945         ct.slideOut('t', {
12946             callback : function(){
12947                this.animating = false;
12948                Roo.callback(callback);
12949             },
12950             scope: this,
12951             duration: this.node.ownerTree.duration || .25
12952         });
12953     },
12954
12955     getContainer : function(){
12956         return this.ctNode;
12957     },
12958
12959     getEl : function(){
12960         return this.wrap;
12961     },
12962
12963     appendDDGhost : function(ghostNode){
12964         ghostNode.appendChild(this.elNode.cloneNode(true));
12965     },
12966
12967     getDDRepairXY : function(){
12968         return Roo.lib.Dom.getXY(this.iconNode);
12969     },
12970
12971     onRender : function(){
12972         this.render();
12973     },
12974
12975     render : function(bulkRender){
12976         var n = this.node, a = n.attributes;
12977         var targetNode = n.parentNode ?
12978               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12979
12980         if(!this.rendered){
12981             this.rendered = true;
12982
12983             this.renderElements(n, a, targetNode, bulkRender);
12984
12985             if(a.qtip){
12986                if(this.textNode.setAttributeNS){
12987                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12988                    if(a.qtipTitle){
12989                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12990                    }
12991                }else{
12992                    this.textNode.setAttribute("ext:qtip", a.qtip);
12993                    if(a.qtipTitle){
12994                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12995                    }
12996                }
12997             }else if(a.qtipCfg){
12998                 a.qtipCfg.target = Roo.id(this.textNode);
12999                 Roo.QuickTips.register(a.qtipCfg);
13000             }
13001             this.initEvents();
13002             if(!this.node.expanded){
13003                 this.updateExpandIcon();
13004             }
13005         }else{
13006             if(bulkRender === true) {
13007                 targetNode.appendChild(this.wrap);
13008             }
13009         }
13010     },
13011
13012     renderElements : function(n, a, targetNode, bulkRender)
13013     {
13014         // add some indent caching, this helps performance when rendering a large tree
13015         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13016         var t = n.getOwnerTree();
13017         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13018         if (typeof(n.attributes.html) != 'undefined') {
13019             txt = n.attributes.html;
13020         }
13021         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13022         var cb = typeof a.checked == 'boolean';
13023         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13024         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13025             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13026             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13027             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13028             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13029             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13030              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13031                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13032             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13033             "</li>"];
13034
13035         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13036             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13037                                 n.nextSibling.ui.getEl(), buf.join(""));
13038         }else{
13039             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13040         }
13041
13042         this.elNode = this.wrap.childNodes[0];
13043         this.ctNode = this.wrap.childNodes[1];
13044         var cs = this.elNode.childNodes;
13045         this.indentNode = cs[0];
13046         this.ecNode = cs[1];
13047         this.iconNode = cs[2];
13048         var index = 3;
13049         if(cb){
13050             this.checkbox = cs[3];
13051             index++;
13052         }
13053         this.anchor = cs[index];
13054         this.textNode = cs[index].firstChild;
13055     },
13056
13057     getAnchor : function(){
13058         return this.anchor;
13059     },
13060
13061     getTextEl : function(){
13062         return this.textNode;
13063     },
13064
13065     getIconEl : function(){
13066         return this.iconNode;
13067     },
13068
13069     isChecked : function(){
13070         return this.checkbox ? this.checkbox.checked : false;
13071     },
13072
13073     updateExpandIcon : function(){
13074         if(this.rendered){
13075             var n = this.node, c1, c2;
13076             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13077             var hasChild = n.hasChildNodes();
13078             if(hasChild){
13079                 if(n.expanded){
13080                     cls += "-minus";
13081                     c1 = "x-tree-node-collapsed";
13082                     c2 = "x-tree-node-expanded";
13083                 }else{
13084                     cls += "-plus";
13085                     c1 = "x-tree-node-expanded";
13086                     c2 = "x-tree-node-collapsed";
13087                 }
13088                 if(this.wasLeaf){
13089                     this.removeClass("x-tree-node-leaf");
13090                     this.wasLeaf = false;
13091                 }
13092                 if(this.c1 != c1 || this.c2 != c2){
13093                     Roo.fly(this.elNode).replaceClass(c1, c2);
13094                     this.c1 = c1; this.c2 = c2;
13095                 }
13096             }else{
13097                 // this changes non-leafs into leafs if they have no children.
13098                 // it's not very rational behaviour..
13099                 
13100                 if(!this.wasLeaf && this.node.leaf){
13101                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13102                     delete this.c1;
13103                     delete this.c2;
13104                     this.wasLeaf = true;
13105                 }
13106             }
13107             var ecc = "x-tree-ec-icon "+cls;
13108             if(this.ecc != ecc){
13109                 this.ecNode.className = ecc;
13110                 this.ecc = ecc;
13111             }
13112         }
13113     },
13114
13115     getChildIndent : function(){
13116         if(!this.childIndent){
13117             var buf = [];
13118             var p = this.node;
13119             while(p){
13120                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13121                     if(!p.isLast()) {
13122                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13123                     } else {
13124                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13125                     }
13126                 }
13127                 p = p.parentNode;
13128             }
13129             this.childIndent = buf.join("");
13130         }
13131         return this.childIndent;
13132     },
13133
13134     renderIndent : function(){
13135         if(this.rendered){
13136             var indent = "";
13137             var p = this.node.parentNode;
13138             if(p){
13139                 indent = p.ui.getChildIndent();
13140             }
13141             if(this.indentMarkup != indent){ // don't rerender if not required
13142                 this.indentNode.innerHTML = indent;
13143                 this.indentMarkup = indent;
13144             }
13145             this.updateExpandIcon();
13146         }
13147     }
13148 };
13149
13150 Roo.tree.RootTreeNodeUI = function(){
13151     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13152 };
13153 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13154     render : function(){
13155         if(!this.rendered){
13156             var targetNode = this.node.ownerTree.innerCt.dom;
13157             this.node.expanded = true;
13158             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13159             this.wrap = this.ctNode = targetNode.firstChild;
13160         }
13161     },
13162     collapse : function(){
13163     },
13164     expand : function(){
13165     }
13166 });/*
13167  * Based on:
13168  * Ext JS Library 1.1.1
13169  * Copyright(c) 2006-2007, Ext JS, LLC.
13170  *
13171  * Originally Released Under LGPL - original licence link has changed is not relivant.
13172  *
13173  * Fork - LGPL
13174  * <script type="text/javascript">
13175  */
13176 /**
13177  * @class Roo.tree.TreeLoader
13178  * @extends Roo.util.Observable
13179  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13180  * nodes from a specified URL. The response must be a javascript Array definition
13181  * who's elements are node definition objects. eg:
13182  * <pre><code>
13183 {  success : true,
13184    data :      [
13185    
13186     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13187     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13188     ]
13189 }
13190
13191
13192 </code></pre>
13193  * <br><br>
13194  * The old style respose with just an array is still supported, but not recommended.
13195  * <br><br>
13196  *
13197  * A server request is sent, and child nodes are loaded only when a node is expanded.
13198  * The loading node's id is passed to the server under the parameter name "node" to
13199  * enable the server to produce the correct child nodes.
13200  * <br><br>
13201  * To pass extra parameters, an event handler may be attached to the "beforeload"
13202  * event, and the parameters specified in the TreeLoader's baseParams property:
13203  * <pre><code>
13204     myTreeLoader.on("beforeload", function(treeLoader, node) {
13205         this.baseParams.category = node.attributes.category;
13206     }, this);
13207     
13208 </code></pre>
13209  *
13210  * This would pass an HTTP parameter called "category" to the server containing
13211  * the value of the Node's "category" attribute.
13212  * @constructor
13213  * Creates a new Treeloader.
13214  * @param {Object} config A config object containing config properties.
13215  */
13216 Roo.tree.TreeLoader = function(config){
13217     this.baseParams = {};
13218     this.requestMethod = "POST";
13219     Roo.apply(this, config);
13220
13221     this.addEvents({
13222     
13223         /**
13224          * @event beforeload
13225          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13226          * @param {Object} This TreeLoader object.
13227          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13228          * @param {Object} callback The callback function specified in the {@link #load} call.
13229          */
13230         beforeload : true,
13231         /**
13232          * @event load
13233          * Fires when the node has been successfuly loaded.
13234          * @param {Object} This TreeLoader object.
13235          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13236          * @param {Object} response The response object containing the data from the server.
13237          */
13238         load : true,
13239         /**
13240          * @event loadexception
13241          * Fires if the network request failed.
13242          * @param {Object} This TreeLoader object.
13243          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13244          * @param {Object} response The response object containing the data from the server.
13245          */
13246         loadexception : true,
13247         /**
13248          * @event create
13249          * Fires before a node is created, enabling you to return custom Node types 
13250          * @param {Object} This TreeLoader object.
13251          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13252          */
13253         create : true
13254     });
13255
13256     Roo.tree.TreeLoader.superclass.constructor.call(this);
13257 };
13258
13259 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13260     /**
13261     * @cfg {String} dataUrl The URL from which to request a Json string which
13262     * specifies an array of node definition object representing the child nodes
13263     * to be loaded.
13264     */
13265     /**
13266     * @cfg {String} requestMethod either GET or POST
13267     * defaults to POST (due to BC)
13268     * to be loaded.
13269     */
13270     /**
13271     * @cfg {Object} baseParams (optional) An object containing properties which
13272     * specify HTTP parameters to be passed to each request for child nodes.
13273     */
13274     /**
13275     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13276     * created by this loader. If the attributes sent by the server have an attribute in this object,
13277     * they take priority.
13278     */
13279     /**
13280     * @cfg {Object} uiProviders (optional) An object containing properties which
13281     * 
13282     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13283     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13284     * <i>uiProvider</i> attribute of a returned child node is a string rather
13285     * than a reference to a TreeNodeUI implementation, this that string value
13286     * is used as a property name in the uiProviders object. You can define the provider named
13287     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13288     */
13289     uiProviders : {},
13290
13291     /**
13292     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13293     * child nodes before loading.
13294     */
13295     clearOnLoad : true,
13296
13297     /**
13298     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13299     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13300     * Grid query { data : [ .....] }
13301     */
13302     
13303     root : false,
13304      /**
13305     * @cfg {String} queryParam (optional) 
13306     * Name of the query as it will be passed on the querystring (defaults to 'node')
13307     * eg. the request will be ?node=[id]
13308     */
13309     
13310     
13311     queryParam: false,
13312     
13313     /**
13314      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13315      * This is called automatically when a node is expanded, but may be used to reload
13316      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13317      * @param {Roo.tree.TreeNode} node
13318      * @param {Function} callback
13319      */
13320     load : function(node, callback){
13321         if(this.clearOnLoad){
13322             while(node.firstChild){
13323                 node.removeChild(node.firstChild);
13324             }
13325         }
13326         if(node.attributes.children){ // preloaded json children
13327             var cs = node.attributes.children;
13328             for(var i = 0, len = cs.length; i < len; i++){
13329                 node.appendChild(this.createNode(cs[i]));
13330             }
13331             if(typeof callback == "function"){
13332                 callback();
13333             }
13334         }else if(this.dataUrl){
13335             this.requestData(node, callback);
13336         }
13337     },
13338
13339     getParams: function(node){
13340         var buf = [], bp = this.baseParams;
13341         for(var key in bp){
13342             if(typeof bp[key] != "function"){
13343                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13344             }
13345         }
13346         var n = this.queryParam === false ? 'node' : this.queryParam;
13347         buf.push(n + "=", encodeURIComponent(node.id));
13348         return buf.join("");
13349     },
13350
13351     requestData : function(node, callback){
13352         if(this.fireEvent("beforeload", this, node, callback) !== false){
13353             this.transId = Roo.Ajax.request({
13354                 method:this.requestMethod,
13355                 url: this.dataUrl||this.url,
13356                 success: this.handleResponse,
13357                 failure: this.handleFailure,
13358                 scope: this,
13359                 argument: {callback: callback, node: node},
13360                 params: this.getParams(node)
13361             });
13362         }else{
13363             // if the load is cancelled, make sure we notify
13364             // the node that we are done
13365             if(typeof callback == "function"){
13366                 callback();
13367             }
13368         }
13369     },
13370
13371     isLoading : function(){
13372         return this.transId ? true : false;
13373     },
13374
13375     abort : function(){
13376         if(this.isLoading()){
13377             Roo.Ajax.abort(this.transId);
13378         }
13379     },
13380
13381     // private
13382     createNode : function(attr)
13383     {
13384         // apply baseAttrs, nice idea Corey!
13385         if(this.baseAttrs){
13386             Roo.applyIf(attr, this.baseAttrs);
13387         }
13388         if(this.applyLoader !== false){
13389             attr.loader = this;
13390         }
13391         // uiProvider = depreciated..
13392         
13393         if(typeof(attr.uiProvider) == 'string'){
13394            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13395                 /**  eval:var:attr */ eval(attr.uiProvider);
13396         }
13397         if(typeof(this.uiProviders['default']) != 'undefined') {
13398             attr.uiProvider = this.uiProviders['default'];
13399         }
13400         
13401         this.fireEvent('create', this, attr);
13402         
13403         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13404         return(attr.leaf ?
13405                         new Roo.tree.TreeNode(attr) :
13406                         new Roo.tree.AsyncTreeNode(attr));
13407     },
13408
13409     processResponse : function(response, node, callback)
13410     {
13411         var json = response.responseText;
13412         try {
13413             
13414             var o = Roo.decode(json);
13415             
13416             if (this.root === false && typeof(o.success) != undefined) {
13417                 this.root = 'data'; // the default behaviour for list like data..
13418                 }
13419                 
13420             if (this.root !== false &&  !o.success) {
13421                 // it's a failure condition.
13422                 var a = response.argument;
13423                 this.fireEvent("loadexception", this, a.node, response);
13424                 Roo.log("Load failed - should have a handler really");
13425                 return;
13426             }
13427             
13428             
13429             
13430             if (this.root !== false) {
13431                  o = o[this.root];
13432             }
13433             
13434             for(var i = 0, len = o.length; i < len; i++){
13435                 var n = this.createNode(o[i]);
13436                 if(n){
13437                     node.appendChild(n);
13438                 }
13439             }
13440             if(typeof callback == "function"){
13441                 callback(this, node);
13442             }
13443         }catch(e){
13444             this.handleFailure(response);
13445         }
13446     },
13447
13448     handleResponse : function(response){
13449         this.transId = false;
13450         var a = response.argument;
13451         this.processResponse(response, a.node, a.callback);
13452         this.fireEvent("load", this, a.node, response);
13453     },
13454
13455     handleFailure : function(response)
13456     {
13457         // should handle failure better..
13458         this.transId = false;
13459         var a = response.argument;
13460         this.fireEvent("loadexception", this, a.node, response);
13461         if(typeof a.callback == "function"){
13462             a.callback(this, a.node);
13463         }
13464     }
13465 });/*
13466  * Based on:
13467  * Ext JS Library 1.1.1
13468  * Copyright(c) 2006-2007, Ext JS, LLC.
13469  *
13470  * Originally Released Under LGPL - original licence link has changed is not relivant.
13471  *
13472  * Fork - LGPL
13473  * <script type="text/javascript">
13474  */
13475
13476 /**
13477 * @class Roo.tree.TreeFilter
13478 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13479 * @param {TreePanel} tree
13480 * @param {Object} config (optional)
13481  */
13482 Roo.tree.TreeFilter = function(tree, config){
13483     this.tree = tree;
13484     this.filtered = {};
13485     Roo.apply(this, config);
13486 };
13487
13488 Roo.tree.TreeFilter.prototype = {
13489     clearBlank:false,
13490     reverse:false,
13491     autoClear:false,
13492     remove:false,
13493
13494      /**
13495      * Filter the data by a specific attribute.
13496      * @param {String/RegExp} value Either string that the attribute value
13497      * should start with or a RegExp to test against the attribute
13498      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13499      * @param {TreeNode} startNode (optional) The node to start the filter at.
13500      */
13501     filter : function(value, attr, startNode){
13502         attr = attr || "text";
13503         var f;
13504         if(typeof value == "string"){
13505             var vlen = value.length;
13506             // auto clear empty filter
13507             if(vlen == 0 && this.clearBlank){
13508                 this.clear();
13509                 return;
13510             }
13511             value = value.toLowerCase();
13512             f = function(n){
13513                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13514             };
13515         }else if(value.exec){ // regex?
13516             f = function(n){
13517                 return value.test(n.attributes[attr]);
13518             };
13519         }else{
13520             throw 'Illegal filter type, must be string or regex';
13521         }
13522         this.filterBy(f, null, startNode);
13523         },
13524
13525     /**
13526      * Filter by a function. The passed function will be called with each
13527      * node in the tree (or from the startNode). If the function returns true, the node is kept
13528      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13529      * @param {Function} fn The filter function
13530      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13531      */
13532     filterBy : function(fn, scope, startNode){
13533         startNode = startNode || this.tree.root;
13534         if(this.autoClear){
13535             this.clear();
13536         }
13537         var af = this.filtered, rv = this.reverse;
13538         var f = function(n){
13539             if(n == startNode){
13540                 return true;
13541             }
13542             if(af[n.id]){
13543                 return false;
13544             }
13545             var m = fn.call(scope || n, n);
13546             if(!m || rv){
13547                 af[n.id] = n;
13548                 n.ui.hide();
13549                 return false;
13550             }
13551             return true;
13552         };
13553         startNode.cascade(f);
13554         if(this.remove){
13555            for(var id in af){
13556                if(typeof id != "function"){
13557                    var n = af[id];
13558                    if(n && n.parentNode){
13559                        n.parentNode.removeChild(n);
13560                    }
13561                }
13562            }
13563         }
13564     },
13565
13566     /**
13567      * Clears the current filter. Note: with the "remove" option
13568      * set a filter cannot be cleared.
13569      */
13570     clear : function(){
13571         var t = this.tree;
13572         var af = this.filtered;
13573         for(var id in af){
13574             if(typeof id != "function"){
13575                 var n = af[id];
13576                 if(n){
13577                     n.ui.show();
13578                 }
13579             }
13580         }
13581         this.filtered = {};
13582     }
13583 };
13584 /*
13585  * Based on:
13586  * Ext JS Library 1.1.1
13587  * Copyright(c) 2006-2007, Ext JS, LLC.
13588  *
13589  * Originally Released Under LGPL - original licence link has changed is not relivant.
13590  *
13591  * Fork - LGPL
13592  * <script type="text/javascript">
13593  */
13594  
13595
13596 /**
13597  * @class Roo.tree.TreeSorter
13598  * Provides sorting of nodes in a TreePanel
13599  * 
13600  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13601  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13602  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13603  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13604  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13605  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13606  * @constructor
13607  * @param {TreePanel} tree
13608  * @param {Object} config
13609  */
13610 Roo.tree.TreeSorter = function(tree, config){
13611     Roo.apply(this, config);
13612     tree.on("beforechildrenrendered", this.doSort, this);
13613     tree.on("append", this.updateSort, this);
13614     tree.on("insert", this.updateSort, this);
13615     
13616     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13617     var p = this.property || "text";
13618     var sortType = this.sortType;
13619     var fs = this.folderSort;
13620     var cs = this.caseSensitive === true;
13621     var leafAttr = this.leafAttr || 'leaf';
13622
13623     this.sortFn = function(n1, n2){
13624         if(fs){
13625             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13626                 return 1;
13627             }
13628             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13629                 return -1;
13630             }
13631         }
13632         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13633         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13634         if(v1 < v2){
13635                         return dsc ? +1 : -1;
13636                 }else if(v1 > v2){
13637                         return dsc ? -1 : +1;
13638         }else{
13639                 return 0;
13640         }
13641     };
13642 };
13643
13644 Roo.tree.TreeSorter.prototype = {
13645     doSort : function(node){
13646         node.sort(this.sortFn);
13647     },
13648     
13649     compareNodes : function(n1, n2){
13650         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13651     },
13652     
13653     updateSort : function(tree, node){
13654         if(node.childrenRendered){
13655             this.doSort.defer(1, this, [node]);
13656         }
13657     }
13658 };/*
13659  * Based on:
13660  * Ext JS Library 1.1.1
13661  * Copyright(c) 2006-2007, Ext JS, LLC.
13662  *
13663  * Originally Released Under LGPL - original licence link has changed is not relivant.
13664  *
13665  * Fork - LGPL
13666  * <script type="text/javascript">
13667  */
13668
13669 if(Roo.dd.DropZone){
13670     
13671 Roo.tree.TreeDropZone = function(tree, config){
13672     this.allowParentInsert = false;
13673     this.allowContainerDrop = false;
13674     this.appendOnly = false;
13675     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13676     this.tree = tree;
13677     this.lastInsertClass = "x-tree-no-status";
13678     this.dragOverData = {};
13679 };
13680
13681 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13682     ddGroup : "TreeDD",
13683     scroll:  true,
13684     
13685     expandDelay : 1000,
13686     
13687     expandNode : function(node){
13688         if(node.hasChildNodes() && !node.isExpanded()){
13689             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13690         }
13691     },
13692     
13693     queueExpand : function(node){
13694         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13695     },
13696     
13697     cancelExpand : function(){
13698         if(this.expandProcId){
13699             clearTimeout(this.expandProcId);
13700             this.expandProcId = false;
13701         }
13702     },
13703     
13704     isValidDropPoint : function(n, pt, dd, e, data){
13705         if(!n || !data){ return false; }
13706         var targetNode = n.node;
13707         var dropNode = data.node;
13708         // default drop rules
13709         if(!(targetNode && targetNode.isTarget && pt)){
13710             return false;
13711         }
13712         if(pt == "append" && targetNode.allowChildren === false){
13713             return false;
13714         }
13715         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13716             return false;
13717         }
13718         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13719             return false;
13720         }
13721         // reuse the object
13722         var overEvent = this.dragOverData;
13723         overEvent.tree = this.tree;
13724         overEvent.target = targetNode;
13725         overEvent.data = data;
13726         overEvent.point = pt;
13727         overEvent.source = dd;
13728         overEvent.rawEvent = e;
13729         overEvent.dropNode = dropNode;
13730         overEvent.cancel = false;  
13731         var result = this.tree.fireEvent("nodedragover", overEvent);
13732         return overEvent.cancel === false && result !== false;
13733     },
13734     
13735     getDropPoint : function(e, n, dd)
13736     {
13737         var tn = n.node;
13738         if(tn.isRoot){
13739             return tn.allowChildren !== false ? "append" : false; // always append for root
13740         }
13741         var dragEl = n.ddel;
13742         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13743         var y = Roo.lib.Event.getPageY(e);
13744         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13745         
13746         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13747         var noAppend = tn.allowChildren === false;
13748         if(this.appendOnly || tn.parentNode.allowChildren === false){
13749             return noAppend ? false : "append";
13750         }
13751         var noBelow = false;
13752         if(!this.allowParentInsert){
13753             noBelow = tn.hasChildNodes() && tn.isExpanded();
13754         }
13755         var q = (b - t) / (noAppend ? 2 : 3);
13756         if(y >= t && y < (t + q)){
13757             return "above";
13758         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13759             return "below";
13760         }else{
13761             return "append";
13762         }
13763     },
13764     
13765     onNodeEnter : function(n, dd, e, data)
13766     {
13767         this.cancelExpand();
13768     },
13769     
13770     onNodeOver : function(n, dd, e, data)
13771     {
13772        
13773         var pt = this.getDropPoint(e, n, dd);
13774         var node = n.node;
13775         
13776         // auto node expand check
13777         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13778             this.queueExpand(node);
13779         }else if(pt != "append"){
13780             this.cancelExpand();
13781         }
13782         
13783         // set the insert point style on the target node
13784         var returnCls = this.dropNotAllowed;
13785         if(this.isValidDropPoint(n, pt, dd, e, data)){
13786            if(pt){
13787                var el = n.ddel;
13788                var cls;
13789                if(pt == "above"){
13790                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13791                    cls = "x-tree-drag-insert-above";
13792                }else if(pt == "below"){
13793                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13794                    cls = "x-tree-drag-insert-below";
13795                }else{
13796                    returnCls = "x-tree-drop-ok-append";
13797                    cls = "x-tree-drag-append";
13798                }
13799                if(this.lastInsertClass != cls){
13800                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13801                    this.lastInsertClass = cls;
13802                }
13803            }
13804        }
13805        return returnCls;
13806     },
13807     
13808     onNodeOut : function(n, dd, e, data){
13809         
13810         this.cancelExpand();
13811         this.removeDropIndicators(n);
13812     },
13813     
13814     onNodeDrop : function(n, dd, e, data){
13815         var point = this.getDropPoint(e, n, dd);
13816         var targetNode = n.node;
13817         targetNode.ui.startDrop();
13818         if(!this.isValidDropPoint(n, point, dd, e, data)){
13819             targetNode.ui.endDrop();
13820             return false;
13821         }
13822         // first try to find the drop node
13823         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13824         var dropEvent = {
13825             tree : this.tree,
13826             target: targetNode,
13827             data: data,
13828             point: point,
13829             source: dd,
13830             rawEvent: e,
13831             dropNode: dropNode,
13832             cancel: !dropNode   
13833         };
13834         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13835         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13836             targetNode.ui.endDrop();
13837             return false;
13838         }
13839         // allow target changing
13840         targetNode = dropEvent.target;
13841         if(point == "append" && !targetNode.isExpanded()){
13842             targetNode.expand(false, null, function(){
13843                 this.completeDrop(dropEvent);
13844             }.createDelegate(this));
13845         }else{
13846             this.completeDrop(dropEvent);
13847         }
13848         return true;
13849     },
13850     
13851     completeDrop : function(de){
13852         var ns = de.dropNode, p = de.point, t = de.target;
13853         if(!(ns instanceof Array)){
13854             ns = [ns];
13855         }
13856         var n;
13857         for(var i = 0, len = ns.length; i < len; i++){
13858             n = ns[i];
13859             if(p == "above"){
13860                 t.parentNode.insertBefore(n, t);
13861             }else if(p == "below"){
13862                 t.parentNode.insertBefore(n, t.nextSibling);
13863             }else{
13864                 t.appendChild(n);
13865             }
13866         }
13867         n.ui.focus();
13868         if(this.tree.hlDrop){
13869             n.ui.highlight();
13870         }
13871         t.ui.endDrop();
13872         this.tree.fireEvent("nodedrop", de);
13873     },
13874     
13875     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13876         if(this.tree.hlDrop){
13877             dropNode.ui.focus();
13878             dropNode.ui.highlight();
13879         }
13880         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13881     },
13882     
13883     getTree : function(){
13884         return this.tree;
13885     },
13886     
13887     removeDropIndicators : function(n){
13888         if(n && n.ddel){
13889             var el = n.ddel;
13890             Roo.fly(el).removeClass([
13891                     "x-tree-drag-insert-above",
13892                     "x-tree-drag-insert-below",
13893                     "x-tree-drag-append"]);
13894             this.lastInsertClass = "_noclass";
13895         }
13896     },
13897     
13898     beforeDragDrop : function(target, e, id){
13899         this.cancelExpand();
13900         return true;
13901     },
13902     
13903     afterRepair : function(data){
13904         if(data && Roo.enableFx){
13905             data.node.ui.highlight();
13906         }
13907         this.hideProxy();
13908     } 
13909     
13910 });
13911
13912 }
13913 /*
13914  * Based on:
13915  * Ext JS Library 1.1.1
13916  * Copyright(c) 2006-2007, Ext JS, LLC.
13917  *
13918  * Originally Released Under LGPL - original licence link has changed is not relivant.
13919  *
13920  * Fork - LGPL
13921  * <script type="text/javascript">
13922  */
13923  
13924
13925 if(Roo.dd.DragZone){
13926 Roo.tree.TreeDragZone = function(tree, config){
13927     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13928     this.tree = tree;
13929 };
13930
13931 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13932     ddGroup : "TreeDD",
13933    
13934     onBeforeDrag : function(data, e){
13935         var n = data.node;
13936         return n && n.draggable && !n.disabled;
13937     },
13938      
13939     
13940     onInitDrag : function(e){
13941         var data = this.dragData;
13942         this.tree.getSelectionModel().select(data.node);
13943         this.proxy.update("");
13944         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13945         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13946     },
13947     
13948     getRepairXY : function(e, data){
13949         return data.node.ui.getDDRepairXY();
13950     },
13951     
13952     onEndDrag : function(data, e){
13953         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13954         
13955         
13956     },
13957     
13958     onValidDrop : function(dd, e, id){
13959         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13960         this.hideProxy();
13961     },
13962     
13963     beforeInvalidDrop : function(e, id){
13964         // this scrolls the original position back into view
13965         var sm = this.tree.getSelectionModel();
13966         sm.clearSelections();
13967         sm.select(this.dragData.node);
13968     }
13969 });
13970 }/*
13971  * Based on:
13972  * Ext JS Library 1.1.1
13973  * Copyright(c) 2006-2007, Ext JS, LLC.
13974  *
13975  * Originally Released Under LGPL - original licence link has changed is not relivant.
13976  *
13977  * Fork - LGPL
13978  * <script type="text/javascript">
13979  */
13980 /**
13981  * @class Roo.tree.TreeEditor
13982  * @extends Roo.Editor
13983  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13984  * as the editor field.
13985  * @constructor
13986  * @param {Object} config (used to be the tree panel.)
13987  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13988  * 
13989  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13990  * @cfg {Roo.form.TextField} field [required] The field configuration
13991  *
13992  * 
13993  */
13994 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13995     var tree = config;
13996     var field;
13997     if (oldconfig) { // old style..
13998         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13999     } else {
14000         // new style..
14001         tree = config.tree;
14002         config.field = config.field  || {};
14003         config.field.xtype = 'TextField';
14004         field = Roo.factory(config.field, Roo.form);
14005     }
14006     config = config || {};
14007     
14008     
14009     this.addEvents({
14010         /**
14011          * @event beforenodeedit
14012          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14013          * false from the handler of this event.
14014          * @param {Editor} this
14015          * @param {Roo.tree.Node} node 
14016          */
14017         "beforenodeedit" : true
14018     });
14019     
14020     //Roo.log(config);
14021     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14022
14023     this.tree = tree;
14024
14025     tree.on('beforeclick', this.beforeNodeClick, this);
14026     tree.getTreeEl().on('mousedown', this.hide, this);
14027     this.on('complete', this.updateNode, this);
14028     this.on('beforestartedit', this.fitToTree, this);
14029     this.on('startedit', this.bindScroll, this, {delay:10});
14030     this.on('specialkey', this.onSpecialKey, this);
14031 };
14032
14033 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14034     /**
14035      * @cfg {String} alignment
14036      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14037      */
14038     alignment: "l-l",
14039     // inherit
14040     autoSize: false,
14041     /**
14042      * @cfg {Boolean} hideEl
14043      * True to hide the bound element while the editor is displayed (defaults to false)
14044      */
14045     hideEl : false,
14046     /**
14047      * @cfg {String} cls
14048      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14049      */
14050     cls: "x-small-editor x-tree-editor",
14051     /**
14052      * @cfg {Boolean} shim
14053      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14054      */
14055     shim:false,
14056     // inherit
14057     shadow:"frame",
14058     /**
14059      * @cfg {Number} maxWidth
14060      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14061      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14062      * scroll and client offsets into account prior to each edit.
14063      */
14064     maxWidth: 250,
14065
14066     editDelay : 350,
14067
14068     // private
14069     fitToTree : function(ed, el){
14070         var td = this.tree.getTreeEl().dom, nd = el.dom;
14071         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14072             td.scrollLeft = nd.offsetLeft;
14073         }
14074         var w = Math.min(
14075                 this.maxWidth,
14076                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14077         this.setSize(w, '');
14078         
14079         return this.fireEvent('beforenodeedit', this, this.editNode);
14080         
14081     },
14082
14083     // private
14084     triggerEdit : function(node){
14085         this.completeEdit();
14086         this.editNode = node;
14087         this.startEdit(node.ui.textNode, node.text);
14088     },
14089
14090     // private
14091     bindScroll : function(){
14092         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14093     },
14094
14095     // private
14096     beforeNodeClick : function(node, e){
14097         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14098         this.lastClick = new Date();
14099         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14100             e.stopEvent();
14101             this.triggerEdit(node);
14102             return false;
14103         }
14104         return true;
14105     },
14106
14107     // private
14108     updateNode : function(ed, value){
14109         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14110         this.editNode.setText(value);
14111     },
14112
14113     // private
14114     onHide : function(){
14115         Roo.tree.TreeEditor.superclass.onHide.call(this);
14116         if(this.editNode){
14117             this.editNode.ui.focus();
14118         }
14119     },
14120
14121     // private
14122     onSpecialKey : function(field, e){
14123         var k = e.getKey();
14124         if(k == e.ESC){
14125             e.stopEvent();
14126             this.cancelEdit();
14127         }else if(k == e.ENTER && !e.hasModifier()){
14128             e.stopEvent();
14129             this.completeEdit();
14130         }
14131     }
14132 });//<Script type="text/javascript">
14133 /*
14134  * Based on:
14135  * Ext JS Library 1.1.1
14136  * Copyright(c) 2006-2007, Ext JS, LLC.
14137  *
14138  * Originally Released Under LGPL - original licence link has changed is not relivant.
14139  *
14140  * Fork - LGPL
14141  * <script type="text/javascript">
14142  */
14143  
14144 /**
14145  * Not documented??? - probably should be...
14146  */
14147
14148 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14149     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14150     
14151     renderElements : function(n, a, targetNode, bulkRender){
14152         //consel.log("renderElements?");
14153         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14154
14155         var t = n.getOwnerTree();
14156         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14157         
14158         var cols = t.columns;
14159         var bw = t.borderWidth;
14160         var c = cols[0];
14161         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14162          var cb = typeof a.checked == "boolean";
14163         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14164         var colcls = 'x-t-' + tid + '-c0';
14165         var buf = [
14166             '<li class="x-tree-node">',
14167             
14168                 
14169                 '<div class="x-tree-node-el ', a.cls,'">',
14170                     // extran...
14171                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14172                 
14173                 
14174                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14175                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14176                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14177                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14178                            (a.iconCls ? ' '+a.iconCls : ''),
14179                            '" unselectable="on" />',
14180                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14181                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14182                              
14183                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14184                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14185                             '<span unselectable="on" qtip="' + tx + '">',
14186                              tx,
14187                              '</span></a>' ,
14188                     '</div>',
14189                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14190                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14191                  ];
14192         for(var i = 1, len = cols.length; i < len; i++){
14193             c = cols[i];
14194             colcls = 'x-t-' + tid + '-c' +i;
14195             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14196             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14197                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14198                       "</div>");
14199          }
14200          
14201          buf.push(
14202             '</a>',
14203             '<div class="x-clear"></div></div>',
14204             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14205             "</li>");
14206         
14207         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14208             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14209                                 n.nextSibling.ui.getEl(), buf.join(""));
14210         }else{
14211             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14212         }
14213         var el = this.wrap.firstChild;
14214         this.elRow = el;
14215         this.elNode = el.firstChild;
14216         this.ranchor = el.childNodes[1];
14217         this.ctNode = this.wrap.childNodes[1];
14218         var cs = el.firstChild.childNodes;
14219         this.indentNode = cs[0];
14220         this.ecNode = cs[1];
14221         this.iconNode = cs[2];
14222         var index = 3;
14223         if(cb){
14224             this.checkbox = cs[3];
14225             index++;
14226         }
14227         this.anchor = cs[index];
14228         
14229         this.textNode = cs[index].firstChild;
14230         
14231         //el.on("click", this.onClick, this);
14232         //el.on("dblclick", this.onDblClick, this);
14233         
14234         
14235        // console.log(this);
14236     },
14237     initEvents : function(){
14238         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14239         
14240             
14241         var a = this.ranchor;
14242
14243         var el = Roo.get(a);
14244
14245         if(Roo.isOpera){ // opera render bug ignores the CSS
14246             el.setStyle("text-decoration", "none");
14247         }
14248
14249         el.on("click", this.onClick, this);
14250         el.on("dblclick", this.onDblClick, this);
14251         el.on("contextmenu", this.onContextMenu, this);
14252         
14253     },
14254     
14255     /*onSelectedChange : function(state){
14256         if(state){
14257             this.focus();
14258             this.addClass("x-tree-selected");
14259         }else{
14260             //this.blur();
14261             this.removeClass("x-tree-selected");
14262         }
14263     },*/
14264     addClass : function(cls){
14265         if(this.elRow){
14266             Roo.fly(this.elRow).addClass(cls);
14267         }
14268         
14269     },
14270     
14271     
14272     removeClass : function(cls){
14273         if(this.elRow){
14274             Roo.fly(this.elRow).removeClass(cls);
14275         }
14276     }
14277
14278     
14279     
14280 });//<Script type="text/javascript">
14281
14282 /*
14283  * Based on:
14284  * Ext JS Library 1.1.1
14285  * Copyright(c) 2006-2007, Ext JS, LLC.
14286  *
14287  * Originally Released Under LGPL - original licence link has changed is not relivant.
14288  *
14289  * Fork - LGPL
14290  * <script type="text/javascript">
14291  */
14292  
14293
14294 /**
14295  * @class Roo.tree.ColumnTree
14296  * @extends Roo.tree.TreePanel
14297  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14298  * @cfg {int} borderWidth  compined right/left border allowance
14299  * @constructor
14300  * @param {String/HTMLElement/Element} el The container element
14301  * @param {Object} config
14302  */
14303 Roo.tree.ColumnTree =  function(el, config)
14304 {
14305    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14306    this.addEvents({
14307         /**
14308         * @event resize
14309         * Fire this event on a container when it resizes
14310         * @param {int} w Width
14311         * @param {int} h Height
14312         */
14313        "resize" : true
14314     });
14315     this.on('resize', this.onResize, this);
14316 };
14317
14318 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14319     //lines:false,
14320     
14321     
14322     borderWidth: Roo.isBorderBox ? 0 : 2, 
14323     headEls : false,
14324     
14325     render : function(){
14326         // add the header.....
14327        
14328         Roo.tree.ColumnTree.superclass.render.apply(this);
14329         
14330         this.el.addClass('x-column-tree');
14331         
14332         this.headers = this.el.createChild(
14333             {cls:'x-tree-headers'},this.innerCt.dom);
14334    
14335         var cols = this.columns, c;
14336         var totalWidth = 0;
14337         this.headEls = [];
14338         var  len = cols.length;
14339         for(var i = 0; i < len; i++){
14340              c = cols[i];
14341              totalWidth += c.width;
14342             this.headEls.push(this.headers.createChild({
14343                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14344                  cn: {
14345                      cls:'x-tree-hd-text',
14346                      html: c.header
14347                  },
14348                  style:'width:'+(c.width-this.borderWidth)+'px;'
14349              }));
14350         }
14351         this.headers.createChild({cls:'x-clear'});
14352         // prevent floats from wrapping when clipped
14353         this.headers.setWidth(totalWidth);
14354         //this.innerCt.setWidth(totalWidth);
14355         this.innerCt.setStyle({ overflow: 'auto' });
14356         this.onResize(this.width, this.height);
14357              
14358         
14359     },
14360     onResize : function(w,h)
14361     {
14362         this.height = h;
14363         this.width = w;
14364         // resize cols..
14365         this.innerCt.setWidth(this.width);
14366         this.innerCt.setHeight(this.height-20);
14367         
14368         // headers...
14369         var cols = this.columns, c;
14370         var totalWidth = 0;
14371         var expEl = false;
14372         var len = cols.length;
14373         for(var i = 0; i < len; i++){
14374             c = cols[i];
14375             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14376                 // it's the expander..
14377                 expEl  = this.headEls[i];
14378                 continue;
14379             }
14380             totalWidth += c.width;
14381             
14382         }
14383         if (expEl) {
14384             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14385         }
14386         this.headers.setWidth(w-20);
14387
14388         
14389         
14390         
14391     }
14392 });
14393 /*
14394  * Based on:
14395  * Ext JS Library 1.1.1
14396  * Copyright(c) 2006-2007, Ext JS, LLC.
14397  *
14398  * Originally Released Under LGPL - original licence link has changed is not relivant.
14399  *
14400  * Fork - LGPL
14401  * <script type="text/javascript">
14402  */
14403  
14404 /**
14405  * @class Roo.menu.Menu
14406  * @extends Roo.util.Observable
14407  * @children Roo.menu.BaseItem
14408  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14409  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14410  * @constructor
14411  * Creates a new Menu
14412  * @param {Object} config Configuration options
14413  */
14414 Roo.menu.Menu = function(config){
14415     
14416     Roo.menu.Menu.superclass.constructor.call(this, config);
14417     
14418     this.id = this.id || Roo.id();
14419     this.addEvents({
14420         /**
14421          * @event beforeshow
14422          * Fires before this menu is displayed
14423          * @param {Roo.menu.Menu} this
14424          */
14425         beforeshow : true,
14426         /**
14427          * @event beforehide
14428          * Fires before this menu is hidden
14429          * @param {Roo.menu.Menu} this
14430          */
14431         beforehide : true,
14432         /**
14433          * @event show
14434          * Fires after this menu is displayed
14435          * @param {Roo.menu.Menu} this
14436          */
14437         show : true,
14438         /**
14439          * @event hide
14440          * Fires after this menu is hidden
14441          * @param {Roo.menu.Menu} this
14442          */
14443         hide : true,
14444         /**
14445          * @event click
14446          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14447          * @param {Roo.menu.Menu} this
14448          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14449          * @param {Roo.EventObject} e
14450          */
14451         click : true,
14452         /**
14453          * @event mouseover
14454          * Fires when the mouse is hovering over this menu
14455          * @param {Roo.menu.Menu} this
14456          * @param {Roo.EventObject} e
14457          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14458          */
14459         mouseover : true,
14460         /**
14461          * @event mouseout
14462          * Fires when the mouse exits this menu
14463          * @param {Roo.menu.Menu} this
14464          * @param {Roo.EventObject} e
14465          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14466          */
14467         mouseout : true,
14468         /**
14469          * @event itemclick
14470          * Fires when a menu item contained in this menu is clicked
14471          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14472          * @param {Roo.EventObject} e
14473          */
14474         itemclick: true
14475     });
14476     if (this.registerMenu) {
14477         Roo.menu.MenuMgr.register(this);
14478     }
14479     
14480     var mis = this.items;
14481     this.items = new Roo.util.MixedCollection();
14482     if(mis){
14483         this.add.apply(this, mis);
14484     }
14485 };
14486
14487 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14488     /**
14489      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14490      */
14491     minWidth : 120,
14492     /**
14493      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14494      * for bottom-right shadow (defaults to "sides")
14495      */
14496     shadow : "sides",
14497     /**
14498      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14499      * this menu (defaults to "tl-tr?")
14500      */
14501     subMenuAlign : "tl-tr?",
14502     /**
14503      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14504      * relative to its element of origin (defaults to "tl-bl?")
14505      */
14506     defaultAlign : "tl-bl?",
14507     /**
14508      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14509      */
14510     allowOtherMenus : false,
14511     /**
14512      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14513      */
14514     registerMenu : true,
14515
14516     hidden:true,
14517
14518     // private
14519     render : function(){
14520         if(this.el){
14521             return;
14522         }
14523         var el = this.el = new Roo.Layer({
14524             cls: "x-menu",
14525             shadow:this.shadow,
14526             constrain: false,
14527             parentEl: this.parentEl || document.body,
14528             zindex:15000
14529         });
14530
14531         this.keyNav = new Roo.menu.MenuNav(this);
14532
14533         if(this.plain){
14534             el.addClass("x-menu-plain");
14535         }
14536         if(this.cls){
14537             el.addClass(this.cls);
14538         }
14539         // generic focus element
14540         this.focusEl = el.createChild({
14541             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14542         });
14543         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14544         //disabling touch- as it's causing issues ..
14545         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14546         ul.on('click'   , this.onClick, this);
14547         
14548         
14549         ul.on("mouseover", this.onMouseOver, this);
14550         ul.on("mouseout", this.onMouseOut, this);
14551         this.items.each(function(item){
14552             if (item.hidden) {
14553                 return;
14554             }
14555             
14556             var li = document.createElement("li");
14557             li.className = "x-menu-list-item";
14558             ul.dom.appendChild(li);
14559             item.render(li, this);
14560         }, this);
14561         this.ul = ul;
14562         this.autoWidth();
14563     },
14564
14565     // private
14566     autoWidth : function(){
14567         var el = this.el, ul = this.ul;
14568         if(!el){
14569             return;
14570         }
14571         var w = this.width;
14572         if(w){
14573             el.setWidth(w);
14574         }else if(Roo.isIE){
14575             el.setWidth(this.minWidth);
14576             var t = el.dom.offsetWidth; // force recalc
14577             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14578         }
14579     },
14580
14581     // private
14582     delayAutoWidth : function(){
14583         if(this.rendered){
14584             if(!this.awTask){
14585                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14586             }
14587             this.awTask.delay(20);
14588         }
14589     },
14590
14591     // private
14592     findTargetItem : function(e){
14593         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14594         if(t && t.menuItemId){
14595             return this.items.get(t.menuItemId);
14596         }
14597     },
14598
14599     // private
14600     onClick : function(e){
14601         Roo.log("menu.onClick");
14602         var t = this.findTargetItem(e);
14603         if(!t){
14604             return;
14605         }
14606         Roo.log(e);
14607         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14608             if(t == this.activeItem && t.shouldDeactivate(e)){
14609                 this.activeItem.deactivate();
14610                 delete this.activeItem;
14611                 return;
14612             }
14613             if(t.canActivate){
14614                 this.setActiveItem(t, true);
14615             }
14616             return;
14617             
14618             
14619         }
14620         
14621         t.onClick(e);
14622         this.fireEvent("click", this, t, e);
14623     },
14624
14625     // private
14626     setActiveItem : function(item, autoExpand){
14627         if(item != this.activeItem){
14628             if(this.activeItem){
14629                 this.activeItem.deactivate();
14630             }
14631             this.activeItem = item;
14632             item.activate(autoExpand);
14633         }else if(autoExpand){
14634             item.expandMenu();
14635         }
14636     },
14637
14638     // private
14639     tryActivate : function(start, step){
14640         var items = this.items;
14641         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14642             var item = items.get(i);
14643             if(!item.disabled && item.canActivate){
14644                 this.setActiveItem(item, false);
14645                 return item;
14646             }
14647         }
14648         return false;
14649     },
14650
14651     // private
14652     onMouseOver : function(e){
14653         var t;
14654         if(t = this.findTargetItem(e)){
14655             if(t.canActivate && !t.disabled){
14656                 this.setActiveItem(t, true);
14657             }
14658         }
14659         this.fireEvent("mouseover", this, e, t);
14660     },
14661
14662     // private
14663     onMouseOut : function(e){
14664         var t;
14665         if(t = this.findTargetItem(e)){
14666             if(t == this.activeItem && t.shouldDeactivate(e)){
14667                 this.activeItem.deactivate();
14668                 delete this.activeItem;
14669             }
14670         }
14671         this.fireEvent("mouseout", this, e, t);
14672     },
14673
14674     /**
14675      * Read-only.  Returns true if the menu is currently displayed, else false.
14676      * @type Boolean
14677      */
14678     isVisible : function(){
14679         return this.el && !this.hidden;
14680     },
14681
14682     /**
14683      * Displays this menu relative to another element
14684      * @param {String/HTMLElement/Roo.Element} element The element to align to
14685      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14686      * the element (defaults to this.defaultAlign)
14687      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14688      */
14689     show : function(el, pos, parentMenu){
14690         this.parentMenu = parentMenu;
14691         if(!this.el){
14692             this.render();
14693         }
14694         this.fireEvent("beforeshow", this);
14695         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14696     },
14697
14698     /**
14699      * Displays this menu at a specific xy position
14700      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14701      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14702      */
14703     showAt : function(xy, parentMenu, /* private: */_e){
14704         this.parentMenu = parentMenu;
14705         if(!this.el){
14706             this.render();
14707         }
14708         if(_e !== false){
14709             this.fireEvent("beforeshow", this);
14710             xy = this.el.adjustForConstraints(xy);
14711         }
14712         this.el.setXY(xy);
14713         this.el.show();
14714         this.hidden = false;
14715         this.focus();
14716         this.fireEvent("show", this);
14717     },
14718
14719     focus : function(){
14720         if(!this.hidden){
14721             this.doFocus.defer(50, this);
14722         }
14723     },
14724
14725     doFocus : function(){
14726         if(!this.hidden){
14727             this.focusEl.focus();
14728         }
14729     },
14730
14731     /**
14732      * Hides this menu and optionally all parent menus
14733      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14734      */
14735     hide : function(deep){
14736         if(this.el && this.isVisible()){
14737             this.fireEvent("beforehide", this);
14738             if(this.activeItem){
14739                 this.activeItem.deactivate();
14740                 this.activeItem = null;
14741             }
14742             this.el.hide();
14743             this.hidden = true;
14744             this.fireEvent("hide", this);
14745         }
14746         if(deep === true && this.parentMenu){
14747             this.parentMenu.hide(true);
14748         }
14749     },
14750
14751     /**
14752      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14753      * Any of the following are valid:
14754      * <ul>
14755      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14756      * <li>An HTMLElement object which will be converted to a menu item</li>
14757      * <li>A menu item config object that will be created as a new menu item</li>
14758      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14759      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14760      * </ul>
14761      * Usage:
14762      * <pre><code>
14763 // Create the menu
14764 var menu = new Roo.menu.Menu();
14765
14766 // Create a menu item to add by reference
14767 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14768
14769 // Add a bunch of items at once using different methods.
14770 // Only the last item added will be returned.
14771 var item = menu.add(
14772     menuItem,                // add existing item by ref
14773     'Dynamic Item',          // new TextItem
14774     '-',                     // new separator
14775     { text: 'Config Item' }  // new item by config
14776 );
14777 </code></pre>
14778      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14779      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14780      */
14781     add : function(){
14782         var a = arguments, l = a.length, item;
14783         for(var i = 0; i < l; i++){
14784             var el = a[i];
14785             if ((typeof(el) == "object") && el.xtype && el.xns) {
14786                 el = Roo.factory(el, Roo.menu);
14787             }
14788             
14789             if(el.render){ // some kind of Item
14790                 item = this.addItem(el);
14791             }else if(typeof el == "string"){ // string
14792                 if(el == "separator" || el == "-"){
14793                     item = this.addSeparator();
14794                 }else{
14795                     item = this.addText(el);
14796                 }
14797             }else if(el.tagName || el.el){ // element
14798                 item = this.addElement(el);
14799             }else if(typeof el == "object"){ // must be menu item config?
14800                 item = this.addMenuItem(el);
14801             }
14802         }
14803         return item;
14804     },
14805
14806     /**
14807      * Returns this menu's underlying {@link Roo.Element} object
14808      * @return {Roo.Element} The element
14809      */
14810     getEl : function(){
14811         if(!this.el){
14812             this.render();
14813         }
14814         return this.el;
14815     },
14816
14817     /**
14818      * Adds a separator bar to the menu
14819      * @return {Roo.menu.Item} The menu item that was added
14820      */
14821     addSeparator : function(){
14822         return this.addItem(new Roo.menu.Separator());
14823     },
14824
14825     /**
14826      * Adds an {@link Roo.Element} object to the menu
14827      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14828      * @return {Roo.menu.Item} The menu item that was added
14829      */
14830     addElement : function(el){
14831         return this.addItem(new Roo.menu.BaseItem(el));
14832     },
14833
14834     /**
14835      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14836      * @param {Roo.menu.Item} item The menu item to add
14837      * @return {Roo.menu.Item} The menu item that was added
14838      */
14839     addItem : function(item){
14840         this.items.add(item);
14841         if(this.ul){
14842             var li = document.createElement("li");
14843             li.className = "x-menu-list-item";
14844             this.ul.dom.appendChild(li);
14845             item.render(li, this);
14846             this.delayAutoWidth();
14847         }
14848         return item;
14849     },
14850
14851     /**
14852      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14853      * @param {Object} config A MenuItem config object
14854      * @return {Roo.menu.Item} The menu item that was added
14855      */
14856     addMenuItem : function(config){
14857         if(!(config instanceof Roo.menu.Item)){
14858             if(typeof config.checked == "boolean"){ // must be check menu item config?
14859                 config = new Roo.menu.CheckItem(config);
14860             }else{
14861                 config = new Roo.menu.Item(config);
14862             }
14863         }
14864         return this.addItem(config);
14865     },
14866
14867     /**
14868      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14869      * @param {String} text The text to display in the menu item
14870      * @return {Roo.menu.Item} The menu item that was added
14871      */
14872     addText : function(text){
14873         return this.addItem(new Roo.menu.TextItem({ text : text }));
14874     },
14875
14876     /**
14877      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14878      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14879      * @param {Roo.menu.Item} item The menu item to add
14880      * @return {Roo.menu.Item} The menu item that was added
14881      */
14882     insert : function(index, item){
14883         this.items.insert(index, item);
14884         if(this.ul){
14885             var li = document.createElement("li");
14886             li.className = "x-menu-list-item";
14887             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14888             item.render(li, this);
14889             this.delayAutoWidth();
14890         }
14891         return item;
14892     },
14893
14894     /**
14895      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14896      * @param {Roo.menu.Item} item The menu item to remove
14897      */
14898     remove : function(item){
14899         this.items.removeKey(item.id);
14900         item.destroy();
14901     },
14902
14903     /**
14904      * Removes and destroys all items in the menu
14905      */
14906     removeAll : function(){
14907         var f;
14908         while(f = this.items.first()){
14909             this.remove(f);
14910         }
14911     }
14912 });
14913
14914 // MenuNav is a private utility class used internally by the Menu
14915 Roo.menu.MenuNav = function(menu){
14916     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14917     this.scope = this.menu = menu;
14918 };
14919
14920 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14921     doRelay : function(e, h){
14922         var k = e.getKey();
14923         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14924             this.menu.tryActivate(0, 1);
14925             return false;
14926         }
14927         return h.call(this.scope || this, e, this.menu);
14928     },
14929
14930     up : function(e, m){
14931         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14932             m.tryActivate(m.items.length-1, -1);
14933         }
14934     },
14935
14936     down : function(e, m){
14937         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14938             m.tryActivate(0, 1);
14939         }
14940     },
14941
14942     right : function(e, m){
14943         if(m.activeItem){
14944             m.activeItem.expandMenu(true);
14945         }
14946     },
14947
14948     left : function(e, m){
14949         m.hide();
14950         if(m.parentMenu && m.parentMenu.activeItem){
14951             m.parentMenu.activeItem.activate();
14952         }
14953     },
14954
14955     enter : function(e, m){
14956         if(m.activeItem){
14957             e.stopPropagation();
14958             m.activeItem.onClick(e);
14959             m.fireEvent("click", this, m.activeItem);
14960             return true;
14961         }
14962     }
14963 });/*
14964  * Based on:
14965  * Ext JS Library 1.1.1
14966  * Copyright(c) 2006-2007, Ext JS, LLC.
14967  *
14968  * Originally Released Under LGPL - original licence link has changed is not relivant.
14969  *
14970  * Fork - LGPL
14971  * <script type="text/javascript">
14972  */
14973  
14974 /**
14975  * @class Roo.menu.MenuMgr
14976  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14977  * @static
14978  */
14979 Roo.menu.MenuMgr = function(){
14980    var menus, active, groups = {}, attached = false, lastShow = new Date();
14981
14982    // private - called when first menu is created
14983    function init(){
14984        menus = {};
14985        active = new Roo.util.MixedCollection();
14986        Roo.get(document).addKeyListener(27, function(){
14987            if(active.length > 0){
14988                hideAll();
14989            }
14990        });
14991    }
14992
14993    // private
14994    function hideAll(){
14995        if(active && active.length > 0){
14996            var c = active.clone();
14997            c.each(function(m){
14998                m.hide();
14999            });
15000        }
15001    }
15002
15003    // private
15004    function onHide(m){
15005        active.remove(m);
15006        if(active.length < 1){
15007            Roo.get(document).un("mousedown", onMouseDown);
15008            attached = false;
15009        }
15010    }
15011
15012    // private
15013    function onShow(m){
15014        var last = active.last();
15015        lastShow = new Date();
15016        active.add(m);
15017        if(!attached){
15018            Roo.get(document).on("mousedown", onMouseDown);
15019            attached = true;
15020        }
15021        if(m.parentMenu){
15022           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15023           m.parentMenu.activeChild = m;
15024        }else if(last && last.isVisible()){
15025           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15026        }
15027    }
15028
15029    // private
15030    function onBeforeHide(m){
15031        if(m.activeChild){
15032            m.activeChild.hide();
15033        }
15034        if(m.autoHideTimer){
15035            clearTimeout(m.autoHideTimer);
15036            delete m.autoHideTimer;
15037        }
15038    }
15039
15040    // private
15041    function onBeforeShow(m){
15042        var pm = m.parentMenu;
15043        if(!pm && !m.allowOtherMenus){
15044            hideAll();
15045        }else if(pm && pm.activeChild && active != m){
15046            pm.activeChild.hide();
15047        }
15048    }
15049
15050    // private
15051    function onMouseDown(e){
15052        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15053            hideAll();
15054        }
15055    }
15056
15057    // private
15058    function onBeforeCheck(mi, state){
15059        if(state){
15060            var g = groups[mi.group];
15061            for(var i = 0, l = g.length; i < l; i++){
15062                if(g[i] != mi){
15063                    g[i].setChecked(false);
15064                }
15065            }
15066        }
15067    }
15068
15069    return {
15070
15071        /**
15072         * Hides all menus that are currently visible
15073         */
15074        hideAll : function(){
15075             hideAll();  
15076        },
15077
15078        // private
15079        register : function(menu){
15080            if(!menus){
15081                init();
15082            }
15083            menus[menu.id] = menu;
15084            menu.on("beforehide", onBeforeHide);
15085            menu.on("hide", onHide);
15086            menu.on("beforeshow", onBeforeShow);
15087            menu.on("show", onShow);
15088            var g = menu.group;
15089            if(g && menu.events["checkchange"]){
15090                if(!groups[g]){
15091                    groups[g] = [];
15092                }
15093                groups[g].push(menu);
15094                menu.on("checkchange", onCheck);
15095            }
15096        },
15097
15098         /**
15099          * Returns a {@link Roo.menu.Menu} object
15100          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15101          * be used to generate and return a new Menu instance.
15102          */
15103        get : function(menu){
15104            if(typeof menu == "string"){ // menu id
15105                return menus[menu];
15106            }else if(menu.events){  // menu instance
15107                return menu;
15108            }else if(typeof menu.length == 'number'){ // array of menu items?
15109                return new Roo.menu.Menu({items:menu});
15110            }else{ // otherwise, must be a config
15111                return new Roo.menu.Menu(menu);
15112            }
15113        },
15114
15115        // private
15116        unregister : function(menu){
15117            delete menus[menu.id];
15118            menu.un("beforehide", onBeforeHide);
15119            menu.un("hide", onHide);
15120            menu.un("beforeshow", onBeforeShow);
15121            menu.un("show", onShow);
15122            var g = menu.group;
15123            if(g && menu.events["checkchange"]){
15124                groups[g].remove(menu);
15125                menu.un("checkchange", onCheck);
15126            }
15127        },
15128
15129        // private
15130        registerCheckable : function(menuItem){
15131            var g = menuItem.group;
15132            if(g){
15133                if(!groups[g]){
15134                    groups[g] = [];
15135                }
15136                groups[g].push(menuItem);
15137                menuItem.on("beforecheckchange", onBeforeCheck);
15138            }
15139        },
15140
15141        // private
15142        unregisterCheckable : function(menuItem){
15143            var g = menuItem.group;
15144            if(g){
15145                groups[g].remove(menuItem);
15146                menuItem.un("beforecheckchange", onBeforeCheck);
15147            }
15148        }
15149    };
15150 }();/*
15151  * Based on:
15152  * Ext JS Library 1.1.1
15153  * Copyright(c) 2006-2007, Ext JS, LLC.
15154  *
15155  * Originally Released Under LGPL - original licence link has changed is not relivant.
15156  *
15157  * Fork - LGPL
15158  * <script type="text/javascript">
15159  */
15160  
15161
15162 /**
15163  * @class Roo.menu.BaseItem
15164  * @extends Roo.Component
15165  * @abstract
15166  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15167  * management and base configuration options shared by all menu components.
15168  * @constructor
15169  * Creates a new BaseItem
15170  * @param {Object} config Configuration options
15171  */
15172 Roo.menu.BaseItem = function(config){
15173     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15174
15175     this.addEvents({
15176         /**
15177          * @event click
15178          * Fires when this item is clicked
15179          * @param {Roo.menu.BaseItem} this
15180          * @param {Roo.EventObject} e
15181          */
15182         click: true,
15183         /**
15184          * @event activate
15185          * Fires when this item is activated
15186          * @param {Roo.menu.BaseItem} this
15187          */
15188         activate : true,
15189         /**
15190          * @event deactivate
15191          * Fires when this item is deactivated
15192          * @param {Roo.menu.BaseItem} this
15193          */
15194         deactivate : true
15195     });
15196
15197     if(this.handler){
15198         this.on("click", this.handler, this.scope, true);
15199     }
15200 };
15201
15202 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15203     /**
15204      * @cfg {Function} handler
15205      * A function that will handle the click event of this menu item (defaults to undefined)
15206      */
15207     /**
15208      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15209      */
15210     canActivate : false,
15211     
15212      /**
15213      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15214      */
15215     hidden: false,
15216     
15217     /**
15218      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15219      */
15220     activeClass : "x-menu-item-active",
15221     /**
15222      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15223      */
15224     hideOnClick : true,
15225     /**
15226      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15227      */
15228     hideDelay : 100,
15229
15230     // private
15231     ctype: "Roo.menu.BaseItem",
15232
15233     // private
15234     actionMode : "container",
15235
15236     // private
15237     render : function(container, parentMenu){
15238         this.parentMenu = parentMenu;
15239         Roo.menu.BaseItem.superclass.render.call(this, container);
15240         this.container.menuItemId = this.id;
15241     },
15242
15243     // private
15244     onRender : function(container, position){
15245         this.el = Roo.get(this.el);
15246         container.dom.appendChild(this.el.dom);
15247     },
15248
15249     // private
15250     onClick : function(e){
15251         if(!this.disabled && this.fireEvent("click", this, e) !== false
15252                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15253             this.handleClick(e);
15254         }else{
15255             e.stopEvent();
15256         }
15257     },
15258
15259     // private
15260     activate : function(){
15261         if(this.disabled){
15262             return false;
15263         }
15264         var li = this.container;
15265         li.addClass(this.activeClass);
15266         this.region = li.getRegion().adjust(2, 2, -2, -2);
15267         this.fireEvent("activate", this);
15268         return true;
15269     },
15270
15271     // private
15272     deactivate : function(){
15273         this.container.removeClass(this.activeClass);
15274         this.fireEvent("deactivate", this);
15275     },
15276
15277     // private
15278     shouldDeactivate : function(e){
15279         return !this.region || !this.region.contains(e.getPoint());
15280     },
15281
15282     // private
15283     handleClick : function(e){
15284         if(this.hideOnClick){
15285             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15286         }
15287     },
15288
15289     // private
15290     expandMenu : function(autoActivate){
15291         // do nothing
15292     },
15293
15294     // private
15295     hideMenu : function(){
15296         // do nothing
15297     }
15298 });/*
15299  * Based on:
15300  * Ext JS Library 1.1.1
15301  * Copyright(c) 2006-2007, Ext JS, LLC.
15302  *
15303  * Originally Released Under LGPL - original licence link has changed is not relivant.
15304  *
15305  * Fork - LGPL
15306  * <script type="text/javascript">
15307  */
15308  
15309 /**
15310  * @class Roo.menu.Adapter
15311  * @extends Roo.menu.BaseItem
15312  * @abstract
15313  * 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.
15314  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15315  * @constructor
15316  * Creates a new Adapter
15317  * @param {Object} config Configuration options
15318  */
15319 Roo.menu.Adapter = function(component, config){
15320     Roo.menu.Adapter.superclass.constructor.call(this, config);
15321     this.component = component;
15322 };
15323 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15324     // private
15325     canActivate : true,
15326
15327     // private
15328     onRender : function(container, position){
15329         this.component.render(container);
15330         this.el = this.component.getEl();
15331     },
15332
15333     // private
15334     activate : function(){
15335         if(this.disabled){
15336             return false;
15337         }
15338         this.component.focus();
15339         this.fireEvent("activate", this);
15340         return true;
15341     },
15342
15343     // private
15344     deactivate : function(){
15345         this.fireEvent("deactivate", this);
15346     },
15347
15348     // private
15349     disable : function(){
15350         this.component.disable();
15351         Roo.menu.Adapter.superclass.disable.call(this);
15352     },
15353
15354     // private
15355     enable : function(){
15356         this.component.enable();
15357         Roo.menu.Adapter.superclass.enable.call(this);
15358     }
15359 });/*
15360  * Based on:
15361  * Ext JS Library 1.1.1
15362  * Copyright(c) 2006-2007, Ext JS, LLC.
15363  *
15364  * Originally Released Under LGPL - original licence link has changed is not relivant.
15365  *
15366  * Fork - LGPL
15367  * <script type="text/javascript">
15368  */
15369
15370 /**
15371  * @class Roo.menu.TextItem
15372  * @extends Roo.menu.BaseItem
15373  * Adds a static text string to a menu, usually used as either a heading or group separator.
15374  * Note: old style constructor with text is still supported.
15375  * 
15376  * @constructor
15377  * Creates a new TextItem
15378  * @param {Object} cfg Configuration
15379  */
15380 Roo.menu.TextItem = function(cfg){
15381     if (typeof(cfg) == 'string') {
15382         this.text = cfg;
15383     } else {
15384         Roo.apply(this,cfg);
15385     }
15386     
15387     Roo.menu.TextItem.superclass.constructor.call(this);
15388 };
15389
15390 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15391     /**
15392      * @cfg {String} text Text to show on item.
15393      */
15394     text : '',
15395     
15396     /**
15397      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15398      */
15399     hideOnClick : false,
15400     /**
15401      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15402      */
15403     itemCls : "x-menu-text",
15404
15405     // private
15406     onRender : function(){
15407         var s = document.createElement("span");
15408         s.className = this.itemCls;
15409         s.innerHTML = this.text;
15410         this.el = s;
15411         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15412     }
15413 });/*
15414  * Based on:
15415  * Ext JS Library 1.1.1
15416  * Copyright(c) 2006-2007, Ext JS, LLC.
15417  *
15418  * Originally Released Under LGPL - original licence link has changed is not relivant.
15419  *
15420  * Fork - LGPL
15421  * <script type="text/javascript">
15422  */
15423
15424 /**
15425  * @class Roo.menu.Separator
15426  * @extends Roo.menu.BaseItem
15427  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15428  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15429  * @constructor
15430  * @param {Object} config Configuration options
15431  */
15432 Roo.menu.Separator = function(config){
15433     Roo.menu.Separator.superclass.constructor.call(this, config);
15434 };
15435
15436 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15437     /**
15438      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15439      */
15440     itemCls : "x-menu-sep",
15441     /**
15442      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15443      */
15444     hideOnClick : false,
15445
15446     // private
15447     onRender : function(li){
15448         var s = document.createElement("span");
15449         s.className = this.itemCls;
15450         s.innerHTML = "&#160;";
15451         this.el = s;
15452         li.addClass("x-menu-sep-li");
15453         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15454     }
15455 });/*
15456  * Based on:
15457  * Ext JS Library 1.1.1
15458  * Copyright(c) 2006-2007, Ext JS, LLC.
15459  *
15460  * Originally Released Under LGPL - original licence link has changed is not relivant.
15461  *
15462  * Fork - LGPL
15463  * <script type="text/javascript">
15464  */
15465 /**
15466  * @class Roo.menu.Item
15467  * @extends Roo.menu.BaseItem
15468  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15469  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15470  * activation and click handling.
15471  * @constructor
15472  * Creates a new Item
15473  * @param {Object} config Configuration options
15474  */
15475 Roo.menu.Item = function(config){
15476     Roo.menu.Item.superclass.constructor.call(this, config);
15477     if(this.menu){
15478         this.menu = Roo.menu.MenuMgr.get(this.menu);
15479     }
15480 };
15481 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15482     /**
15483      * @cfg {Roo.menu.Menu} menu
15484      * A Sub menu
15485      */
15486     /**
15487      * @cfg {String} text
15488      * The text to show on the menu item.
15489      */
15490     text: '',
15491      /**
15492      * @cfg {String} HTML to render in menu
15493      * The text to show on the menu item (HTML version).
15494      */
15495     html: '',
15496     /**
15497      * @cfg {String} icon
15498      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15499      */
15500     icon: undefined,
15501     /**
15502      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15503      */
15504     itemCls : "x-menu-item",
15505     /**
15506      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15507      */
15508     canActivate : true,
15509     /**
15510      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15511      */
15512     showDelay: 200,
15513     // doc'd in BaseItem
15514     hideDelay: 200,
15515
15516     // private
15517     ctype: "Roo.menu.Item",
15518     
15519     // private
15520     onRender : function(container, position){
15521         var el = document.createElement("a");
15522         el.hideFocus = true;
15523         el.unselectable = "on";
15524         el.href = this.href || "#";
15525         if(this.hrefTarget){
15526             el.target = this.hrefTarget;
15527         }
15528         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15529         
15530         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15531         
15532         el.innerHTML = String.format(
15533                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15534                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15535         this.el = el;
15536         Roo.menu.Item.superclass.onRender.call(this, container, position);
15537     },
15538
15539     /**
15540      * Sets the text to display in this menu item
15541      * @param {String} text The text to display
15542      * @param {Boolean} isHTML true to indicate text is pure html.
15543      */
15544     setText : function(text, isHTML){
15545         if (isHTML) {
15546             this.html = text;
15547         } else {
15548             this.text = text;
15549             this.html = '';
15550         }
15551         if(this.rendered){
15552             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15553      
15554             this.el.update(String.format(
15555                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15556                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15557             this.parentMenu.autoWidth();
15558         }
15559     },
15560
15561     // private
15562     handleClick : function(e){
15563         if(!this.href){ // if no link defined, stop the event automatically
15564             e.stopEvent();
15565         }
15566         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15567     },
15568
15569     // private
15570     activate : function(autoExpand){
15571         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15572             this.focus();
15573             if(autoExpand){
15574                 this.expandMenu();
15575             }
15576         }
15577         return true;
15578     },
15579
15580     // private
15581     shouldDeactivate : function(e){
15582         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15583             if(this.menu && this.menu.isVisible()){
15584                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15585             }
15586             return true;
15587         }
15588         return false;
15589     },
15590
15591     // private
15592     deactivate : function(){
15593         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15594         this.hideMenu();
15595     },
15596
15597     // private
15598     expandMenu : function(autoActivate){
15599         if(!this.disabled && this.menu){
15600             clearTimeout(this.hideTimer);
15601             delete this.hideTimer;
15602             if(!this.menu.isVisible() && !this.showTimer){
15603                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15604             }else if (this.menu.isVisible() && autoActivate){
15605                 this.menu.tryActivate(0, 1);
15606             }
15607         }
15608     },
15609
15610     // private
15611     deferExpand : function(autoActivate){
15612         delete this.showTimer;
15613         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15614         if(autoActivate){
15615             this.menu.tryActivate(0, 1);
15616         }
15617     },
15618
15619     // private
15620     hideMenu : function(){
15621         clearTimeout(this.showTimer);
15622         delete this.showTimer;
15623         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15624             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15625         }
15626     },
15627
15628     // private
15629     deferHide : function(){
15630         delete this.hideTimer;
15631         this.menu.hide();
15632     }
15633 });/*
15634  * Based on:
15635  * Ext JS Library 1.1.1
15636  * Copyright(c) 2006-2007, Ext JS, LLC.
15637  *
15638  * Originally Released Under LGPL - original licence link has changed is not relivant.
15639  *
15640  * Fork - LGPL
15641  * <script type="text/javascript">
15642  */
15643  
15644 /**
15645  * @class Roo.menu.CheckItem
15646  * @extends Roo.menu.Item
15647  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15648  * @constructor
15649  * Creates a new CheckItem
15650  * @param {Object} config Configuration options
15651  */
15652 Roo.menu.CheckItem = function(config){
15653     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15654     this.addEvents({
15655         /**
15656          * @event beforecheckchange
15657          * Fires before the checked value is set, providing an opportunity to cancel if needed
15658          * @param {Roo.menu.CheckItem} this
15659          * @param {Boolean} checked The new checked value that will be set
15660          */
15661         "beforecheckchange" : true,
15662         /**
15663          * @event checkchange
15664          * Fires after the checked value has been set
15665          * @param {Roo.menu.CheckItem} this
15666          * @param {Boolean} checked The checked value that was set
15667          */
15668         "checkchange" : true
15669     });
15670     if(this.checkHandler){
15671         this.on('checkchange', this.checkHandler, this.scope);
15672     }
15673 };
15674 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15675     /**
15676      * @cfg {String} group
15677      * All check items with the same group name will automatically be grouped into a single-select
15678      * radio button group (defaults to '')
15679      */
15680     /**
15681      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15682      */
15683     itemCls : "x-menu-item x-menu-check-item",
15684     /**
15685      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15686      */
15687     groupClass : "x-menu-group-item",
15688
15689     /**
15690      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15691      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15692      * initialized with checked = true will be rendered as checked.
15693      */
15694     checked: false,
15695
15696     // private
15697     ctype: "Roo.menu.CheckItem",
15698
15699     // private
15700     onRender : function(c){
15701         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15702         if(this.group){
15703             this.el.addClass(this.groupClass);
15704         }
15705         Roo.menu.MenuMgr.registerCheckable(this);
15706         if(this.checked){
15707             this.checked = false;
15708             this.setChecked(true, true);
15709         }
15710     },
15711
15712     // private
15713     destroy : function(){
15714         if(this.rendered){
15715             Roo.menu.MenuMgr.unregisterCheckable(this);
15716         }
15717         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15718     },
15719
15720     /**
15721      * Set the checked state of this item
15722      * @param {Boolean} checked The new checked value
15723      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15724      */
15725     setChecked : function(state, suppressEvent){
15726         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15727             if(this.container){
15728                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15729             }
15730             this.checked = state;
15731             if(suppressEvent !== true){
15732                 this.fireEvent("checkchange", this, state);
15733             }
15734         }
15735     },
15736
15737     // private
15738     handleClick : function(e){
15739        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15740            this.setChecked(!this.checked);
15741        }
15742        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15743     }
15744 });/*
15745  * Based on:
15746  * Ext JS Library 1.1.1
15747  * Copyright(c) 2006-2007, Ext JS, LLC.
15748  *
15749  * Originally Released Under LGPL - original licence link has changed is not relivant.
15750  *
15751  * Fork - LGPL
15752  * <script type="text/javascript">
15753  */
15754  
15755 /**
15756  * @class Roo.menu.DateItem
15757  * @extends Roo.menu.Adapter
15758  * A menu item that wraps the {@link Roo.DatPicker} component.
15759  * @constructor
15760  * Creates a new DateItem
15761  * @param {Object} config Configuration options
15762  */
15763 Roo.menu.DateItem = function(config){
15764     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15765     /** The Roo.DatePicker object @type Roo.DatePicker */
15766     this.picker = this.component;
15767     this.addEvents({select: true});
15768     
15769     this.picker.on("render", function(picker){
15770         picker.getEl().swallowEvent("click");
15771         picker.container.addClass("x-menu-date-item");
15772     });
15773
15774     this.picker.on("select", this.onSelect, this);
15775 };
15776
15777 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15778     // private
15779     onSelect : function(picker, date){
15780         this.fireEvent("select", this, date, picker);
15781         Roo.menu.DateItem.superclass.handleClick.call(this);
15782     }
15783 });/*
15784  * Based on:
15785  * Ext JS Library 1.1.1
15786  * Copyright(c) 2006-2007, Ext JS, LLC.
15787  *
15788  * Originally Released Under LGPL - original licence link has changed is not relivant.
15789  *
15790  * Fork - LGPL
15791  * <script type="text/javascript">
15792  */
15793  
15794 /**
15795  * @class Roo.menu.ColorItem
15796  * @extends Roo.menu.Adapter
15797  * A menu item that wraps the {@link Roo.ColorPalette} component.
15798  * @constructor
15799  * Creates a new ColorItem
15800  * @param {Object} config Configuration options
15801  */
15802 Roo.menu.ColorItem = function(config){
15803     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15804     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15805     this.palette = this.component;
15806     this.relayEvents(this.palette, ["select"]);
15807     if(this.selectHandler){
15808         this.on('select', this.selectHandler, this.scope);
15809     }
15810 };
15811 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15812  * Based on:
15813  * Ext JS Library 1.1.1
15814  * Copyright(c) 2006-2007, Ext JS, LLC.
15815  *
15816  * Originally Released Under LGPL - original licence link has changed is not relivant.
15817  *
15818  * Fork - LGPL
15819  * <script type="text/javascript">
15820  */
15821  
15822
15823 /**
15824  * @class Roo.menu.DateMenu
15825  * @extends Roo.menu.Menu
15826  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15827  * @constructor
15828  * Creates a new DateMenu
15829  * @param {Object} config Configuration options
15830  */
15831 Roo.menu.DateMenu = function(config){
15832     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15833     this.plain = true;
15834     var di = new Roo.menu.DateItem(config);
15835     this.add(di);
15836     /**
15837      * The {@link Roo.DatePicker} instance for this DateMenu
15838      * @type DatePicker
15839      */
15840     this.picker = di.picker;
15841     /**
15842      * @event select
15843      * @param {DatePicker} picker
15844      * @param {Date} date
15845      */
15846     this.relayEvents(di, ["select"]);
15847     this.on('beforeshow', function(){
15848         if(this.picker){
15849             this.picker.hideMonthPicker(false);
15850         }
15851     }, this);
15852 };
15853 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15854     cls:'x-date-menu'
15855 });/*
15856  * Based on:
15857  * Ext JS Library 1.1.1
15858  * Copyright(c) 2006-2007, Ext JS, LLC.
15859  *
15860  * Originally Released Under LGPL - original licence link has changed is not relivant.
15861  *
15862  * Fork - LGPL
15863  * <script type="text/javascript">
15864  */
15865  
15866
15867 /**
15868  * @class Roo.menu.ColorMenu
15869  * @extends Roo.menu.Menu
15870  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15871  * @constructor
15872  * Creates a new ColorMenu
15873  * @param {Object} config Configuration options
15874  */
15875 Roo.menu.ColorMenu = function(config){
15876     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15877     this.plain = true;
15878     var ci = new Roo.menu.ColorItem(config);
15879     this.add(ci);
15880     /**
15881      * The {@link Roo.ColorPalette} instance for this ColorMenu
15882      * @type ColorPalette
15883      */
15884     this.palette = ci.palette;
15885     /**
15886      * @event select
15887      * @param {ColorPalette} palette
15888      * @param {String} color
15889      */
15890     this.relayEvents(ci, ["select"]);
15891 };
15892 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15893  * Based on:
15894  * Ext JS Library 1.1.1
15895  * Copyright(c) 2006-2007, Ext JS, LLC.
15896  *
15897  * Originally Released Under LGPL - original licence link has changed is not relivant.
15898  *
15899  * Fork - LGPL
15900  * <script type="text/javascript">
15901  */
15902  
15903 /**
15904  * @class Roo.form.TextItem
15905  * @extends Roo.BoxComponent
15906  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15907  * @constructor
15908  * Creates a new TextItem
15909  * @param {Object} config Configuration options
15910  */
15911 Roo.form.TextItem = function(config){
15912     Roo.form.TextItem.superclass.constructor.call(this, config);
15913 };
15914
15915 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15916     
15917     /**
15918      * @cfg {String} tag the tag for this item (default div)
15919      */
15920     tag : 'div',
15921     /**
15922      * @cfg {String} html the content for this item
15923      */
15924     html : '',
15925     
15926     getAutoCreate : function()
15927     {
15928         var cfg = {
15929             id: this.id,
15930             tag: this.tag,
15931             html: this.html,
15932             cls: 'x-form-item'
15933         };
15934         
15935         return cfg;
15936         
15937     },
15938     
15939     onRender : function(ct, position)
15940     {
15941         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15942         
15943         if(!this.el){
15944             var cfg = this.getAutoCreate();
15945             if(!cfg.name){
15946                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15947             }
15948             if (!cfg.name.length) {
15949                 delete cfg.name;
15950             }
15951             this.el = ct.createChild(cfg, position);
15952         }
15953     },
15954     /*
15955      * setHTML
15956      * @param {String} html update the Contents of the element.
15957      */
15958     setHTML : function(html)
15959     {
15960         this.fieldEl.dom.innerHTML = html;
15961     }
15962     
15963 });/*
15964  * Based on:
15965  * Ext JS Library 1.1.1
15966  * Copyright(c) 2006-2007, Ext JS, LLC.
15967  *
15968  * Originally Released Under LGPL - original licence link has changed is not relivant.
15969  *
15970  * Fork - LGPL
15971  * <script type="text/javascript">
15972  */
15973  
15974 /**
15975  * @class Roo.form.Field
15976  * @extends Roo.BoxComponent
15977  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15978  * @constructor
15979  * Creates a new Field
15980  * @param {Object} config Configuration options
15981  */
15982 Roo.form.Field = function(config){
15983     Roo.form.Field.superclass.constructor.call(this, config);
15984 };
15985
15986 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15987     /**
15988      * @cfg {String} fieldLabel Label to use when rendering a form.
15989      */
15990        /**
15991      * @cfg {String} qtip Mouse over tip
15992      */
15993      
15994     /**
15995      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15996      */
15997     invalidClass : "x-form-invalid",
15998     /**
15999      * @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")
16000      */
16001     invalidText : "The value in this field is invalid",
16002     /**
16003      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16004      */
16005     focusClass : "x-form-focus",
16006     /**
16007      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16008       automatic validation (defaults to "keyup").
16009      */
16010     validationEvent : "keyup",
16011     /**
16012      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16013      */
16014     validateOnBlur : true,
16015     /**
16016      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16017      */
16018     validationDelay : 250,
16019     /**
16020      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16021      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16022      */
16023     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16024     /**
16025      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16026      */
16027     fieldClass : "x-form-field",
16028     /**
16029      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16030      *<pre>
16031 Value         Description
16032 -----------   ----------------------------------------------------------------------
16033 qtip          Display a quick tip when the user hovers over the field
16034 title         Display a default browser title attribute popup
16035 under         Add a block div beneath the field containing the error text
16036 side          Add an error icon to the right of the field with a popup on hover
16037 [element id]  Add the error text directly to the innerHTML of the specified element
16038 </pre>
16039      */
16040     msgTarget : 'qtip',
16041     /**
16042      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16043      */
16044     msgFx : 'normal',
16045
16046     /**
16047      * @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.
16048      */
16049     readOnly : false,
16050
16051     /**
16052      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16053      */
16054     disabled : false,
16055
16056     /**
16057      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16058      */
16059     inputType : undefined,
16060     
16061     /**
16062      * @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).
16063          */
16064         tabIndex : undefined,
16065         
16066     // private
16067     isFormField : true,
16068
16069     // private
16070     hasFocus : false,
16071     /**
16072      * @property {Roo.Element} fieldEl
16073      * Element Containing the rendered Field (with label etc.)
16074      */
16075     /**
16076      * @cfg {Mixed} value A value to initialize this field with.
16077      */
16078     value : undefined,
16079
16080     /**
16081      * @cfg {String} name The field's HTML name attribute.
16082      */
16083     /**
16084      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16085      */
16086     // private
16087     loadedValue : false,
16088      
16089      
16090         // private ??
16091         initComponent : function(){
16092         Roo.form.Field.superclass.initComponent.call(this);
16093         this.addEvents({
16094             /**
16095              * @event focus
16096              * Fires when this field receives input focus.
16097              * @param {Roo.form.Field} this
16098              */
16099             focus : true,
16100             /**
16101              * @event blur
16102              * Fires when this field loses input focus.
16103              * @param {Roo.form.Field} this
16104              */
16105             blur : true,
16106             /**
16107              * @event specialkey
16108              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16109              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16110              * @param {Roo.form.Field} this
16111              * @param {Roo.EventObject} e The event object
16112              */
16113             specialkey : true,
16114             /**
16115              * @event change
16116              * Fires just before the field blurs if the field value has changed.
16117              * @param {Roo.form.Field} this
16118              * @param {Mixed} newValue The new value
16119              * @param {Mixed} oldValue The original value
16120              */
16121             change : true,
16122             /**
16123              * @event invalid
16124              * Fires after the field has been marked as invalid.
16125              * @param {Roo.form.Field} this
16126              * @param {String} msg The validation message
16127              */
16128             invalid : true,
16129             /**
16130              * @event valid
16131              * Fires after the field has been validated with no errors.
16132              * @param {Roo.form.Field} this
16133              */
16134             valid : true,
16135              /**
16136              * @event keyup
16137              * Fires after the key up
16138              * @param {Roo.form.Field} this
16139              * @param {Roo.EventObject}  e The event Object
16140              */
16141             keyup : true
16142         });
16143     },
16144
16145     /**
16146      * Returns the name attribute of the field if available
16147      * @return {String} name The field name
16148      */
16149     getName: function(){
16150          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16151     },
16152
16153     // private
16154     onRender : function(ct, position){
16155         Roo.form.Field.superclass.onRender.call(this, ct, position);
16156         if(!this.el){
16157             var cfg = this.getAutoCreate();
16158             if(!cfg.name){
16159                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16160             }
16161             if (!cfg.name.length) {
16162                 delete cfg.name;
16163             }
16164             if(this.inputType){
16165                 cfg.type = this.inputType;
16166             }
16167             this.el = ct.createChild(cfg, position);
16168         }
16169         var type = this.el.dom.type;
16170         if(type){
16171             if(type == 'password'){
16172                 type = 'text';
16173             }
16174             this.el.addClass('x-form-'+type);
16175         }
16176         if(this.readOnly){
16177             this.el.dom.readOnly = true;
16178         }
16179         if(this.tabIndex !== undefined){
16180             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16181         }
16182
16183         this.el.addClass([this.fieldClass, this.cls]);
16184         this.initValue();
16185     },
16186
16187     /**
16188      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16189      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16190      * @return {Roo.form.Field} this
16191      */
16192     applyTo : function(target){
16193         this.allowDomMove = false;
16194         this.el = Roo.get(target);
16195         this.render(this.el.dom.parentNode);
16196         return this;
16197     },
16198
16199     // private
16200     initValue : function(){
16201         if(this.value !== undefined){
16202             this.setValue(this.value);
16203         }else if(this.el.dom.value.length > 0){
16204             this.setValue(this.el.dom.value);
16205         }
16206     },
16207
16208     /**
16209      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16210      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16211      */
16212     isDirty : function() {
16213         if(this.disabled) {
16214             return false;
16215         }
16216         return String(this.getValue()) !== String(this.originalValue);
16217     },
16218
16219     /**
16220      * stores the current value in loadedValue
16221      */
16222     resetHasChanged : function()
16223     {
16224         this.loadedValue = String(this.getValue());
16225     },
16226     /**
16227      * checks the current value against the 'loaded' value.
16228      * Note - will return false if 'resetHasChanged' has not been called first.
16229      */
16230     hasChanged : function()
16231     {
16232         if(this.disabled || this.readOnly) {
16233             return false;
16234         }
16235         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16236     },
16237     
16238     
16239     
16240     // private
16241     afterRender : function(){
16242         Roo.form.Field.superclass.afterRender.call(this);
16243         this.initEvents();
16244     },
16245
16246     // private
16247     fireKey : function(e){
16248         //Roo.log('field ' + e.getKey());
16249         if(e.isNavKeyPress()){
16250             this.fireEvent("specialkey", this, e);
16251         }
16252     },
16253
16254     /**
16255      * Resets the current field value to the originally loaded value and clears any validation messages
16256      */
16257     reset : function(){
16258         this.setValue(this.resetValue);
16259         this.originalValue = this.getValue();
16260         this.clearInvalid();
16261     },
16262
16263     // private
16264     initEvents : function(){
16265         // safari killled keypress - so keydown is now used..
16266         this.el.on("keydown" , this.fireKey,  this);
16267         this.el.on("focus", this.onFocus,  this);
16268         this.el.on("blur", this.onBlur,  this);
16269         this.el.relayEvent('keyup', this);
16270
16271         // reference to original value for reset
16272         this.originalValue = this.getValue();
16273         this.resetValue =  this.getValue();
16274     },
16275
16276     // private
16277     onFocus : function(){
16278         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16279             this.el.addClass(this.focusClass);
16280         }
16281         if(!this.hasFocus){
16282             this.hasFocus = true;
16283             this.startValue = this.getValue();
16284             this.fireEvent("focus", this);
16285         }
16286     },
16287
16288     beforeBlur : Roo.emptyFn,
16289
16290     // private
16291     onBlur : function(){
16292         this.beforeBlur();
16293         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16294             this.el.removeClass(this.focusClass);
16295         }
16296         this.hasFocus = false;
16297         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16298             this.validate();
16299         }
16300         var v = this.getValue();
16301         if(String(v) !== String(this.startValue)){
16302             this.fireEvent('change', this, v, this.startValue);
16303         }
16304         this.fireEvent("blur", this);
16305     },
16306
16307     /**
16308      * Returns whether or not the field value is currently valid
16309      * @param {Boolean} preventMark True to disable marking the field invalid
16310      * @return {Boolean} True if the value is valid, else false
16311      */
16312     isValid : function(preventMark){
16313         if(this.disabled){
16314             return true;
16315         }
16316         var restore = this.preventMark;
16317         this.preventMark = preventMark === true;
16318         var v = this.validateValue(this.processValue(this.getRawValue()));
16319         this.preventMark = restore;
16320         return v;
16321     },
16322
16323     /**
16324      * Validates the field value
16325      * @return {Boolean} True if the value is valid, else false
16326      */
16327     validate : function(){
16328         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16329             this.clearInvalid();
16330             return true;
16331         }
16332         return false;
16333     },
16334
16335     processValue : function(value){
16336         return value;
16337     },
16338
16339     // private
16340     // Subclasses should provide the validation implementation by overriding this
16341     validateValue : function(value){
16342         return true;
16343     },
16344
16345     /**
16346      * Mark this field as invalid
16347      * @param {String} msg The validation message
16348      */
16349     markInvalid : function(msg){
16350         if(!this.rendered || this.preventMark){ // not rendered
16351             return;
16352         }
16353         
16354         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16355         
16356         obj.el.addClass(this.invalidClass);
16357         msg = msg || this.invalidText;
16358         switch(this.msgTarget){
16359             case 'qtip':
16360                 obj.el.dom.qtip = msg;
16361                 obj.el.dom.qclass = 'x-form-invalid-tip';
16362                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16363                     Roo.QuickTips.enable();
16364                 }
16365                 break;
16366             case 'title':
16367                 this.el.dom.title = msg;
16368                 break;
16369             case 'under':
16370                 if(!this.errorEl){
16371                     var elp = this.el.findParent('.x-form-element', 5, true);
16372                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16373                     this.errorEl.setWidth(elp.getWidth(true)-20);
16374                 }
16375                 this.errorEl.update(msg);
16376                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16377                 break;
16378             case 'side':
16379                 if(!this.errorIcon){
16380                     var elp = this.el.findParent('.x-form-element', 5, true);
16381                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16382                 }
16383                 this.alignErrorIcon();
16384                 this.errorIcon.dom.qtip = msg;
16385                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16386                 this.errorIcon.show();
16387                 this.on('resize', this.alignErrorIcon, this);
16388                 break;
16389             default:
16390                 var t = Roo.getDom(this.msgTarget);
16391                 t.innerHTML = msg;
16392                 t.style.display = this.msgDisplay;
16393                 break;
16394         }
16395         this.fireEvent('invalid', this, msg);
16396     },
16397
16398     // private
16399     alignErrorIcon : function(){
16400         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16401     },
16402
16403     /**
16404      * Clear any invalid styles/messages for this field
16405      */
16406     clearInvalid : function(){
16407         if(!this.rendered || this.preventMark){ // not rendered
16408             return;
16409         }
16410         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16411         
16412         obj.el.removeClass(this.invalidClass);
16413         switch(this.msgTarget){
16414             case 'qtip':
16415                 obj.el.dom.qtip = '';
16416                 break;
16417             case 'title':
16418                 this.el.dom.title = '';
16419                 break;
16420             case 'under':
16421                 if(this.errorEl){
16422                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16423                 }
16424                 break;
16425             case 'side':
16426                 if(this.errorIcon){
16427                     this.errorIcon.dom.qtip = '';
16428                     this.errorIcon.hide();
16429                     this.un('resize', this.alignErrorIcon, this);
16430                 }
16431                 break;
16432             default:
16433                 var t = Roo.getDom(this.msgTarget);
16434                 t.innerHTML = '';
16435                 t.style.display = 'none';
16436                 break;
16437         }
16438         this.fireEvent('valid', this);
16439     },
16440
16441     /**
16442      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16443      * @return {Mixed} value The field value
16444      */
16445     getRawValue : function(){
16446         var v = this.el.getValue();
16447         
16448         return v;
16449     },
16450
16451     /**
16452      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16453      * @return {Mixed} value The field value
16454      */
16455     getValue : function(){
16456         var v = this.el.getValue();
16457          
16458         return v;
16459     },
16460
16461     /**
16462      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16463      * @param {Mixed} value The value to set
16464      */
16465     setRawValue : function(v){
16466         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16467     },
16468
16469     /**
16470      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16471      * @param {Mixed} value The value to set
16472      */
16473     setValue : function(v){
16474         this.value = v;
16475         if(this.rendered){
16476             this.el.dom.value = (v === null || v === undefined ? '' : v);
16477              this.validate();
16478         }
16479     },
16480
16481     adjustSize : function(w, h){
16482         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16483         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16484         return s;
16485     },
16486
16487     adjustWidth : function(tag, w){
16488         tag = tag.toLowerCase();
16489         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16490             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16491                 if(tag == 'input'){
16492                     return w + 2;
16493                 }
16494                 if(tag == 'textarea'){
16495                     return w-2;
16496                 }
16497             }else if(Roo.isOpera){
16498                 if(tag == 'input'){
16499                     return w + 2;
16500                 }
16501                 if(tag == 'textarea'){
16502                     return w-2;
16503                 }
16504             }
16505         }
16506         return w;
16507     }
16508 });
16509
16510
16511 // anything other than normal should be considered experimental
16512 Roo.form.Field.msgFx = {
16513     normal : {
16514         show: function(msgEl, f){
16515             msgEl.setDisplayed('block');
16516         },
16517
16518         hide : function(msgEl, f){
16519             msgEl.setDisplayed(false).update('');
16520         }
16521     },
16522
16523     slide : {
16524         show: function(msgEl, f){
16525             msgEl.slideIn('t', {stopFx:true});
16526         },
16527
16528         hide : function(msgEl, f){
16529             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16530         }
16531     },
16532
16533     slideRight : {
16534         show: function(msgEl, f){
16535             msgEl.fixDisplay();
16536             msgEl.alignTo(f.el, 'tl-tr');
16537             msgEl.slideIn('l', {stopFx:true});
16538         },
16539
16540         hide : function(msgEl, f){
16541             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16542         }
16543     }
16544 };/*
16545  * Based on:
16546  * Ext JS Library 1.1.1
16547  * Copyright(c) 2006-2007, Ext JS, LLC.
16548  *
16549  * Originally Released Under LGPL - original licence link has changed is not relivant.
16550  *
16551  * Fork - LGPL
16552  * <script type="text/javascript">
16553  */
16554  
16555
16556 /**
16557  * @class Roo.form.TextField
16558  * @extends Roo.form.Field
16559  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16560  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16561  * @constructor
16562  * Creates a new TextField
16563  * @param {Object} config Configuration options
16564  */
16565 Roo.form.TextField = function(config){
16566     Roo.form.TextField.superclass.constructor.call(this, config);
16567     this.addEvents({
16568         /**
16569          * @event autosize
16570          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16571          * according to the default logic, but this event provides a hook for the developer to apply additional
16572          * logic at runtime to resize the field if needed.
16573              * @param {Roo.form.Field} this This text field
16574              * @param {Number} width The new field width
16575              */
16576         autosize : true
16577     });
16578 };
16579
16580 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16581     /**
16582      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16583      */
16584     grow : false,
16585     /**
16586      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16587      */
16588     growMin : 30,
16589     /**
16590      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16591      */
16592     growMax : 800,
16593     /**
16594      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16595      */
16596     vtype : null,
16597     /**
16598      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16599      */
16600     maskRe : null,
16601     /**
16602      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16603      */
16604     disableKeyFilter : false,
16605     /**
16606      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16607      */
16608     allowBlank : true,
16609     /**
16610      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16611      */
16612     minLength : 0,
16613     /**
16614      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16615      */
16616     maxLength : Number.MAX_VALUE,
16617     /**
16618      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16619      */
16620     minLengthText : "The minimum length for this field is {0}",
16621     /**
16622      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16623      */
16624     maxLengthText : "The maximum length for this field is {0}",
16625     /**
16626      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16627      */
16628     selectOnFocus : false,
16629     /**
16630      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16631      */    
16632     allowLeadingSpace : false,
16633     /**
16634      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16635      */
16636     blankText : "This field is required",
16637     /**
16638      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16639      * If available, this function will be called only after the basic validators all return true, and will be passed the
16640      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16641      */
16642     validator : null,
16643     /**
16644      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16645      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16646      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16647      */
16648     regex : null,
16649     /**
16650      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16651      */
16652     regexText : "",
16653     /**
16654      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16655      */
16656     emptyText : null,
16657    
16658
16659     // private
16660     initEvents : function()
16661     {
16662         if (this.emptyText) {
16663             this.el.attr('placeholder', this.emptyText);
16664         }
16665         
16666         Roo.form.TextField.superclass.initEvents.call(this);
16667         if(this.validationEvent == 'keyup'){
16668             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16669             this.el.on('keyup', this.filterValidation, this);
16670         }
16671         else if(this.validationEvent !== false){
16672             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16673         }
16674         
16675         if(this.selectOnFocus){
16676             this.on("focus", this.preFocus, this);
16677         }
16678         if (!this.allowLeadingSpace) {
16679             this.on('blur', this.cleanLeadingSpace, this);
16680         }
16681         
16682         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16683             this.el.on("keypress", this.filterKeys, this);
16684         }
16685         if(this.grow){
16686             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16687             this.el.on("click", this.autoSize,  this);
16688         }
16689         if(this.el.is('input[type=password]') && Roo.isSafari){
16690             this.el.on('keydown', this.SafariOnKeyDown, this);
16691         }
16692     },
16693
16694     processValue : function(value){
16695         if(this.stripCharsRe){
16696             var newValue = value.replace(this.stripCharsRe, '');
16697             if(newValue !== value){
16698                 this.setRawValue(newValue);
16699                 return newValue;
16700             }
16701         }
16702         return value;
16703     },
16704
16705     filterValidation : function(e){
16706         if(!e.isNavKeyPress()){
16707             this.validationTask.delay(this.validationDelay);
16708         }
16709     },
16710
16711     // private
16712     onKeyUp : function(e){
16713         if(!e.isNavKeyPress()){
16714             this.autoSize();
16715         }
16716     },
16717     // private - clean the leading white space
16718     cleanLeadingSpace : function(e)
16719     {
16720         if ( this.inputType == 'file') {
16721             return;
16722         }
16723         
16724         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16725     },
16726     /**
16727      * Resets the current field value to the originally-loaded value and clears any validation messages.
16728      *  
16729      */
16730     reset : function(){
16731         Roo.form.TextField.superclass.reset.call(this);
16732        
16733     }, 
16734     // private
16735     preFocus : function(){
16736         
16737         if(this.selectOnFocus){
16738             this.el.dom.select();
16739         }
16740     },
16741
16742     
16743     // private
16744     filterKeys : function(e){
16745         var k = e.getKey();
16746         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16747             return;
16748         }
16749         var c = e.getCharCode(), cc = String.fromCharCode(c);
16750         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16751             return;
16752         }
16753         if(!this.maskRe.test(cc)){
16754             e.stopEvent();
16755         }
16756     },
16757
16758     setValue : function(v){
16759         
16760         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16761         
16762         this.autoSize();
16763     },
16764
16765     /**
16766      * Validates a value according to the field's validation rules and marks the field as invalid
16767      * if the validation fails
16768      * @param {Mixed} value The value to validate
16769      * @return {Boolean} True if the value is valid, else false
16770      */
16771     validateValue : function(value){
16772         if(value.length < 1)  { // if it's blank
16773              if(this.allowBlank){
16774                 this.clearInvalid();
16775                 return true;
16776              }else{
16777                 this.markInvalid(this.blankText);
16778                 return false;
16779              }
16780         }
16781         if(value.length < this.minLength){
16782             this.markInvalid(String.format(this.minLengthText, this.minLength));
16783             return false;
16784         }
16785         if(value.length > this.maxLength){
16786             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16787             return false;
16788         }
16789         if(this.vtype){
16790             var vt = Roo.form.VTypes;
16791             if(!vt[this.vtype](value, this)){
16792                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16793                 return false;
16794             }
16795         }
16796         if(typeof this.validator == "function"){
16797             var msg = this.validator(value);
16798             if(msg !== true){
16799                 this.markInvalid(msg);
16800                 return false;
16801             }
16802         }
16803         if(this.regex && !this.regex.test(value)){
16804             this.markInvalid(this.regexText);
16805             return false;
16806         }
16807         return true;
16808     },
16809
16810     /**
16811      * Selects text in this field
16812      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16813      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16814      */
16815     selectText : function(start, end){
16816         var v = this.getRawValue();
16817         if(v.length > 0){
16818             start = start === undefined ? 0 : start;
16819             end = end === undefined ? v.length : end;
16820             var d = this.el.dom;
16821             if(d.setSelectionRange){
16822                 d.setSelectionRange(start, end);
16823             }else if(d.createTextRange){
16824                 var range = d.createTextRange();
16825                 range.moveStart("character", start);
16826                 range.moveEnd("character", v.length-end);
16827                 range.select();
16828             }
16829         }
16830     },
16831
16832     /**
16833      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16834      * This only takes effect if grow = true, and fires the autosize event.
16835      */
16836     autoSize : function(){
16837         if(!this.grow || !this.rendered){
16838             return;
16839         }
16840         if(!this.metrics){
16841             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16842         }
16843         var el = this.el;
16844         var v = el.dom.value;
16845         var d = document.createElement('div');
16846         d.appendChild(document.createTextNode(v));
16847         v = d.innerHTML;
16848         d = null;
16849         v += "&#160;";
16850         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16851         this.el.setWidth(w);
16852         this.fireEvent("autosize", this, w);
16853     },
16854     
16855     // private
16856     SafariOnKeyDown : function(event)
16857     {
16858         // this is a workaround for a password hang bug on chrome/ webkit.
16859         
16860         var isSelectAll = false;
16861         
16862         if(this.el.dom.selectionEnd > 0){
16863             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16864         }
16865         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16866             event.preventDefault();
16867             this.setValue('');
16868             return;
16869         }
16870         
16871         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16872             
16873             event.preventDefault();
16874             // this is very hacky as keydown always get's upper case.
16875             
16876             var cc = String.fromCharCode(event.getCharCode());
16877             
16878             
16879             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16880             
16881         }
16882         
16883         
16884     }
16885 });/*
16886  * Based on:
16887  * Ext JS Library 1.1.1
16888  * Copyright(c) 2006-2007, Ext JS, LLC.
16889  *
16890  * Originally Released Under LGPL - original licence link has changed is not relivant.
16891  *
16892  * Fork - LGPL
16893  * <script type="text/javascript">
16894  */
16895  
16896 /**
16897  * @class Roo.form.Hidden
16898  * @extends Roo.form.TextField
16899  * Simple Hidden element used on forms 
16900  * 
16901  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16902  * 
16903  * @constructor
16904  * Creates a new Hidden form element.
16905  * @param {Object} config Configuration options
16906  */
16907
16908
16909
16910 // easy hidden field...
16911 Roo.form.Hidden = function(config){
16912     Roo.form.Hidden.superclass.constructor.call(this, config);
16913 };
16914   
16915 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16916     fieldLabel:      '',
16917     inputType:      'hidden',
16918     width:          50,
16919     allowBlank:     true,
16920     labelSeparator: '',
16921     hidden:         true,
16922     itemCls :       'x-form-item-display-none'
16923
16924
16925 });
16926
16927
16928 /*
16929  * Based on:
16930  * Ext JS Library 1.1.1
16931  * Copyright(c) 2006-2007, Ext JS, LLC.
16932  *
16933  * Originally Released Under LGPL - original licence link has changed is not relivant.
16934  *
16935  * Fork - LGPL
16936  * <script type="text/javascript">
16937  */
16938  
16939 /**
16940  * @class Roo.form.TriggerField
16941  * @extends Roo.form.TextField
16942  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16943  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16944  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16945  * for which you can provide a custom implementation.  For example:
16946  * <pre><code>
16947 var trigger = new Roo.form.TriggerField();
16948 trigger.onTriggerClick = myTriggerFn;
16949 trigger.applyTo('my-field');
16950 </code></pre>
16951  *
16952  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16953  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16954  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16955  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16956  * @constructor
16957  * Create a new TriggerField.
16958  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16959  * to the base TextField)
16960  */
16961 Roo.form.TriggerField = function(config){
16962     this.mimicing = false;
16963     Roo.form.TriggerField.superclass.constructor.call(this, config);
16964 };
16965
16966 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16967     /**
16968      * @cfg {String} triggerClass A CSS class to apply to the trigger
16969      */
16970     /**
16971      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16972      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16973      */
16974     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16975     /**
16976      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16977      */
16978     hideTrigger:false,
16979
16980     /** @cfg {Boolean} grow @hide */
16981     /** @cfg {Number} growMin @hide */
16982     /** @cfg {Number} growMax @hide */
16983
16984     /**
16985      * @hide 
16986      * @method
16987      */
16988     autoSize: Roo.emptyFn,
16989     // private
16990     monitorTab : true,
16991     // private
16992     deferHeight : true,
16993
16994     
16995     actionMode : 'wrap',
16996     // private
16997     onResize : function(w, h){
16998         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16999         if(typeof w == 'number'){
17000             var x = w - this.trigger.getWidth();
17001             this.el.setWidth(this.adjustWidth('input', x));
17002             this.trigger.setStyle('left', x+'px');
17003         }
17004     },
17005
17006     // private
17007     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17008
17009     // private
17010     getResizeEl : function(){
17011         return this.wrap;
17012     },
17013
17014     // private
17015     getPositionEl : function(){
17016         return this.wrap;
17017     },
17018
17019     // private
17020     alignErrorIcon : function(){
17021         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17022     },
17023
17024     // private
17025     onRender : function(ct, position){
17026         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17027         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17028         this.trigger = this.wrap.createChild(this.triggerConfig ||
17029                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17030         if(this.hideTrigger){
17031             this.trigger.setDisplayed(false);
17032         }
17033         this.initTrigger();
17034         if(!this.width){
17035             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17036         }
17037     },
17038
17039     // private
17040     initTrigger : function(){
17041         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17042         this.trigger.addClassOnOver('x-form-trigger-over');
17043         this.trigger.addClassOnClick('x-form-trigger-click');
17044     },
17045
17046     // private
17047     onDestroy : function(){
17048         if(this.trigger){
17049             this.trigger.removeAllListeners();
17050             this.trigger.remove();
17051         }
17052         if(this.wrap){
17053             this.wrap.remove();
17054         }
17055         Roo.form.TriggerField.superclass.onDestroy.call(this);
17056     },
17057
17058     // private
17059     onFocus : function(){
17060         Roo.form.TriggerField.superclass.onFocus.call(this);
17061         if(!this.mimicing){
17062             this.wrap.addClass('x-trigger-wrap-focus');
17063             this.mimicing = true;
17064             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17065             if(this.monitorTab){
17066                 this.el.on("keydown", this.checkTab, this);
17067             }
17068         }
17069     },
17070
17071     // private
17072     checkTab : function(e){
17073         if(e.getKey() == e.TAB){
17074             this.triggerBlur();
17075         }
17076     },
17077
17078     // private
17079     onBlur : function(){
17080         // do nothing
17081     },
17082
17083     // private
17084     mimicBlur : function(e, t){
17085         if(!this.wrap.contains(t) && this.validateBlur()){
17086             this.triggerBlur();
17087         }
17088     },
17089
17090     // private
17091     triggerBlur : function(){
17092         this.mimicing = false;
17093         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17094         if(this.monitorTab){
17095             this.el.un("keydown", this.checkTab, this);
17096         }
17097         this.wrap.removeClass('x-trigger-wrap-focus');
17098         Roo.form.TriggerField.superclass.onBlur.call(this);
17099     },
17100
17101     // private
17102     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17103     validateBlur : function(e, t){
17104         return true;
17105     },
17106
17107     // private
17108     onDisable : function(){
17109         Roo.form.TriggerField.superclass.onDisable.call(this);
17110         if(this.wrap){
17111             this.wrap.addClass('x-item-disabled');
17112         }
17113     },
17114
17115     // private
17116     onEnable : function(){
17117         Roo.form.TriggerField.superclass.onEnable.call(this);
17118         if(this.wrap){
17119             this.wrap.removeClass('x-item-disabled');
17120         }
17121     },
17122
17123     // private
17124     onShow : function(){
17125         var ae = this.getActionEl();
17126         
17127         if(ae){
17128             ae.dom.style.display = '';
17129             ae.dom.style.visibility = 'visible';
17130         }
17131     },
17132
17133     // private
17134     
17135     onHide : function(){
17136         var ae = this.getActionEl();
17137         ae.dom.style.display = 'none';
17138     },
17139
17140     /**
17141      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17142      * by an implementing function.
17143      * @method
17144      * @param {EventObject} e
17145      */
17146     onTriggerClick : Roo.emptyFn
17147 });
17148
17149 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17150 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17151 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17152 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17153     initComponent : function(){
17154         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17155
17156         this.triggerConfig = {
17157             tag:'span', cls:'x-form-twin-triggers', cn:[
17158             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17159             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17160         ]};
17161     },
17162
17163     getTrigger : function(index){
17164         return this.triggers[index];
17165     },
17166
17167     initTrigger : function(){
17168         var ts = this.trigger.select('.x-form-trigger', true);
17169         this.wrap.setStyle('overflow', 'hidden');
17170         var triggerField = this;
17171         ts.each(function(t, all, index){
17172             t.hide = function(){
17173                 var w = triggerField.wrap.getWidth();
17174                 this.dom.style.display = 'none';
17175                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17176             };
17177             t.show = function(){
17178                 var w = triggerField.wrap.getWidth();
17179                 this.dom.style.display = '';
17180                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17181             };
17182             var triggerIndex = 'Trigger'+(index+1);
17183
17184             if(this['hide'+triggerIndex]){
17185                 t.dom.style.display = 'none';
17186             }
17187             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17188             t.addClassOnOver('x-form-trigger-over');
17189             t.addClassOnClick('x-form-trigger-click');
17190         }, this);
17191         this.triggers = ts.elements;
17192     },
17193
17194     onTrigger1Click : Roo.emptyFn,
17195     onTrigger2Click : Roo.emptyFn
17196 });/*
17197  * Based on:
17198  * Ext JS Library 1.1.1
17199  * Copyright(c) 2006-2007, Ext JS, LLC.
17200  *
17201  * Originally Released Under LGPL - original licence link has changed is not relivant.
17202  *
17203  * Fork - LGPL
17204  * <script type="text/javascript">
17205  */
17206  
17207 /**
17208  * @class Roo.form.TextArea
17209  * @extends Roo.form.TextField
17210  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17211  * support for auto-sizing.
17212  * @constructor
17213  * Creates a new TextArea
17214  * @param {Object} config Configuration options
17215  */
17216 Roo.form.TextArea = function(config){
17217     Roo.form.TextArea.superclass.constructor.call(this, config);
17218     // these are provided exchanges for backwards compat
17219     // minHeight/maxHeight were replaced by growMin/growMax to be
17220     // compatible with TextField growing config values
17221     if(this.minHeight !== undefined){
17222         this.growMin = this.minHeight;
17223     }
17224     if(this.maxHeight !== undefined){
17225         this.growMax = this.maxHeight;
17226     }
17227 };
17228
17229 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17230     /**
17231      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17232      */
17233     growMin : 60,
17234     /**
17235      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17236      */
17237     growMax: 1000,
17238     /**
17239      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17240      * in the field (equivalent to setting overflow: hidden, defaults to false)
17241      */
17242     preventScrollbars: false,
17243     /**
17244      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17245      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17246      */
17247
17248     // private
17249     onRender : function(ct, position){
17250         if(!this.el){
17251             this.defaultAutoCreate = {
17252                 tag: "textarea",
17253                 style:"width:300px;height:60px;",
17254                 autocomplete: "new-password"
17255             };
17256         }
17257         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17258         if(this.grow){
17259             this.textSizeEl = Roo.DomHelper.append(document.body, {
17260                 tag: "pre", cls: "x-form-grow-sizer"
17261             });
17262             if(this.preventScrollbars){
17263                 this.el.setStyle("overflow", "hidden");
17264             }
17265             this.el.setHeight(this.growMin);
17266         }
17267     },
17268
17269     onDestroy : function(){
17270         if(this.textSizeEl){
17271             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17272         }
17273         Roo.form.TextArea.superclass.onDestroy.call(this);
17274     },
17275
17276     // private
17277     onKeyUp : function(e){
17278         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17279             this.autoSize();
17280         }
17281     },
17282
17283     /**
17284      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17285      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17286      */
17287     autoSize : function(){
17288         if(!this.grow || !this.textSizeEl){
17289             return;
17290         }
17291         var el = this.el;
17292         var v = el.dom.value;
17293         var ts = this.textSizeEl;
17294
17295         ts.innerHTML = '';
17296         ts.appendChild(document.createTextNode(v));
17297         v = ts.innerHTML;
17298
17299         Roo.fly(ts).setWidth(this.el.getWidth());
17300         if(v.length < 1){
17301             v = "&#160;&#160;";
17302         }else{
17303             if(Roo.isIE){
17304                 v = v.replace(/\n/g, '<p>&#160;</p>');
17305             }
17306             v += "&#160;\n&#160;";
17307         }
17308         ts.innerHTML = v;
17309         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17310         if(h != this.lastHeight){
17311             this.lastHeight = h;
17312             this.el.setHeight(h);
17313             this.fireEvent("autosize", this, h);
17314         }
17315     }
17316 });/*
17317  * Based on:
17318  * Ext JS Library 1.1.1
17319  * Copyright(c) 2006-2007, Ext JS, LLC.
17320  *
17321  * Originally Released Under LGPL - original licence link has changed is not relivant.
17322  *
17323  * Fork - LGPL
17324  * <script type="text/javascript">
17325  */
17326  
17327
17328 /**
17329  * @class Roo.form.NumberField
17330  * @extends Roo.form.TextField
17331  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17332  * @constructor
17333  * Creates a new NumberField
17334  * @param {Object} config Configuration options
17335  */
17336 Roo.form.NumberField = function(config){
17337     Roo.form.NumberField.superclass.constructor.call(this, config);
17338 };
17339
17340 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17341     /**
17342      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17343      */
17344     fieldClass: "x-form-field x-form-num-field",
17345     /**
17346      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17347      */
17348     allowDecimals : true,
17349     /**
17350      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17351      */
17352     decimalSeparator : ".",
17353     /**
17354      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17355      */
17356     decimalPrecision : 2,
17357     /**
17358      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17359      */
17360     allowNegative : true,
17361     /**
17362      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17363      */
17364     minValue : Number.NEGATIVE_INFINITY,
17365     /**
17366      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17367      */
17368     maxValue : Number.MAX_VALUE,
17369     /**
17370      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17371      */
17372     minText : "The minimum value for this field is {0}",
17373     /**
17374      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17375      */
17376     maxText : "The maximum value for this field is {0}",
17377     /**
17378      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17379      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17380      */
17381     nanText : "{0} is not a valid number",
17382
17383     // private
17384     initEvents : function(){
17385         Roo.form.NumberField.superclass.initEvents.call(this);
17386         var allowed = "0123456789";
17387         if(this.allowDecimals){
17388             allowed += this.decimalSeparator;
17389         }
17390         if(this.allowNegative){
17391             allowed += "-";
17392         }
17393         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17394         var keyPress = function(e){
17395             var k = e.getKey();
17396             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17397                 return;
17398             }
17399             var c = e.getCharCode();
17400             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17401                 e.stopEvent();
17402             }
17403         };
17404         this.el.on("keypress", keyPress, this);
17405     },
17406
17407     // private
17408     validateValue : function(value){
17409         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17410             return false;
17411         }
17412         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17413              return true;
17414         }
17415         var num = this.parseValue(value);
17416         if(isNaN(num)){
17417             this.markInvalid(String.format(this.nanText, value));
17418             return false;
17419         }
17420         if(num < this.minValue){
17421             this.markInvalid(String.format(this.minText, this.minValue));
17422             return false;
17423         }
17424         if(num > this.maxValue){
17425             this.markInvalid(String.format(this.maxText, this.maxValue));
17426             return false;
17427         }
17428         return true;
17429     },
17430
17431     getValue : function(){
17432         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17433     },
17434
17435     // private
17436     parseValue : function(value){
17437         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17438         return isNaN(value) ? '' : value;
17439     },
17440
17441     // private
17442     fixPrecision : function(value){
17443         var nan = isNaN(value);
17444         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17445             return nan ? '' : value;
17446         }
17447         return parseFloat(value).toFixed(this.decimalPrecision);
17448     },
17449
17450     setValue : function(v){
17451         v = this.fixPrecision(v);
17452         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17453     },
17454
17455     // private
17456     decimalPrecisionFcn : function(v){
17457         return Math.floor(v);
17458     },
17459
17460     beforeBlur : function(){
17461         var v = this.parseValue(this.getRawValue());
17462         if(v){
17463             this.setValue(v);
17464         }
17465     }
17466 });/*
17467  * Based on:
17468  * Ext JS Library 1.1.1
17469  * Copyright(c) 2006-2007, Ext JS, LLC.
17470  *
17471  * Originally Released Under LGPL - original licence link has changed is not relivant.
17472  *
17473  * Fork - LGPL
17474  * <script type="text/javascript">
17475  */
17476  
17477 /**
17478  * @class Roo.form.DateField
17479  * @extends Roo.form.TriggerField
17480  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17481 * @constructor
17482 * Create a new DateField
17483 * @param {Object} config
17484  */
17485 Roo.form.DateField = function(config)
17486 {
17487     Roo.form.DateField.superclass.constructor.call(this, config);
17488     
17489       this.addEvents({
17490          
17491         /**
17492          * @event select
17493          * Fires when a date is selected
17494              * @param {Roo.form.DateField} combo This combo box
17495              * @param {Date} date The date selected
17496              */
17497         'select' : true
17498          
17499     });
17500     
17501     
17502     if(typeof this.minValue == "string") {
17503         this.minValue = this.parseDate(this.minValue);
17504     }
17505     if(typeof this.maxValue == "string") {
17506         this.maxValue = this.parseDate(this.maxValue);
17507     }
17508     this.ddMatch = null;
17509     if(this.disabledDates){
17510         var dd = this.disabledDates;
17511         var re = "(?:";
17512         for(var i = 0; i < dd.length; i++){
17513             re += dd[i];
17514             if(i != dd.length-1) {
17515                 re += "|";
17516             }
17517         }
17518         this.ddMatch = new RegExp(re + ")");
17519     }
17520 };
17521
17522 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17523     /**
17524      * @cfg {String} format
17525      * The default date format string which can be overriden for localization support.  The format must be
17526      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17527      */
17528     format : "m/d/y",
17529     /**
17530      * @cfg {String} altFormats
17531      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17532      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17533      */
17534     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17535     /**
17536      * @cfg {Array} disabledDays
17537      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17538      */
17539     disabledDays : null,
17540     /**
17541      * @cfg {String} disabledDaysText
17542      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17543      */
17544     disabledDaysText : "Disabled",
17545     /**
17546      * @cfg {Array} disabledDates
17547      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17548      * expression so they are very powerful. Some examples:
17549      * <ul>
17550      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17551      * <li>["03/08", "09/16"] would disable those days for every year</li>
17552      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17553      * <li>["03/../2006"] would disable every day in March 2006</li>
17554      * <li>["^03"] would disable every day in every March</li>
17555      * </ul>
17556      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17557      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17558      */
17559     disabledDates : null,
17560     /**
17561      * @cfg {String} disabledDatesText
17562      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17563      */
17564     disabledDatesText : "Disabled",
17565     /**
17566      * @cfg {Date/String} minValue
17567      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17568      * valid format (defaults to null).
17569      */
17570     minValue : null,
17571     /**
17572      * @cfg {Date/String} maxValue
17573      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17574      * valid format (defaults to null).
17575      */
17576     maxValue : null,
17577     /**
17578      * @cfg {String} minText
17579      * The error text to display when the date in the cell is before minValue (defaults to
17580      * 'The date in this field must be after {minValue}').
17581      */
17582     minText : "The date in this field must be equal to or after {0}",
17583     /**
17584      * @cfg {String} maxText
17585      * The error text to display when the date in the cell is after maxValue (defaults to
17586      * 'The date in this field must be before {maxValue}').
17587      */
17588     maxText : "The date in this field must be equal to or before {0}",
17589     /**
17590      * @cfg {String} invalidText
17591      * The error text to display when the date in the field is invalid (defaults to
17592      * '{value} is not a valid date - it must be in the format {format}').
17593      */
17594     invalidText : "{0} is not a valid date - it must be in the format {1}",
17595     /**
17596      * @cfg {String} triggerClass
17597      * An additional CSS class used to style the trigger button.  The trigger will always get the
17598      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17599      * which displays a calendar icon).
17600      */
17601     triggerClass : 'x-form-date-trigger',
17602     
17603
17604     /**
17605      * @cfg {Boolean} useIso
17606      * if enabled, then the date field will use a hidden field to store the 
17607      * real value as iso formated date. default (false)
17608      */ 
17609     useIso : false,
17610     /**
17611      * @cfg {String/Object} autoCreate
17612      * A DomHelper element spec, or true for a default element spec (defaults to
17613      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17614      */ 
17615     // private
17616     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17617     
17618     // private
17619     hiddenField: false,
17620     
17621     onRender : function(ct, position)
17622     {
17623         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17624         if (this.useIso) {
17625             //this.el.dom.removeAttribute('name'); 
17626             Roo.log("Changing name?");
17627             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17628             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17629                     'before', true);
17630             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17631             // prevent input submission
17632             this.hiddenName = this.name;
17633         }
17634             
17635             
17636     },
17637     
17638     // private
17639     validateValue : function(value)
17640     {
17641         value = this.formatDate(value);
17642         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17643             Roo.log('super failed');
17644             return false;
17645         }
17646         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17647              return true;
17648         }
17649         var svalue = value;
17650         value = this.parseDate(value);
17651         if(!value){
17652             Roo.log('parse date failed' + svalue);
17653             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17654             return false;
17655         }
17656         var time = value.getTime();
17657         if(this.minValue && time < this.minValue.getTime()){
17658             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17659             return false;
17660         }
17661         if(this.maxValue && time > this.maxValue.getTime()){
17662             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17663             return false;
17664         }
17665         if(this.disabledDays){
17666             var day = value.getDay();
17667             for(var i = 0; i < this.disabledDays.length; i++) {
17668                 if(day === this.disabledDays[i]){
17669                     this.markInvalid(this.disabledDaysText);
17670                     return false;
17671                 }
17672             }
17673         }
17674         var fvalue = this.formatDate(value);
17675         if(this.ddMatch && this.ddMatch.test(fvalue)){
17676             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17677             return false;
17678         }
17679         return true;
17680     },
17681
17682     // private
17683     // Provides logic to override the default TriggerField.validateBlur which just returns true
17684     validateBlur : function(){
17685         return !this.menu || !this.menu.isVisible();
17686     },
17687     
17688     getName: function()
17689     {
17690         // returns hidden if it's set..
17691         if (!this.rendered) {return ''};
17692         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17693         
17694     },
17695
17696     /**
17697      * Returns the current date value of the date field.
17698      * @return {Date} The date value
17699      */
17700     getValue : function(){
17701         
17702         return  this.hiddenField ?
17703                 this.hiddenField.value :
17704                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17705     },
17706
17707     /**
17708      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17709      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17710      * (the default format used is "m/d/y").
17711      * <br />Usage:
17712      * <pre><code>
17713 //All of these calls set the same date value (May 4, 2006)
17714
17715 //Pass a date object:
17716 var dt = new Date('5/4/06');
17717 dateField.setValue(dt);
17718
17719 //Pass a date string (default format):
17720 dateField.setValue('5/4/06');
17721
17722 //Pass a date string (custom format):
17723 dateField.format = 'Y-m-d';
17724 dateField.setValue('2006-5-4');
17725 </code></pre>
17726      * @param {String/Date} date The date or valid date string
17727      */
17728     setValue : function(date){
17729         if (this.hiddenField) {
17730             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17731         }
17732         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17733         // make sure the value field is always stored as a date..
17734         this.value = this.parseDate(date);
17735         
17736         
17737     },
17738
17739     // private
17740     parseDate : function(value){
17741         if(!value || value instanceof Date){
17742             return value;
17743         }
17744         var v = Date.parseDate(value, this.format);
17745          if (!v && this.useIso) {
17746             v = Date.parseDate(value, 'Y-m-d');
17747         }
17748         if(!v && this.altFormats){
17749             if(!this.altFormatsArray){
17750                 this.altFormatsArray = this.altFormats.split("|");
17751             }
17752             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17753                 v = Date.parseDate(value, this.altFormatsArray[i]);
17754             }
17755         }
17756         return v;
17757     },
17758
17759     // private
17760     formatDate : function(date, fmt){
17761         return (!date || !(date instanceof Date)) ?
17762                date : date.dateFormat(fmt || this.format);
17763     },
17764
17765     // private
17766     menuListeners : {
17767         select: function(m, d){
17768             
17769             this.setValue(d);
17770             this.fireEvent('select', this, d);
17771         },
17772         show : function(){ // retain focus styling
17773             this.onFocus();
17774         },
17775         hide : function(){
17776             this.focus.defer(10, this);
17777             var ml = this.menuListeners;
17778             this.menu.un("select", ml.select,  this);
17779             this.menu.un("show", ml.show,  this);
17780             this.menu.un("hide", ml.hide,  this);
17781         }
17782     },
17783
17784     // private
17785     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17786     onTriggerClick : function(){
17787         if(this.disabled){
17788             return;
17789         }
17790         if(this.menu == null){
17791             this.menu = new Roo.menu.DateMenu();
17792         }
17793         Roo.apply(this.menu.picker,  {
17794             showClear: this.allowBlank,
17795             minDate : this.minValue,
17796             maxDate : this.maxValue,
17797             disabledDatesRE : this.ddMatch,
17798             disabledDatesText : this.disabledDatesText,
17799             disabledDays : this.disabledDays,
17800             disabledDaysText : this.disabledDaysText,
17801             format : this.useIso ? 'Y-m-d' : this.format,
17802             minText : String.format(this.minText, this.formatDate(this.minValue)),
17803             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17804         });
17805         this.menu.on(Roo.apply({}, this.menuListeners, {
17806             scope:this
17807         }));
17808         this.menu.picker.setValue(this.getValue() || new Date());
17809         this.menu.show(this.el, "tl-bl?");
17810     },
17811
17812     beforeBlur : function(){
17813         var v = this.parseDate(this.getRawValue());
17814         if(v){
17815             this.setValue(v);
17816         }
17817     },
17818
17819     /*@
17820      * overide
17821      * 
17822      */
17823     isDirty : function() {
17824         if(this.disabled) {
17825             return false;
17826         }
17827         
17828         if(typeof(this.startValue) === 'undefined'){
17829             return false;
17830         }
17831         
17832         return String(this.getValue()) !== String(this.startValue);
17833         
17834     },
17835     // @overide
17836     cleanLeadingSpace : function(e)
17837     {
17838        return;
17839     }
17840     
17841 });/*
17842  * Based on:
17843  * Ext JS Library 1.1.1
17844  * Copyright(c) 2006-2007, Ext JS, LLC.
17845  *
17846  * Originally Released Under LGPL - original licence link has changed is not relivant.
17847  *
17848  * Fork - LGPL
17849  * <script type="text/javascript">
17850  */
17851  
17852 /**
17853  * @class Roo.form.MonthField
17854  * @extends Roo.form.TriggerField
17855  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17856 * @constructor
17857 * Create a new MonthField
17858 * @param {Object} config
17859  */
17860 Roo.form.MonthField = function(config){
17861     
17862     Roo.form.MonthField.superclass.constructor.call(this, config);
17863     
17864       this.addEvents({
17865          
17866         /**
17867          * @event select
17868          * Fires when a date is selected
17869              * @param {Roo.form.MonthFieeld} combo This combo box
17870              * @param {Date} date The date selected
17871              */
17872         'select' : true
17873          
17874     });
17875     
17876     
17877     if(typeof this.minValue == "string") {
17878         this.minValue = this.parseDate(this.minValue);
17879     }
17880     if(typeof this.maxValue == "string") {
17881         this.maxValue = this.parseDate(this.maxValue);
17882     }
17883     this.ddMatch = null;
17884     if(this.disabledDates){
17885         var dd = this.disabledDates;
17886         var re = "(?:";
17887         for(var i = 0; i < dd.length; i++){
17888             re += dd[i];
17889             if(i != dd.length-1) {
17890                 re += "|";
17891             }
17892         }
17893         this.ddMatch = new RegExp(re + ")");
17894     }
17895 };
17896
17897 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17898     /**
17899      * @cfg {String} format
17900      * The default date format string which can be overriden for localization support.  The format must be
17901      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17902      */
17903     format : "M Y",
17904     /**
17905      * @cfg {String} altFormats
17906      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17907      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17908      */
17909     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17910     /**
17911      * @cfg {Array} disabledDays
17912      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17913      */
17914     disabledDays : [0,1,2,3,4,5,6],
17915     /**
17916      * @cfg {String} disabledDaysText
17917      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17918      */
17919     disabledDaysText : "Disabled",
17920     /**
17921      * @cfg {Array} disabledDates
17922      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17923      * expression so they are very powerful. Some examples:
17924      * <ul>
17925      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17926      * <li>["03/08", "09/16"] would disable those days for every year</li>
17927      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17928      * <li>["03/../2006"] would disable every day in March 2006</li>
17929      * <li>["^03"] would disable every day in every March</li>
17930      * </ul>
17931      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17932      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17933      */
17934     disabledDates : null,
17935     /**
17936      * @cfg {String} disabledDatesText
17937      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17938      */
17939     disabledDatesText : "Disabled",
17940     /**
17941      * @cfg {Date/String} minValue
17942      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17943      * valid format (defaults to null).
17944      */
17945     minValue : null,
17946     /**
17947      * @cfg {Date/String} maxValue
17948      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17949      * valid format (defaults to null).
17950      */
17951     maxValue : null,
17952     /**
17953      * @cfg {String} minText
17954      * The error text to display when the date in the cell is before minValue (defaults to
17955      * 'The date in this field must be after {minValue}').
17956      */
17957     minText : "The date in this field must be equal to or after {0}",
17958     /**
17959      * @cfg {String} maxTextf
17960      * The error text to display when the date in the cell is after maxValue (defaults to
17961      * 'The date in this field must be before {maxValue}').
17962      */
17963     maxText : "The date in this field must be equal to or before {0}",
17964     /**
17965      * @cfg {String} invalidText
17966      * The error text to display when the date in the field is invalid (defaults to
17967      * '{value} is not a valid date - it must be in the format {format}').
17968      */
17969     invalidText : "{0} is not a valid date - it must be in the format {1}",
17970     /**
17971      * @cfg {String} triggerClass
17972      * An additional CSS class used to style the trigger button.  The trigger will always get the
17973      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17974      * which displays a calendar icon).
17975      */
17976     triggerClass : 'x-form-date-trigger',
17977     
17978
17979     /**
17980      * @cfg {Boolean} useIso
17981      * if enabled, then the date field will use a hidden field to store the 
17982      * real value as iso formated date. default (true)
17983      */ 
17984     useIso : true,
17985     /**
17986      * @cfg {String/Object} autoCreate
17987      * A DomHelper element spec, or true for a default element spec (defaults to
17988      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17989      */ 
17990     // private
17991     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17992     
17993     // private
17994     hiddenField: false,
17995     
17996     hideMonthPicker : false,
17997     
17998     onRender : function(ct, position)
17999     {
18000         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18001         if (this.useIso) {
18002             this.el.dom.removeAttribute('name'); 
18003             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18004                     'before', true);
18005             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18006             // prevent input submission
18007             this.hiddenName = this.name;
18008         }
18009             
18010             
18011     },
18012     
18013     // private
18014     validateValue : function(value)
18015     {
18016         value = this.formatDate(value);
18017         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18018             return false;
18019         }
18020         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18021              return true;
18022         }
18023         var svalue = value;
18024         value = this.parseDate(value);
18025         if(!value){
18026             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18027             return false;
18028         }
18029         var time = value.getTime();
18030         if(this.minValue && time < this.minValue.getTime()){
18031             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18032             return false;
18033         }
18034         if(this.maxValue && time > this.maxValue.getTime()){
18035             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18036             return false;
18037         }
18038         /*if(this.disabledDays){
18039             var day = value.getDay();
18040             for(var i = 0; i < this.disabledDays.length; i++) {
18041                 if(day === this.disabledDays[i]){
18042                     this.markInvalid(this.disabledDaysText);
18043                     return false;
18044                 }
18045             }
18046         }
18047         */
18048         var fvalue = this.formatDate(value);
18049         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18050             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18051             return false;
18052         }
18053         */
18054         return true;
18055     },
18056
18057     // private
18058     // Provides logic to override the default TriggerField.validateBlur which just returns true
18059     validateBlur : function(){
18060         return !this.menu || !this.menu.isVisible();
18061     },
18062
18063     /**
18064      * Returns the current date value of the date field.
18065      * @return {Date} The date value
18066      */
18067     getValue : function(){
18068         
18069         
18070         
18071         return  this.hiddenField ?
18072                 this.hiddenField.value :
18073                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18074     },
18075
18076     /**
18077      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18078      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18079      * (the default format used is "m/d/y").
18080      * <br />Usage:
18081      * <pre><code>
18082 //All of these calls set the same date value (May 4, 2006)
18083
18084 //Pass a date object:
18085 var dt = new Date('5/4/06');
18086 monthField.setValue(dt);
18087
18088 //Pass a date string (default format):
18089 monthField.setValue('5/4/06');
18090
18091 //Pass a date string (custom format):
18092 monthField.format = 'Y-m-d';
18093 monthField.setValue('2006-5-4');
18094 </code></pre>
18095      * @param {String/Date} date The date or valid date string
18096      */
18097     setValue : function(date){
18098         Roo.log('month setValue' + date);
18099         // can only be first of month..
18100         
18101         var val = this.parseDate(date);
18102         
18103         if (this.hiddenField) {
18104             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18105         }
18106         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18107         this.value = this.parseDate(date);
18108     },
18109
18110     // private
18111     parseDate : function(value){
18112         if(!value || value instanceof Date){
18113             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18114             return value;
18115         }
18116         var v = Date.parseDate(value, this.format);
18117         if (!v && this.useIso) {
18118             v = Date.parseDate(value, 'Y-m-d');
18119         }
18120         if (v) {
18121             // 
18122             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18123         }
18124         
18125         
18126         if(!v && this.altFormats){
18127             if(!this.altFormatsArray){
18128                 this.altFormatsArray = this.altFormats.split("|");
18129             }
18130             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18131                 v = Date.parseDate(value, this.altFormatsArray[i]);
18132             }
18133         }
18134         return v;
18135     },
18136
18137     // private
18138     formatDate : function(date, fmt){
18139         return (!date || !(date instanceof Date)) ?
18140                date : date.dateFormat(fmt || this.format);
18141     },
18142
18143     // private
18144     menuListeners : {
18145         select: function(m, d){
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     // private
18161     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18162     onTriggerClick : function(){
18163         if(this.disabled){
18164             return;
18165         }
18166         if(this.menu == null){
18167             this.menu = new Roo.menu.DateMenu();
18168            
18169         }
18170         
18171         Roo.apply(this.menu.picker,  {
18172             
18173             showClear: this.allowBlank,
18174             minDate : this.minValue,
18175             maxDate : this.maxValue,
18176             disabledDatesRE : this.ddMatch,
18177             disabledDatesText : this.disabledDatesText,
18178             
18179             format : this.useIso ? 'Y-m-d' : this.format,
18180             minText : String.format(this.minText, this.formatDate(this.minValue)),
18181             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18182             
18183         });
18184          this.menu.on(Roo.apply({}, this.menuListeners, {
18185             scope:this
18186         }));
18187        
18188         
18189         var m = this.menu;
18190         var p = m.picker;
18191         
18192         // hide month picker get's called when we called by 'before hide';
18193         
18194         var ignorehide = true;
18195         p.hideMonthPicker  = function(disableAnim){
18196             if (ignorehide) {
18197                 return;
18198             }
18199              if(this.monthPicker){
18200                 Roo.log("hideMonthPicker called");
18201                 if(disableAnim === true){
18202                     this.monthPicker.hide();
18203                 }else{
18204                     this.monthPicker.slideOut('t', {duration:.2});
18205                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18206                     p.fireEvent("select", this, this.value);
18207                     m.hide();
18208                 }
18209             }
18210         }
18211         
18212         Roo.log('picker set value');
18213         Roo.log(this.getValue());
18214         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18215         m.show(this.el, 'tl-bl?');
18216         ignorehide  = false;
18217         // this will trigger hideMonthPicker..
18218         
18219         
18220         // hidden the day picker
18221         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18222         
18223         
18224         
18225       
18226         
18227         p.showMonthPicker.defer(100, p);
18228     
18229         
18230        
18231     },
18232
18233     beforeBlur : function(){
18234         var v = this.parseDate(this.getRawValue());
18235         if(v){
18236             this.setValue(v);
18237         }
18238     }
18239
18240     /** @cfg {Boolean} grow @hide */
18241     /** @cfg {Number} growMin @hide */
18242     /** @cfg {Number} growMax @hide */
18243     /**
18244      * @hide
18245      * @method autoSize
18246      */
18247 });/*
18248  * Based on:
18249  * Ext JS Library 1.1.1
18250  * Copyright(c) 2006-2007, Ext JS, LLC.
18251  *
18252  * Originally Released Under LGPL - original licence link has changed is not relivant.
18253  *
18254  * Fork - LGPL
18255  * <script type="text/javascript">
18256  */
18257  
18258
18259 /**
18260  * @class Roo.form.ComboBox
18261  * @extends Roo.form.TriggerField
18262  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18263  * @constructor
18264  * Create a new ComboBox.
18265  * @param {Object} config Configuration options
18266  */
18267 Roo.form.ComboBox = function(config){
18268     Roo.form.ComboBox.superclass.constructor.call(this, config);
18269     this.addEvents({
18270         /**
18271          * @event expand
18272          * Fires when the dropdown list is expanded
18273              * @param {Roo.form.ComboBox} combo This combo box
18274              */
18275         'expand' : true,
18276         /**
18277          * @event collapse
18278          * Fires when the dropdown list is collapsed
18279              * @param {Roo.form.ComboBox} combo This combo box
18280              */
18281         'collapse' : true,
18282         /**
18283          * @event beforeselect
18284          * Fires before a list item is selected. Return false to cancel the selection.
18285              * @param {Roo.form.ComboBox} combo This combo box
18286              * @param {Roo.data.Record} record The data record returned from the underlying store
18287              * @param {Number} index The index of the selected item in the dropdown list
18288              */
18289         'beforeselect' : true,
18290         /**
18291          * @event select
18292          * Fires when a list item is selected
18293              * @param {Roo.form.ComboBox} combo This combo box
18294              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18295              * @param {Number} index The index of the selected item in the dropdown list
18296              */
18297         'select' : true,
18298         /**
18299          * @event beforequery
18300          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18301          * The event object passed has these properties:
18302              * @param {Roo.form.ComboBox} combo This combo box
18303              * @param {String} query The query
18304              * @param {Boolean} forceAll true to force "all" query
18305              * @param {Boolean} cancel true to cancel the query
18306              * @param {Object} e The query event object
18307              */
18308         'beforequery': true,
18309          /**
18310          * @event add
18311          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18312              * @param {Roo.form.ComboBox} combo This combo box
18313              */
18314         'add' : true,
18315         /**
18316          * @event edit
18317          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18318              * @param {Roo.form.ComboBox} combo This combo box
18319              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18320              */
18321         'edit' : true
18322         
18323         
18324     });
18325     if(this.transform){
18326         this.allowDomMove = false;
18327         var s = Roo.getDom(this.transform);
18328         if(!this.hiddenName){
18329             this.hiddenName = s.name;
18330         }
18331         if(!this.store){
18332             this.mode = 'local';
18333             var d = [], opts = s.options;
18334             for(var i = 0, len = opts.length;i < len; i++){
18335                 var o = opts[i];
18336                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18337                 if(o.selected) {
18338                     this.value = value;
18339                 }
18340                 d.push([value, o.text]);
18341             }
18342             this.store = new Roo.data.SimpleStore({
18343                 'id': 0,
18344                 fields: ['value', 'text'],
18345                 data : d
18346             });
18347             this.valueField = 'value';
18348             this.displayField = 'text';
18349         }
18350         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18351         if(!this.lazyRender){
18352             this.target = true;
18353             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18354             s.parentNode.removeChild(s); // remove it
18355             this.render(this.el.parentNode);
18356         }else{
18357             s.parentNode.removeChild(s); // remove it
18358         }
18359
18360     }
18361     if (this.store) {
18362         this.store = Roo.factory(this.store, Roo.data);
18363     }
18364     
18365     this.selectedIndex = -1;
18366     if(this.mode == 'local'){
18367         if(config.queryDelay === undefined){
18368             this.queryDelay = 10;
18369         }
18370         if(config.minChars === undefined){
18371             this.minChars = 0;
18372         }
18373     }
18374 };
18375
18376 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18377     /**
18378      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18379      */
18380     /**
18381      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18382      * rendering into an Roo.Editor, defaults to false)
18383      */
18384     /**
18385      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18386      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18387      */
18388     /**
18389      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18390      */
18391     /**
18392      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18393      * the dropdown list (defaults to undefined, with no header element)
18394      */
18395
18396      /**
18397      * @cfg {String/Roo.Template} tpl The template to use to render the output
18398      */
18399      
18400     // private
18401     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18402     /**
18403      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18404      */
18405     listWidth: undefined,
18406     /**
18407      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18408      * mode = 'remote' or 'text' if mode = 'local')
18409      */
18410     displayField: undefined,
18411     /**
18412      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18413      * mode = 'remote' or 'value' if mode = 'local'). 
18414      * Note: use of a valueField requires the user make a selection
18415      * in order for a value to be mapped.
18416      */
18417     valueField: undefined,
18418     
18419     
18420     /**
18421      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18422      * field's data value (defaults to the underlying DOM element's name)
18423      */
18424     hiddenName: undefined,
18425     /**
18426      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18427      */
18428     listClass: '',
18429     /**
18430      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18431      */
18432     selectedClass: 'x-combo-selected',
18433     /**
18434      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18435      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18436      * which displays a downward arrow icon).
18437      */
18438     triggerClass : 'x-form-arrow-trigger',
18439     /**
18440      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18441      */
18442     shadow:'sides',
18443     /**
18444      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18445      * anchor positions (defaults to 'tl-bl')
18446      */
18447     listAlign: 'tl-bl?',
18448     /**
18449      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18450      */
18451     maxHeight: 300,
18452     /**
18453      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18454      * query specified by the allQuery config option (defaults to 'query')
18455      */
18456     triggerAction: 'query',
18457     /**
18458      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18459      * (defaults to 4, does not apply if editable = false)
18460      */
18461     minChars : 4,
18462     /**
18463      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18464      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18465      */
18466     typeAhead: false,
18467     /**
18468      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18469      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18470      */
18471     queryDelay: 500,
18472     /**
18473      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18474      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18475      */
18476     pageSize: 0,
18477     /**
18478      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18479      * when editable = true (defaults to false)
18480      */
18481     selectOnFocus:false,
18482     /**
18483      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18484      */
18485     queryParam: 'query',
18486     /**
18487      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18488      * when mode = 'remote' (defaults to 'Loading...')
18489      */
18490     loadingText: 'Loading...',
18491     /**
18492      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18493      */
18494     resizable: false,
18495     /**
18496      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18497      */
18498     handleHeight : 8,
18499     /**
18500      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18501      * traditional select (defaults to true)
18502      */
18503     editable: true,
18504     /**
18505      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18506      */
18507     allQuery: '',
18508     /**
18509      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18510      */
18511     mode: 'remote',
18512     /**
18513      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18514      * listWidth has a higher value)
18515      */
18516     minListWidth : 70,
18517     /**
18518      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18519      * allow the user to set arbitrary text into the field (defaults to false)
18520      */
18521     forceSelection:false,
18522     /**
18523      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18524      * if typeAhead = true (defaults to 250)
18525      */
18526     typeAheadDelay : 250,
18527     /**
18528      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18529      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18530      */
18531     valueNotFoundText : undefined,
18532     /**
18533      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18534      */
18535     blockFocus : false,
18536     
18537     /**
18538      * @cfg {Boolean} disableClear Disable showing of clear button.
18539      */
18540     disableClear : false,
18541     /**
18542      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18543      */
18544     alwaysQuery : false,
18545     
18546     //private
18547     addicon : false,
18548     editicon: false,
18549     
18550     // element that contains real text value.. (when hidden is used..)
18551      
18552     // private
18553     onRender : function(ct, position)
18554     {
18555         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18556         
18557         if(this.hiddenName){
18558             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18559                     'before', true);
18560             this.hiddenField.value =
18561                 this.hiddenValue !== undefined ? this.hiddenValue :
18562                 this.value !== undefined ? this.value : '';
18563
18564             // prevent input submission
18565             this.el.dom.removeAttribute('name');
18566              
18567              
18568         }
18569         
18570         if(Roo.isGecko){
18571             this.el.dom.setAttribute('autocomplete', 'off');
18572         }
18573
18574         var cls = 'x-combo-list';
18575
18576         this.list = new Roo.Layer({
18577             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18578         });
18579
18580         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18581         this.list.setWidth(lw);
18582         this.list.swallowEvent('mousewheel');
18583         this.assetHeight = 0;
18584
18585         if(this.title){
18586             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18587             this.assetHeight += this.header.getHeight();
18588         }
18589
18590         this.innerList = this.list.createChild({cls:cls+'-inner'});
18591         this.innerList.on('mouseover', this.onViewOver, this);
18592         this.innerList.on('mousemove', this.onViewMove, this);
18593         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18594         
18595         if(this.allowBlank && !this.pageSize && !this.disableClear){
18596             this.footer = this.list.createChild({cls:cls+'-ft'});
18597             this.pageTb = new Roo.Toolbar(this.footer);
18598            
18599         }
18600         if(this.pageSize){
18601             this.footer = this.list.createChild({cls:cls+'-ft'});
18602             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18603                     {pageSize: this.pageSize});
18604             
18605         }
18606         
18607         if (this.pageTb && this.allowBlank && !this.disableClear) {
18608             var _this = this;
18609             this.pageTb.add(new Roo.Toolbar.Fill(), {
18610                 cls: 'x-btn-icon x-btn-clear',
18611                 text: '&#160;',
18612                 handler: function()
18613                 {
18614                     _this.collapse();
18615                     _this.clearValue();
18616                     _this.onSelect(false, -1);
18617                 }
18618             });
18619         }
18620         if (this.footer) {
18621             this.assetHeight += this.footer.getHeight();
18622         }
18623         
18624
18625         if(!this.tpl){
18626             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18627         }
18628
18629         this.view = new Roo.View(this.innerList, this.tpl, {
18630             singleSelect:true,
18631             store: this.store,
18632             selectedClass: this.selectedClass
18633         });
18634
18635         this.view.on('click', this.onViewClick, this);
18636
18637         this.store.on('beforeload', this.onBeforeLoad, this);
18638         this.store.on('load', this.onLoad, this);
18639         this.store.on('loadexception', this.onLoadException, this);
18640
18641         if(this.resizable){
18642             this.resizer = new Roo.Resizable(this.list,  {
18643                pinned:true, handles:'se'
18644             });
18645             this.resizer.on('resize', function(r, w, h){
18646                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18647                 this.listWidth = w;
18648                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18649                 this.restrictHeight();
18650             }, this);
18651             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18652         }
18653         if(!this.editable){
18654             this.editable = true;
18655             this.setEditable(false);
18656         }  
18657         
18658         
18659         if (typeof(this.events.add.listeners) != 'undefined') {
18660             
18661             this.addicon = this.wrap.createChild(
18662                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18663        
18664             this.addicon.on('click', function(e) {
18665                 this.fireEvent('add', this);
18666             }, this);
18667         }
18668         if (typeof(this.events.edit.listeners) != 'undefined') {
18669             
18670             this.editicon = this.wrap.createChild(
18671                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18672             if (this.addicon) {
18673                 this.editicon.setStyle('margin-left', '40px');
18674             }
18675             this.editicon.on('click', function(e) {
18676                 
18677                 // we fire even  if inothing is selected..
18678                 this.fireEvent('edit', this, this.lastData );
18679                 
18680             }, this);
18681         }
18682         
18683         
18684         
18685     },
18686
18687     // private
18688     initEvents : function(){
18689         Roo.form.ComboBox.superclass.initEvents.call(this);
18690
18691         this.keyNav = new Roo.KeyNav(this.el, {
18692             "up" : function(e){
18693                 this.inKeyMode = true;
18694                 this.selectPrev();
18695             },
18696
18697             "down" : function(e){
18698                 if(!this.isExpanded()){
18699                     this.onTriggerClick();
18700                 }else{
18701                     this.inKeyMode = true;
18702                     this.selectNext();
18703                 }
18704             },
18705
18706             "enter" : function(e){
18707                 this.onViewClick();
18708                 //return true;
18709             },
18710
18711             "esc" : function(e){
18712                 this.collapse();
18713             },
18714
18715             "tab" : function(e){
18716                 this.onViewClick(false);
18717                 this.fireEvent("specialkey", this, e);
18718                 return true;
18719             },
18720
18721             scope : this,
18722
18723             doRelay : function(foo, bar, hname){
18724                 if(hname == 'down' || this.scope.isExpanded()){
18725                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18726                 }
18727                 return true;
18728             },
18729
18730             forceKeyDown: true
18731         });
18732         this.queryDelay = Math.max(this.queryDelay || 10,
18733                 this.mode == 'local' ? 10 : 250);
18734         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18735         if(this.typeAhead){
18736             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18737         }
18738         if(this.editable !== false){
18739             this.el.on("keyup", this.onKeyUp, this);
18740         }
18741         if(this.forceSelection){
18742             this.on('blur', this.doForce, this);
18743         }
18744     },
18745
18746     onDestroy : function(){
18747         if(this.view){
18748             this.view.setStore(null);
18749             this.view.el.removeAllListeners();
18750             this.view.el.remove();
18751             this.view.purgeListeners();
18752         }
18753         if(this.list){
18754             this.list.destroy();
18755         }
18756         if(this.store){
18757             this.store.un('beforeload', this.onBeforeLoad, this);
18758             this.store.un('load', this.onLoad, this);
18759             this.store.un('loadexception', this.onLoadException, this);
18760         }
18761         Roo.form.ComboBox.superclass.onDestroy.call(this);
18762     },
18763
18764     // private
18765     fireKey : function(e){
18766         if(e.isNavKeyPress() && !this.list.isVisible()){
18767             this.fireEvent("specialkey", this, e);
18768         }
18769     },
18770
18771     // private
18772     onResize: function(w, h){
18773         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18774         
18775         if(typeof w != 'number'){
18776             // we do not handle it!?!?
18777             return;
18778         }
18779         var tw = this.trigger.getWidth();
18780         tw += this.addicon ? this.addicon.getWidth() : 0;
18781         tw += this.editicon ? this.editicon.getWidth() : 0;
18782         var x = w - tw;
18783         this.el.setWidth( this.adjustWidth('input', x));
18784             
18785         this.trigger.setStyle('left', x+'px');
18786         
18787         if(this.list && this.listWidth === undefined){
18788             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18789             this.list.setWidth(lw);
18790             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18791         }
18792         
18793     
18794         
18795     },
18796
18797     /**
18798      * Allow or prevent the user from directly editing the field text.  If false is passed,
18799      * the user will only be able to select from the items defined in the dropdown list.  This method
18800      * is the runtime equivalent of setting the 'editable' config option at config time.
18801      * @param {Boolean} value True to allow the user to directly edit the field text
18802      */
18803     setEditable : function(value){
18804         if(value == this.editable){
18805             return;
18806         }
18807         this.editable = value;
18808         if(!value){
18809             this.el.dom.setAttribute('readOnly', true);
18810             this.el.on('mousedown', this.onTriggerClick,  this);
18811             this.el.addClass('x-combo-noedit');
18812         }else{
18813             this.el.dom.setAttribute('readOnly', false);
18814             this.el.un('mousedown', this.onTriggerClick,  this);
18815             this.el.removeClass('x-combo-noedit');
18816         }
18817     },
18818
18819     // private
18820     onBeforeLoad : function(){
18821         if(!this.hasFocus){
18822             return;
18823         }
18824         this.innerList.update(this.loadingText ?
18825                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18826         this.restrictHeight();
18827         this.selectedIndex = -1;
18828     },
18829
18830     // private
18831     onLoad : function(){
18832         if(!this.hasFocus){
18833             return;
18834         }
18835         if(this.store.getCount() > 0){
18836             this.expand();
18837             this.restrictHeight();
18838             if(this.lastQuery == this.allQuery){
18839                 if(this.editable){
18840                     this.el.dom.select();
18841                 }
18842                 if(!this.selectByValue(this.value, true)){
18843                     this.select(0, true);
18844                 }
18845             }else{
18846                 this.selectNext();
18847                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18848                     this.taTask.delay(this.typeAheadDelay);
18849                 }
18850             }
18851         }else{
18852             this.onEmptyResults();
18853         }
18854         //this.el.focus();
18855     },
18856     // private
18857     onLoadException : function()
18858     {
18859         this.collapse();
18860         Roo.log(this.store.reader.jsonData);
18861         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18862             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18863         }
18864         
18865         
18866     },
18867     // private
18868     onTypeAhead : function(){
18869         if(this.store.getCount() > 0){
18870             var r = this.store.getAt(0);
18871             var newValue = r.data[this.displayField];
18872             var len = newValue.length;
18873             var selStart = this.getRawValue().length;
18874             if(selStart != len){
18875                 this.setRawValue(newValue);
18876                 this.selectText(selStart, newValue.length);
18877             }
18878         }
18879     },
18880
18881     // private
18882     onSelect : function(record, index){
18883         if(this.fireEvent('beforeselect', this, record, index) !== false){
18884             this.setFromData(index > -1 ? record.data : false);
18885             this.collapse();
18886             this.fireEvent('select', this, record, index);
18887         }
18888     },
18889
18890     /**
18891      * Returns the currently selected field value or empty string if no value is set.
18892      * @return {String} value The selected value
18893      */
18894     getValue : function(){
18895         if(this.valueField){
18896             return typeof this.value != 'undefined' ? this.value : '';
18897         }
18898         return Roo.form.ComboBox.superclass.getValue.call(this);
18899     },
18900
18901     /**
18902      * Clears any text/value currently set in the field
18903      */
18904     clearValue : function(){
18905         if(this.hiddenField){
18906             this.hiddenField.value = '';
18907         }
18908         this.value = '';
18909         this.setRawValue('');
18910         this.lastSelectionText = '';
18911         
18912     },
18913
18914     /**
18915      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18916      * will be displayed in the field.  If the value does not match the data value of an existing item,
18917      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18918      * Otherwise the field will be blank (although the value will still be set).
18919      * @param {String} value The value to match
18920      */
18921     setValue : function(v){
18922         var text = v;
18923         if(this.valueField){
18924             var r = this.findRecord(this.valueField, v);
18925             if(r){
18926                 text = r.data[this.displayField];
18927             }else if(this.valueNotFoundText !== undefined){
18928                 text = this.valueNotFoundText;
18929             }
18930         }
18931         this.lastSelectionText = text;
18932         if(this.hiddenField){
18933             this.hiddenField.value = v;
18934         }
18935         Roo.form.ComboBox.superclass.setValue.call(this, text);
18936         this.value = v;
18937     },
18938     /**
18939      * @property {Object} the last set data for the element
18940      */
18941     
18942     lastData : false,
18943     /**
18944      * Sets the value of the field based on a object which is related to the record format for the store.
18945      * @param {Object} value the value to set as. or false on reset?
18946      */
18947     setFromData : function(o){
18948         var dv = ''; // display value
18949         var vv = ''; // value value..
18950         this.lastData = o;
18951         if (this.displayField) {
18952             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18953         } else {
18954             // this is an error condition!!!
18955             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18956         }
18957         
18958         if(this.valueField){
18959             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18960         }
18961         if(this.hiddenField){
18962             this.hiddenField.value = vv;
18963             
18964             this.lastSelectionText = dv;
18965             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18966             this.value = vv;
18967             return;
18968         }
18969         // no hidden field.. - we store the value in 'value', but still display
18970         // display field!!!!
18971         this.lastSelectionText = dv;
18972         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18973         this.value = vv;
18974         
18975         
18976     },
18977     // private
18978     reset : function(){
18979         // overridden so that last data is reset..
18980         this.setValue(this.resetValue);
18981         this.originalValue = this.getValue();
18982         this.clearInvalid();
18983         this.lastData = false;
18984         if (this.view) {
18985             this.view.clearSelections();
18986         }
18987     },
18988     // private
18989     findRecord : function(prop, value){
18990         var record;
18991         if(this.store.getCount() > 0){
18992             this.store.each(function(r){
18993                 if(r.data[prop] == value){
18994                     record = r;
18995                     return false;
18996                 }
18997                 return true;
18998             });
18999         }
19000         return record;
19001     },
19002     
19003     getName: function()
19004     {
19005         // returns hidden if it's set..
19006         if (!this.rendered) {return ''};
19007         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19008         
19009     },
19010     // private
19011     onViewMove : function(e, t){
19012         this.inKeyMode = false;
19013     },
19014
19015     // private
19016     onViewOver : function(e, t){
19017         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19018             return;
19019         }
19020         var item = this.view.findItemFromChild(t);
19021         if(item){
19022             var index = this.view.indexOf(item);
19023             this.select(index, false);
19024         }
19025     },
19026
19027     // private
19028     onViewClick : function(doFocus)
19029     {
19030         var index = this.view.getSelectedIndexes()[0];
19031         var r = this.store.getAt(index);
19032         if(r){
19033             this.onSelect(r, index);
19034         }
19035         if(doFocus !== false && !this.blockFocus){
19036             this.el.focus();
19037         }
19038     },
19039
19040     // private
19041     restrictHeight : function(){
19042         this.innerList.dom.style.height = '';
19043         var inner = this.innerList.dom;
19044         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19045         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19046         this.list.beginUpdate();
19047         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19048         this.list.alignTo(this.el, this.listAlign);
19049         this.list.endUpdate();
19050     },
19051
19052     // private
19053     onEmptyResults : function(){
19054         this.collapse();
19055     },
19056
19057     /**
19058      * Returns true if the dropdown list is expanded, else false.
19059      */
19060     isExpanded : function(){
19061         return this.list.isVisible();
19062     },
19063
19064     /**
19065      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19066      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19067      * @param {String} value The data value of the item to select
19068      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19069      * selected item if it is not currently in view (defaults to true)
19070      * @return {Boolean} True if the value matched an item in the list, else false
19071      */
19072     selectByValue : function(v, scrollIntoView){
19073         if(v !== undefined && v !== null){
19074             var r = this.findRecord(this.valueField || this.displayField, v);
19075             if(r){
19076                 this.select(this.store.indexOf(r), scrollIntoView);
19077                 return true;
19078             }
19079         }
19080         return false;
19081     },
19082
19083     /**
19084      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19085      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19086      * @param {Number} index The zero-based index of the list item to select
19087      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19088      * selected item if it is not currently in view (defaults to true)
19089      */
19090     select : function(index, scrollIntoView){
19091         this.selectedIndex = index;
19092         this.view.select(index);
19093         if(scrollIntoView !== false){
19094             var el = this.view.getNode(index);
19095             if(el){
19096                 this.innerList.scrollChildIntoView(el, false);
19097             }
19098         }
19099     },
19100
19101     // private
19102     selectNext : function(){
19103         var ct = this.store.getCount();
19104         if(ct > 0){
19105             if(this.selectedIndex == -1){
19106                 this.select(0);
19107             }else if(this.selectedIndex < ct-1){
19108                 this.select(this.selectedIndex+1);
19109             }
19110         }
19111     },
19112
19113     // private
19114     selectPrev : function(){
19115         var ct = this.store.getCount();
19116         if(ct > 0){
19117             if(this.selectedIndex == -1){
19118                 this.select(0);
19119             }else if(this.selectedIndex != 0){
19120                 this.select(this.selectedIndex-1);
19121             }
19122         }
19123     },
19124
19125     // private
19126     onKeyUp : function(e){
19127         if(this.editable !== false && !e.isSpecialKey()){
19128             this.lastKey = e.getKey();
19129             this.dqTask.delay(this.queryDelay);
19130         }
19131     },
19132
19133     // private
19134     validateBlur : function(){
19135         return !this.list || !this.list.isVisible();   
19136     },
19137
19138     // private
19139     initQuery : function(){
19140         this.doQuery(this.getRawValue());
19141     },
19142
19143     // private
19144     doForce : function(){
19145         if(this.el.dom.value.length > 0){
19146             this.el.dom.value =
19147                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19148              
19149         }
19150     },
19151
19152     /**
19153      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19154      * query allowing the query action to be canceled if needed.
19155      * @param {String} query The SQL query to execute
19156      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19157      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19158      * saved in the current store (defaults to false)
19159      */
19160     doQuery : function(q, forceAll){
19161         if(q === undefined || q === null){
19162             q = '';
19163         }
19164         var qe = {
19165             query: q,
19166             forceAll: forceAll,
19167             combo: this,
19168             cancel:false
19169         };
19170         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19171             return false;
19172         }
19173         q = qe.query;
19174         forceAll = qe.forceAll;
19175         if(forceAll === true || (q.length >= this.minChars)){
19176             if(this.lastQuery != q || this.alwaysQuery){
19177                 this.lastQuery = q;
19178                 if(this.mode == 'local'){
19179                     this.selectedIndex = -1;
19180                     if(forceAll){
19181                         this.store.clearFilter();
19182                     }else{
19183                         this.store.filter(this.displayField, q);
19184                     }
19185                     this.onLoad();
19186                 }else{
19187                     this.store.baseParams[this.queryParam] = q;
19188                     this.store.load({
19189                         params: this.getParams(q)
19190                     });
19191                     this.expand();
19192                 }
19193             }else{
19194                 this.selectedIndex = -1;
19195                 this.onLoad();   
19196             }
19197         }
19198     },
19199
19200     // private
19201     getParams : function(q){
19202         var p = {};
19203         //p[this.queryParam] = q;
19204         if(this.pageSize){
19205             p.start = 0;
19206             p.limit = this.pageSize;
19207         }
19208         return p;
19209     },
19210
19211     /**
19212      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19213      */
19214     collapse : function(){
19215         if(!this.isExpanded()){
19216             return;
19217         }
19218         this.list.hide();
19219         Roo.get(document).un('mousedown', this.collapseIf, this);
19220         Roo.get(document).un('mousewheel', this.collapseIf, this);
19221         if (!this.editable) {
19222             Roo.get(document).un('keydown', this.listKeyPress, this);
19223         }
19224         this.fireEvent('collapse', this);
19225     },
19226
19227     // private
19228     collapseIf : function(e){
19229         if(!e.within(this.wrap) && !e.within(this.list)){
19230             this.collapse();
19231         }
19232     },
19233
19234     /**
19235      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19236      */
19237     expand : function(){
19238         if(this.isExpanded() || !this.hasFocus){
19239             return;
19240         }
19241         this.list.alignTo(this.el, this.listAlign);
19242         this.list.show();
19243         Roo.get(document).on('mousedown', this.collapseIf, this);
19244         Roo.get(document).on('mousewheel', this.collapseIf, this);
19245         if (!this.editable) {
19246             Roo.get(document).on('keydown', this.listKeyPress, this);
19247         }
19248         
19249         this.fireEvent('expand', this);
19250     },
19251
19252     // private
19253     // Implements the default empty TriggerField.onTriggerClick function
19254     onTriggerClick : function(){
19255         if(this.disabled){
19256             return;
19257         }
19258         if(this.isExpanded()){
19259             this.collapse();
19260             if (!this.blockFocus) {
19261                 this.el.focus();
19262             }
19263             
19264         }else {
19265             this.hasFocus = true;
19266             if(this.triggerAction == 'all') {
19267                 this.doQuery(this.allQuery, true);
19268             } else {
19269                 this.doQuery(this.getRawValue());
19270             }
19271             if (!this.blockFocus) {
19272                 this.el.focus();
19273             }
19274         }
19275     },
19276     listKeyPress : function(e)
19277     {
19278         //Roo.log('listkeypress');
19279         // scroll to first matching element based on key pres..
19280         if (e.isSpecialKey()) {
19281             return false;
19282         }
19283         var k = String.fromCharCode(e.getKey()).toUpperCase();
19284         //Roo.log(k);
19285         var match  = false;
19286         var csel = this.view.getSelectedNodes();
19287         var cselitem = false;
19288         if (csel.length) {
19289             var ix = this.view.indexOf(csel[0]);
19290             cselitem  = this.store.getAt(ix);
19291             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19292                 cselitem = false;
19293             }
19294             
19295         }
19296         
19297         this.store.each(function(v) { 
19298             if (cselitem) {
19299                 // start at existing selection.
19300                 if (cselitem.id == v.id) {
19301                     cselitem = false;
19302                 }
19303                 return;
19304             }
19305                 
19306             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19307                 match = this.store.indexOf(v);
19308                 return false;
19309             }
19310         }, this);
19311         
19312         if (match === false) {
19313             return true; // no more action?
19314         }
19315         // scroll to?
19316         this.view.select(match);
19317         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19318         sn.scrollIntoView(sn.dom.parentNode, false);
19319     } 
19320
19321     /** 
19322     * @cfg {Boolean} grow 
19323     * @hide 
19324     */
19325     /** 
19326     * @cfg {Number} growMin 
19327     * @hide 
19328     */
19329     /** 
19330     * @cfg {Number} growMax 
19331     * @hide 
19332     */
19333     /**
19334      * @hide
19335      * @method autoSize
19336      */
19337 });/*
19338  * Copyright(c) 2010-2012, Roo J Solutions Limited
19339  *
19340  * Licence LGPL
19341  *
19342  */
19343
19344 /**
19345  * @class Roo.form.ComboBoxArray
19346  * @extends Roo.form.TextField
19347  * A facebook style adder... for lists of email / people / countries  etc...
19348  * pick multiple items from a combo box, and shows each one.
19349  *
19350  *  Fred [x]  Brian [x]  [Pick another |v]
19351  *
19352  *
19353  *  For this to work: it needs various extra information
19354  *    - normal combo problay has
19355  *      name, hiddenName
19356  *    + displayField, valueField
19357  *
19358  *    For our purpose...
19359  *
19360  *
19361  *   If we change from 'extends' to wrapping...
19362  *   
19363  *  
19364  *
19365  
19366  
19367  * @constructor
19368  * Create a new ComboBoxArray.
19369  * @param {Object} config Configuration options
19370  */
19371  
19372
19373 Roo.form.ComboBoxArray = function(config)
19374 {
19375     this.addEvents({
19376         /**
19377          * @event beforeremove
19378          * Fires before remove the value from the list
19379              * @param {Roo.form.ComboBoxArray} _self This combo box array
19380              * @param {Roo.form.ComboBoxArray.Item} item removed item
19381              */
19382         'beforeremove' : true,
19383         /**
19384          * @event remove
19385          * Fires when remove the value from the list
19386              * @param {Roo.form.ComboBoxArray} _self This combo box array
19387              * @param {Roo.form.ComboBoxArray.Item} item removed item
19388              */
19389         'remove' : true
19390         
19391         
19392     });
19393     
19394     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19395     
19396     this.items = new Roo.util.MixedCollection(false);
19397     
19398     // construct the child combo...
19399     
19400     
19401     
19402     
19403    
19404     
19405 }
19406
19407  
19408 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19409
19410     /**
19411      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19412      */
19413     
19414     lastData : false,
19415     
19416     // behavies liek a hiddne field
19417     inputType:      'hidden',
19418     /**
19419      * @cfg {Number} width The width of the box that displays the selected element
19420      */ 
19421     width:          300,
19422
19423     
19424     
19425     /**
19426      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19427      */
19428     name : false,
19429     /**
19430      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19431      */
19432     hiddenName : false,
19433       /**
19434      * @cfg {String} seperator    The value seperator normally ',' 
19435      */
19436     seperator : ',',
19437     
19438     // private the array of items that are displayed..
19439     items  : false,
19440     // private - the hidden field el.
19441     hiddenEl : false,
19442     // private - the filed el..
19443     el : false,
19444     
19445     //validateValue : function() { return true; }, // all values are ok!
19446     //onAddClick: function() { },
19447     
19448     onRender : function(ct, position) 
19449     {
19450         
19451         // create the standard hidden element
19452         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19453         
19454         
19455         // give fake names to child combo;
19456         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19457         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19458         
19459         this.combo = Roo.factory(this.combo, Roo.form);
19460         this.combo.onRender(ct, position);
19461         if (typeof(this.combo.width) != 'undefined') {
19462             this.combo.onResize(this.combo.width,0);
19463         }
19464         
19465         this.combo.initEvents();
19466         
19467         // assigned so form know we need to do this..
19468         this.store          = this.combo.store;
19469         this.valueField     = this.combo.valueField;
19470         this.displayField   = this.combo.displayField ;
19471         
19472         
19473         this.combo.wrap.addClass('x-cbarray-grp');
19474         
19475         var cbwrap = this.combo.wrap.createChild(
19476             {tag: 'div', cls: 'x-cbarray-cb'},
19477             this.combo.el.dom
19478         );
19479         
19480              
19481         this.hiddenEl = this.combo.wrap.createChild({
19482             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19483         });
19484         this.el = this.combo.wrap.createChild({
19485             tag: 'input',  type:'hidden' , name: this.name, value : ''
19486         });
19487          //   this.el.dom.removeAttribute("name");
19488         
19489         
19490         this.outerWrap = this.combo.wrap;
19491         this.wrap = cbwrap;
19492         
19493         this.outerWrap.setWidth(this.width);
19494         this.outerWrap.dom.removeChild(this.el.dom);
19495         
19496         this.wrap.dom.appendChild(this.el.dom);
19497         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19498         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19499         
19500         this.combo.trigger.setStyle('position','relative');
19501         this.combo.trigger.setStyle('left', '0px');
19502         this.combo.trigger.setStyle('top', '2px');
19503         
19504         this.combo.el.setStyle('vertical-align', 'text-bottom');
19505         
19506         //this.trigger.setStyle('vertical-align', 'top');
19507         
19508         // this should use the code from combo really... on('add' ....)
19509         if (this.adder) {
19510             
19511         
19512             this.adder = this.outerWrap.createChild(
19513                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19514             var _t = this;
19515             this.adder.on('click', function(e) {
19516                 _t.fireEvent('adderclick', this, e);
19517             }, _t);
19518         }
19519         //var _t = this;
19520         //this.adder.on('click', this.onAddClick, _t);
19521         
19522         
19523         this.combo.on('select', function(cb, rec, ix) {
19524             this.addItem(rec.data);
19525             
19526             cb.setValue('');
19527             cb.el.dom.value = '';
19528             //cb.lastData = rec.data;
19529             // add to list
19530             
19531         }, this);
19532         
19533         
19534     },
19535     
19536     
19537     getName: function()
19538     {
19539         // returns hidden if it's set..
19540         if (!this.rendered) {return ''};
19541         return  this.hiddenName ? this.hiddenName : this.name;
19542         
19543     },
19544     
19545     
19546     onResize: function(w, h){
19547         
19548         return;
19549         // not sure if this is needed..
19550         //this.combo.onResize(w,h);
19551         
19552         if(typeof w != 'number'){
19553             // we do not handle it!?!?
19554             return;
19555         }
19556         var tw = this.combo.trigger.getWidth();
19557         tw += this.addicon ? this.addicon.getWidth() : 0;
19558         tw += this.editicon ? this.editicon.getWidth() : 0;
19559         var x = w - tw;
19560         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19561             
19562         this.combo.trigger.setStyle('left', '0px');
19563         
19564         if(this.list && this.listWidth === undefined){
19565             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19566             this.list.setWidth(lw);
19567             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19568         }
19569         
19570     
19571         
19572     },
19573     
19574     addItem: function(rec)
19575     {
19576         var valueField = this.combo.valueField;
19577         var displayField = this.combo.displayField;
19578         
19579         if (this.items.indexOfKey(rec[valueField]) > -1) {
19580             //console.log("GOT " + rec.data.id);
19581             return;
19582         }
19583         
19584         var x = new Roo.form.ComboBoxArray.Item({
19585             //id : rec[this.idField],
19586             data : rec,
19587             displayField : displayField ,
19588             tipField : displayField ,
19589             cb : this
19590         });
19591         // use the 
19592         this.items.add(rec[valueField],x);
19593         // add it before the element..
19594         this.updateHiddenEl();
19595         x.render(this.outerWrap, this.wrap.dom);
19596         // add the image handler..
19597     },
19598     
19599     updateHiddenEl : function()
19600     {
19601         this.validate();
19602         if (!this.hiddenEl) {
19603             return;
19604         }
19605         var ar = [];
19606         var idField = this.combo.valueField;
19607         
19608         this.items.each(function(f) {
19609             ar.push(f.data[idField]);
19610         });
19611         this.hiddenEl.dom.value = ar.join(this.seperator);
19612         this.validate();
19613     },
19614     
19615     reset : function()
19616     {
19617         this.items.clear();
19618         
19619         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19620            el.remove();
19621         });
19622         
19623         this.el.dom.value = '';
19624         if (this.hiddenEl) {
19625             this.hiddenEl.dom.value = '';
19626         }
19627         
19628     },
19629     getValue: function()
19630     {
19631         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19632     },
19633     setValue: function(v) // not a valid action - must use addItems..
19634     {
19635         
19636         this.reset();
19637          
19638         if (this.store.isLocal && (typeof(v) == 'string')) {
19639             // then we can use the store to find the values..
19640             // comma seperated at present.. this needs to allow JSON based encoding..
19641             this.hiddenEl.value  = v;
19642             var v_ar = [];
19643             Roo.each(v.split(this.seperator), function(k) {
19644                 Roo.log("CHECK " + this.valueField + ',' + k);
19645                 var li = this.store.query(this.valueField, k);
19646                 if (!li.length) {
19647                     return;
19648                 }
19649                 var add = {};
19650                 add[this.valueField] = k;
19651                 add[this.displayField] = li.item(0).data[this.displayField];
19652                 
19653                 this.addItem(add);
19654             }, this) 
19655              
19656         }
19657         if (typeof(v) == 'object' ) {
19658             // then let's assume it's an array of objects..
19659             Roo.each(v, function(l) {
19660                 var add = l;
19661                 if (typeof(l) == 'string') {
19662                     add = {};
19663                     add[this.valueField] = l;
19664                     add[this.displayField] = l
19665                 }
19666                 this.addItem(add);
19667             }, this);
19668              
19669         }
19670         
19671         
19672     },
19673     setFromData: function(v)
19674     {
19675         // this recieves an object, if setValues is called.
19676         this.reset();
19677         this.el.dom.value = v[this.displayField];
19678         this.hiddenEl.dom.value = v[this.valueField];
19679         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19680             return;
19681         }
19682         var kv = v[this.valueField];
19683         var dv = v[this.displayField];
19684         kv = typeof(kv) != 'string' ? '' : kv;
19685         dv = typeof(dv) != 'string' ? '' : dv;
19686         
19687         
19688         var keys = kv.split(this.seperator);
19689         var display = dv.split(this.seperator);
19690         for (var i = 0 ; i < keys.length; i++) {
19691             add = {};
19692             add[this.valueField] = keys[i];
19693             add[this.displayField] = display[i];
19694             this.addItem(add);
19695         }
19696       
19697         
19698     },
19699     
19700     /**
19701      * Validates the combox array value
19702      * @return {Boolean} True if the value is valid, else false
19703      */
19704     validate : function(){
19705         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19706             this.clearInvalid();
19707             return true;
19708         }
19709         return false;
19710     },
19711     
19712     validateValue : function(value){
19713         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19714         
19715     },
19716     
19717     /*@
19718      * overide
19719      * 
19720      */
19721     isDirty : function() {
19722         if(this.disabled) {
19723             return false;
19724         }
19725         
19726         try {
19727             var d = Roo.decode(String(this.originalValue));
19728         } catch (e) {
19729             return String(this.getValue()) !== String(this.originalValue);
19730         }
19731         
19732         var originalValue = [];
19733         
19734         for (var i = 0; i < d.length; i++){
19735             originalValue.push(d[i][this.valueField]);
19736         }
19737         
19738         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19739         
19740     }
19741     
19742 });
19743
19744
19745
19746 /**
19747  * @class Roo.form.ComboBoxArray.Item
19748  * @extends Roo.BoxComponent
19749  * A selected item in the list
19750  *  Fred [x]  Brian [x]  [Pick another |v]
19751  * 
19752  * @constructor
19753  * Create a new item.
19754  * @param {Object} config Configuration options
19755  */
19756  
19757 Roo.form.ComboBoxArray.Item = function(config) {
19758     config.id = Roo.id();
19759     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19760 }
19761
19762 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19763     data : {},
19764     cb: false,
19765     displayField : false,
19766     tipField : false,
19767     
19768     
19769     defaultAutoCreate : {
19770         tag: 'div',
19771         cls: 'x-cbarray-item',
19772         cn : [ 
19773             { tag: 'div' },
19774             {
19775                 tag: 'img',
19776                 width:16,
19777                 height : 16,
19778                 src : Roo.BLANK_IMAGE_URL ,
19779                 align: 'center'
19780             }
19781         ]
19782         
19783     },
19784     
19785  
19786     onRender : function(ct, position)
19787     {
19788         Roo.form.Field.superclass.onRender.call(this, ct, position);
19789         
19790         if(!this.el){
19791             var cfg = this.getAutoCreate();
19792             this.el = ct.createChild(cfg, position);
19793         }
19794         
19795         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19796         
19797         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19798             this.cb.renderer(this.data) :
19799             String.format('{0}',this.data[this.displayField]);
19800         
19801             
19802         this.el.child('div').dom.setAttribute('qtip',
19803                         String.format('{0}',this.data[this.tipField])
19804         );
19805         
19806         this.el.child('img').on('click', this.remove, this);
19807         
19808     },
19809    
19810     remove : function()
19811     {
19812         if(this.cb.disabled){
19813             return;
19814         }
19815         
19816         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19817             this.cb.items.remove(this);
19818             this.el.child('img').un('click', this.remove, this);
19819             this.el.remove();
19820             this.cb.updateHiddenEl();
19821
19822             this.cb.fireEvent('remove', this.cb, this);
19823         }
19824         
19825     }
19826 });/*
19827  * RooJS Library 1.1.1
19828  * Copyright(c) 2008-2011  Alan Knowles
19829  *
19830  * License - LGPL
19831  */
19832  
19833
19834 /**
19835  * @class Roo.form.ComboNested
19836  * @extends Roo.form.ComboBox
19837  * A combobox for that allows selection of nested items in a list,
19838  * eg.
19839  *
19840  *  Book
19841  *    -> red
19842  *    -> green
19843  *  Table
19844  *    -> square
19845  *      ->red
19846  *      ->green
19847  *    -> rectangle
19848  *      ->green
19849  *      
19850  * 
19851  * @constructor
19852  * Create a new ComboNested
19853  * @param {Object} config Configuration options
19854  */
19855 Roo.form.ComboNested = function(config){
19856     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19857     // should verify some data...
19858     // like
19859     // hiddenName = required..
19860     // displayField = required
19861     // valudField == required
19862     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19863     var _t = this;
19864     Roo.each(req, function(e) {
19865         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19866             throw "Roo.form.ComboNested : missing value for: " + e;
19867         }
19868     });
19869      
19870     
19871 };
19872
19873 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19874    
19875     /*
19876      * @config {Number} max Number of columns to show
19877      */
19878     
19879     maxColumns : 3,
19880    
19881     list : null, // the outermost div..
19882     innerLists : null, // the
19883     views : null,
19884     stores : null,
19885     // private
19886     loadingChildren : false,
19887     
19888     onRender : function(ct, position)
19889     {
19890         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19891         
19892         if(this.hiddenName){
19893             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19894                     'before', true);
19895             this.hiddenField.value =
19896                 this.hiddenValue !== undefined ? this.hiddenValue :
19897                 this.value !== undefined ? this.value : '';
19898
19899             // prevent input submission
19900             this.el.dom.removeAttribute('name');
19901              
19902              
19903         }
19904         
19905         if(Roo.isGecko){
19906             this.el.dom.setAttribute('autocomplete', 'off');
19907         }
19908
19909         var cls = 'x-combo-list';
19910
19911         this.list = new Roo.Layer({
19912             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19913         });
19914
19915         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19916         this.list.setWidth(lw);
19917         this.list.swallowEvent('mousewheel');
19918         this.assetHeight = 0;
19919
19920         if(this.title){
19921             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19922             this.assetHeight += this.header.getHeight();
19923         }
19924         this.innerLists = [];
19925         this.views = [];
19926         this.stores = [];
19927         for (var i =0 ; i < this.maxColumns; i++) {
19928             this.onRenderList( cls, i);
19929         }
19930         
19931         // always needs footer, as we are going to have an 'OK' button.
19932         this.footer = this.list.createChild({cls:cls+'-ft'});
19933         this.pageTb = new Roo.Toolbar(this.footer);  
19934         var _this = this;
19935         this.pageTb.add(  {
19936             
19937             text: 'Done',
19938             handler: function()
19939             {
19940                 _this.collapse();
19941             }
19942         });
19943         
19944         if ( this.allowBlank && !this.disableClear) {
19945             
19946             this.pageTb.add(new Roo.Toolbar.Fill(), {
19947                 cls: 'x-btn-icon x-btn-clear',
19948                 text: '&#160;',
19949                 handler: function()
19950                 {
19951                     _this.collapse();
19952                     _this.clearValue();
19953                     _this.onSelect(false, -1);
19954                 }
19955             });
19956         }
19957         if (this.footer) {
19958             this.assetHeight += this.footer.getHeight();
19959         }
19960         
19961     },
19962     onRenderList : function (  cls, i)
19963     {
19964         
19965         var lw = Math.floor(
19966                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19967         );
19968         
19969         this.list.setWidth(lw); // default to '1'
19970
19971         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19972         //il.on('mouseover', this.onViewOver, this, { list:  i });
19973         //il.on('mousemove', this.onViewMove, this, { list:  i });
19974         il.setWidth(lw);
19975         il.setStyle({ 'overflow-x' : 'hidden'});
19976
19977         if(!this.tpl){
19978             this.tpl = new Roo.Template({
19979                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19980                 isEmpty: function (value, allValues) {
19981                     //Roo.log(value);
19982                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19983                     return dl ? 'has-children' : 'no-children'
19984                 }
19985             });
19986         }
19987         
19988         var store  = this.store;
19989         if (i > 0) {
19990             store  = new Roo.data.SimpleStore({
19991                 //fields : this.store.reader.meta.fields,
19992                 reader : this.store.reader,
19993                 data : [ ]
19994             });
19995         }
19996         this.stores[i]  = store;
19997                   
19998         var view = this.views[i] = new Roo.View(
19999             il,
20000             this.tpl,
20001             {
20002                 singleSelect:true,
20003                 store: store,
20004                 selectedClass: this.selectedClass
20005             }
20006         );
20007         view.getEl().setWidth(lw);
20008         view.getEl().setStyle({
20009             position: i < 1 ? 'relative' : 'absolute',
20010             top: 0,
20011             left: (i * lw ) + 'px',
20012             display : i > 0 ? 'none' : 'block'
20013         });
20014         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20015         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20016         //view.on('click', this.onViewClick, this, { list : i });
20017
20018         store.on('beforeload', this.onBeforeLoad, this);
20019         store.on('load',  this.onLoad, this, { list  : i});
20020         store.on('loadexception', this.onLoadException, this);
20021
20022         // hide the other vies..
20023         
20024         
20025         
20026     },
20027       
20028     restrictHeight : function()
20029     {
20030         var mh = 0;
20031         Roo.each(this.innerLists, function(il,i) {
20032             var el = this.views[i].getEl();
20033             el.dom.style.height = '';
20034             var inner = el.dom;
20035             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20036             // only adjust heights on other ones..
20037             mh = Math.max(h, mh);
20038             if (i < 1) {
20039                 
20040                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20041                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20042                
20043             }
20044             
20045             
20046         }, this);
20047         
20048         this.list.beginUpdate();
20049         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20050         this.list.alignTo(this.el, this.listAlign);
20051         this.list.endUpdate();
20052         
20053     },
20054      
20055     
20056     // -- store handlers..
20057     // private
20058     onBeforeLoad : function()
20059     {
20060         if(!this.hasFocus){
20061             return;
20062         }
20063         this.innerLists[0].update(this.loadingText ?
20064                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20065         this.restrictHeight();
20066         this.selectedIndex = -1;
20067     },
20068     // private
20069     onLoad : function(a,b,c,d)
20070     {
20071         if (!this.loadingChildren) {
20072             // then we are loading the top level. - hide the children
20073             for (var i = 1;i < this.views.length; i++) {
20074                 this.views[i].getEl().setStyle({ display : 'none' });
20075             }
20076             var lw = Math.floor(
20077                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20078             );
20079         
20080              this.list.setWidth(lw); // default to '1'
20081
20082             
20083         }
20084         if(!this.hasFocus){
20085             return;
20086         }
20087         
20088         if(this.store.getCount() > 0) {
20089             this.expand();
20090             this.restrictHeight();   
20091         } else {
20092             this.onEmptyResults();
20093         }
20094         
20095         if (!this.loadingChildren) {
20096             this.selectActive();
20097         }
20098         /*
20099         this.stores[1].loadData([]);
20100         this.stores[2].loadData([]);
20101         this.views
20102         */    
20103     
20104         //this.el.focus();
20105     },
20106     
20107     
20108     // private
20109     onLoadException : function()
20110     {
20111         this.collapse();
20112         Roo.log(this.store.reader.jsonData);
20113         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20114             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20115         }
20116         
20117         
20118     },
20119     // no cleaning of leading spaces on blur here.
20120     cleanLeadingSpace : function(e) { },
20121     
20122
20123     onSelectChange : function (view, sels, opts )
20124     {
20125         var ix = view.getSelectedIndexes();
20126          
20127         if (opts.list > this.maxColumns - 2) {
20128             if (view.store.getCount()<  1) {
20129                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20130
20131             } else  {
20132                 if (ix.length) {
20133                     // used to clear ?? but if we are loading unselected 
20134                     this.setFromData(view.store.getAt(ix[0]).data);
20135                 }
20136                 
20137             }
20138             
20139             return;
20140         }
20141         
20142         if (!ix.length) {
20143             // this get's fired when trigger opens..
20144            // this.setFromData({});
20145             var str = this.stores[opts.list+1];
20146             str.data.clear(); // removeall wihtout the fire events..
20147             return;
20148         }
20149         
20150         var rec = view.store.getAt(ix[0]);
20151          
20152         this.setFromData(rec.data);
20153         this.fireEvent('select', this, rec, ix[0]);
20154         
20155         var lw = Math.floor(
20156              (
20157                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20158              ) / this.maxColumns
20159         );
20160         this.loadingChildren = true;
20161         this.stores[opts.list+1].loadDataFromChildren( rec );
20162         this.loadingChildren = false;
20163         var dl = this.stores[opts.list+1]. getTotalCount();
20164         
20165         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20166         
20167         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20168         for (var i = opts.list+2; i < this.views.length;i++) {
20169             this.views[i].getEl().setStyle({ display : 'none' });
20170         }
20171         
20172         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20173         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20174         
20175         if (this.isLoading) {
20176            // this.selectActive(opts.list);
20177         }
20178          
20179     },
20180     
20181     
20182     
20183     
20184     onDoubleClick : function()
20185     {
20186         this.collapse(); //??
20187     },
20188     
20189      
20190     
20191     
20192     
20193     // private
20194     recordToStack : function(store, prop, value, stack)
20195     {
20196         var cstore = new Roo.data.SimpleStore({
20197             //fields : this.store.reader.meta.fields, // we need array reader.. for
20198             reader : this.store.reader,
20199             data : [ ]
20200         });
20201         var _this = this;
20202         var record  = false;
20203         var srec = false;
20204         if(store.getCount() < 1){
20205             return false;
20206         }
20207         store.each(function(r){
20208             if(r.data[prop] == value){
20209                 record = r;
20210             srec = r;
20211                 return false;
20212             }
20213             if (r.data.cn && r.data.cn.length) {
20214                 cstore.loadDataFromChildren( r);
20215                 var cret = _this.recordToStack(cstore, prop, value, stack);
20216                 if (cret !== false) {
20217                     record = cret;
20218                     srec = r;
20219                     return false;
20220                 }
20221             }
20222              
20223             return true;
20224         });
20225         if (record == false) {
20226             return false
20227         }
20228         stack.unshift(srec);
20229         return record;
20230     },
20231     
20232     /*
20233      * find the stack of stores that match our value.
20234      *
20235      * 
20236      */
20237     
20238     selectActive : function ()
20239     {
20240         // if store is not loaded, then we will need to wait for that to happen first.
20241         var stack = [];
20242         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20243         for (var i = 0; i < stack.length; i++ ) {
20244             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20245         }
20246         
20247     }
20248         
20249          
20250     
20251     
20252     
20253     
20254 });/*
20255  * Based on:
20256  * Ext JS Library 1.1.1
20257  * Copyright(c) 2006-2007, Ext JS, LLC.
20258  *
20259  * Originally Released Under LGPL - original licence link has changed is not relivant.
20260  *
20261  * Fork - LGPL
20262  * <script type="text/javascript">
20263  */
20264 /**
20265  * @class Roo.form.Checkbox
20266  * @extends Roo.form.Field
20267  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20268  * @constructor
20269  * Creates a new Checkbox
20270  * @param {Object} config Configuration options
20271  */
20272 Roo.form.Checkbox = function(config){
20273     Roo.form.Checkbox.superclass.constructor.call(this, config);
20274     this.addEvents({
20275         /**
20276          * @event check
20277          * Fires when the checkbox is checked or unchecked.
20278              * @param {Roo.form.Checkbox} this This checkbox
20279              * @param {Boolean} checked The new checked value
20280              */
20281         check : true
20282     });
20283 };
20284
20285 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20286     /**
20287      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20288      */
20289     focusClass : undefined,
20290     /**
20291      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20292      */
20293     fieldClass: "x-form-field",
20294     /**
20295      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20296      */
20297     checked: false,
20298     /**
20299      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20300      * {tag: "input", type: "checkbox", autocomplete: "off"})
20301      */
20302     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20303     /**
20304      * @cfg {String} boxLabel The text that appears beside the checkbox
20305      */
20306     boxLabel : "",
20307     /**
20308      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20309      */  
20310     inputValue : '1',
20311     /**
20312      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20313      */
20314      valueOff: '0', // value when not checked..
20315
20316     actionMode : 'viewEl', 
20317     //
20318     // private
20319     itemCls : 'x-menu-check-item x-form-item',
20320     groupClass : 'x-menu-group-item',
20321     inputType : 'hidden',
20322     
20323     
20324     inSetChecked: false, // check that we are not calling self...
20325     
20326     inputElement: false, // real input element?
20327     basedOn: false, // ????
20328     
20329     isFormField: true, // not sure where this is needed!!!!
20330
20331     onResize : function(){
20332         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20333         if(!this.boxLabel){
20334             this.el.alignTo(this.wrap, 'c-c');
20335         }
20336     },
20337
20338     initEvents : function(){
20339         Roo.form.Checkbox.superclass.initEvents.call(this);
20340         this.el.on("click", this.onClick,  this);
20341         this.el.on("change", this.onClick,  this);
20342     },
20343
20344
20345     getResizeEl : function(){
20346         return this.wrap;
20347     },
20348
20349     getPositionEl : function(){
20350         return this.wrap;
20351     },
20352
20353     // private
20354     onRender : function(ct, position){
20355         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20356         /*
20357         if(this.inputValue !== undefined){
20358             this.el.dom.value = this.inputValue;
20359         }
20360         */
20361         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20362         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20363         var viewEl = this.wrap.createChild({ 
20364             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20365         this.viewEl = viewEl;   
20366         this.wrap.on('click', this.onClick,  this); 
20367         
20368         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20369         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20370         
20371         
20372         
20373         if(this.boxLabel){
20374             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20375         //    viewEl.on('click', this.onClick,  this); 
20376         }
20377         //if(this.checked){
20378             this.setChecked(this.checked);
20379         //}else{
20380             //this.checked = this.el.dom;
20381         //}
20382
20383     },
20384
20385     // private
20386     initValue : Roo.emptyFn,
20387
20388     /**
20389      * Returns the checked state of the checkbox.
20390      * @return {Boolean} True if checked, else false
20391      */
20392     getValue : function(){
20393         if(this.el){
20394             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20395         }
20396         return this.valueOff;
20397         
20398     },
20399
20400         // private
20401     onClick : function(){ 
20402         if (this.disabled) {
20403             return;
20404         }
20405         this.setChecked(!this.checked);
20406
20407         //if(this.el.dom.checked != this.checked){
20408         //    this.setValue(this.el.dom.checked);
20409        // }
20410     },
20411
20412     /**
20413      * Sets the checked state of the checkbox.
20414      * On is always based on a string comparison between inputValue and the param.
20415      * @param {Boolean/String} value - the value to set 
20416      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20417      */
20418     setValue : function(v,suppressEvent){
20419         
20420         
20421         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20422         //if(this.el && this.el.dom){
20423         //    this.el.dom.checked = this.checked;
20424         //    this.el.dom.defaultChecked = this.checked;
20425         //}
20426         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20427         //this.fireEvent("check", this, this.checked);
20428     },
20429     // private..
20430     setChecked : function(state,suppressEvent)
20431     {
20432         if (this.inSetChecked) {
20433             this.checked = state;
20434             return;
20435         }
20436         
20437     
20438         if(this.wrap){
20439             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20440         }
20441         this.checked = state;
20442         if(suppressEvent !== true){
20443             this.fireEvent('check', this, state);
20444         }
20445         this.inSetChecked = true;
20446         this.el.dom.value = state ? this.inputValue : this.valueOff;
20447         this.inSetChecked = false;
20448         
20449     },
20450     // handle setting of hidden value by some other method!!?!?
20451     setFromHidden: function()
20452     {
20453         if(!this.el){
20454             return;
20455         }
20456         //console.log("SET FROM HIDDEN");
20457         //alert('setFrom hidden');
20458         this.setValue(this.el.dom.value);
20459     },
20460     
20461     onDestroy : function()
20462     {
20463         if(this.viewEl){
20464             Roo.get(this.viewEl).remove();
20465         }
20466          
20467         Roo.form.Checkbox.superclass.onDestroy.call(this);
20468     },
20469     
20470     setBoxLabel : function(str)
20471     {
20472         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20473     }
20474
20475 });/*
20476  * Based on:
20477  * Ext JS Library 1.1.1
20478  * Copyright(c) 2006-2007, Ext JS, LLC.
20479  *
20480  * Originally Released Under LGPL - original licence link has changed is not relivant.
20481  *
20482  * Fork - LGPL
20483  * <script type="text/javascript">
20484  */
20485  
20486 /**
20487  * @class Roo.form.Radio
20488  * @extends Roo.form.Checkbox
20489  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20490  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20491  * @constructor
20492  * Creates a new Radio
20493  * @param {Object} config Configuration options
20494  */
20495 Roo.form.Radio = function(){
20496     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20497 };
20498 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20499     inputType: 'radio',
20500
20501     /**
20502      * If this radio is part of a group, it will return the selected value
20503      * @return {String}
20504      */
20505     getGroupValue : function(){
20506         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20507     },
20508     
20509     
20510     onRender : function(ct, position){
20511         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20512         
20513         if(this.inputValue !== undefined){
20514             this.el.dom.value = this.inputValue;
20515         }
20516          
20517         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20518         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20519         //var viewEl = this.wrap.createChild({ 
20520         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20521         //this.viewEl = viewEl;   
20522         //this.wrap.on('click', this.onClick,  this); 
20523         
20524         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20525         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20526         
20527         
20528         
20529         if(this.boxLabel){
20530             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20531         //    viewEl.on('click', this.onClick,  this); 
20532         }
20533          if(this.checked){
20534             this.el.dom.checked =   'checked' ;
20535         }
20536          
20537     } 
20538     
20539     
20540 });//<script type="text/javascript">
20541
20542 /*
20543  * Based  Ext JS Library 1.1.1
20544  * Copyright(c) 2006-2007, Ext JS, LLC.
20545  * LGPL
20546  *
20547  */
20548  
20549 /**
20550  * @class Roo.HtmlEditorCore
20551  * @extends Roo.Component
20552  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20553  *
20554  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20555  */
20556
20557 Roo.HtmlEditorCore = function(config){
20558     
20559     
20560     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20561     
20562     
20563     this.addEvents({
20564         /**
20565          * @event initialize
20566          * Fires when the editor is fully initialized (including the iframe)
20567          * @param {Roo.HtmlEditorCore} this
20568          */
20569         initialize: true,
20570         /**
20571          * @event activate
20572          * Fires when the editor is first receives the focus. Any insertion must wait
20573          * until after this event.
20574          * @param {Roo.HtmlEditorCore} this
20575          */
20576         activate: true,
20577          /**
20578          * @event beforesync
20579          * Fires before the textarea is updated with content from the editor iframe. Return false
20580          * to cancel the sync.
20581          * @param {Roo.HtmlEditorCore} this
20582          * @param {String} html
20583          */
20584         beforesync: true,
20585          /**
20586          * @event beforepush
20587          * Fires before the iframe editor is updated with content from the textarea. Return false
20588          * to cancel the push.
20589          * @param {Roo.HtmlEditorCore} this
20590          * @param {String} html
20591          */
20592         beforepush: true,
20593          /**
20594          * @event sync
20595          * Fires when the textarea is updated with content from the editor iframe.
20596          * @param {Roo.HtmlEditorCore} this
20597          * @param {String} html
20598          */
20599         sync: true,
20600          /**
20601          * @event push
20602          * Fires when the iframe editor is updated with content from the textarea.
20603          * @param {Roo.HtmlEditorCore} this
20604          * @param {String} html
20605          */
20606         push: true,
20607         
20608         /**
20609          * @event editorevent
20610          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20611          * @param {Roo.HtmlEditorCore} this
20612          */
20613         editorevent: true
20614         
20615     });
20616     
20617     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20618     
20619     // defaults : white / black...
20620     this.applyBlacklists();
20621     
20622     
20623     
20624 };
20625
20626
20627 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20628
20629
20630      /**
20631      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20632      */
20633     
20634     owner : false,
20635     
20636      /**
20637      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20638      *                        Roo.resizable.
20639      */
20640     resizable : false,
20641      /**
20642      * @cfg {Number} height (in pixels)
20643      */   
20644     height: 300,
20645    /**
20646      * @cfg {Number} width (in pixels)
20647      */   
20648     width: 500,
20649     
20650     /**
20651      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20652      * 
20653      */
20654     stylesheets: false,
20655     
20656     /**
20657      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20658      */
20659     allowComments: false,
20660     // id of frame..
20661     frameId: false,
20662     
20663     // private properties
20664     validationEvent : false,
20665     deferHeight: true,
20666     initialized : false,
20667     activated : false,
20668     sourceEditMode : false,
20669     onFocus : Roo.emptyFn,
20670     iframePad:3,
20671     hideMode:'offsets',
20672     
20673     clearUp: true,
20674     
20675     // blacklist + whitelisted elements..
20676     black: false,
20677     white: false,
20678      
20679     bodyCls : '',
20680
20681     /**
20682      * Protected method that will not generally be called directly. It
20683      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20684      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20685      */
20686     getDocMarkup : function(){
20687         // body styles..
20688         var st = '';
20689         
20690         // inherit styels from page...?? 
20691         if (this.stylesheets === false) {
20692             
20693             Roo.get(document.head).select('style').each(function(node) {
20694                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20695             });
20696             
20697             Roo.get(document.head).select('link').each(function(node) { 
20698                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20699             });
20700             
20701         } else if (!this.stylesheets.length) {
20702                 // simple..
20703                 st = '<style type="text/css">' +
20704                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20705                    '</style>';
20706         } else {
20707             for (var i in this.stylesheets) { 
20708                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20709             }
20710             
20711         }
20712         
20713         st +=  '<style type="text/css">' +
20714             'IMG { cursor: pointer } ' +
20715         '</style>';
20716
20717         var cls = 'roo-htmleditor-body';
20718         
20719         if(this.bodyCls.length){
20720             cls += ' ' + this.bodyCls;
20721         }
20722         
20723         return '<html><head>' + st  +
20724             //<style type="text/css">' +
20725             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20726             //'</style>' +
20727             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20728     },
20729
20730     // private
20731     onRender : function(ct, position)
20732     {
20733         var _t = this;
20734         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20735         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20736         
20737         
20738         this.el.dom.style.border = '0 none';
20739         this.el.dom.setAttribute('tabIndex', -1);
20740         this.el.addClass('x-hidden hide');
20741         
20742         
20743         
20744         if(Roo.isIE){ // fix IE 1px bogus margin
20745             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20746         }
20747        
20748         
20749         this.frameId = Roo.id();
20750         
20751          
20752         
20753         var iframe = this.owner.wrap.createChild({
20754             tag: 'iframe',
20755             cls: 'form-control', // bootstrap..
20756             id: this.frameId,
20757             name: this.frameId,
20758             frameBorder : 'no',
20759             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20760         }, this.el
20761         );
20762         
20763         
20764         this.iframe = iframe.dom;
20765
20766          this.assignDocWin();
20767         
20768         this.doc.designMode = 'on';
20769        
20770         this.doc.open();
20771         this.doc.write(this.getDocMarkup());
20772         this.doc.close();
20773
20774         
20775         var task = { // must defer to wait for browser to be ready
20776             run : function(){
20777                 //console.log("run task?" + this.doc.readyState);
20778                 this.assignDocWin();
20779                 if(this.doc.body || this.doc.readyState == 'complete'){
20780                     try {
20781                         this.doc.designMode="on";
20782                     } catch (e) {
20783                         return;
20784                     }
20785                     Roo.TaskMgr.stop(task);
20786                     this.initEditor.defer(10, this);
20787                 }
20788             },
20789             interval : 10,
20790             duration: 10000,
20791             scope: this
20792         };
20793         Roo.TaskMgr.start(task);
20794
20795     },
20796
20797     // private
20798     onResize : function(w, h)
20799     {
20800          Roo.log('resize: ' +w + ',' + h );
20801         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20802         if(!this.iframe){
20803             return;
20804         }
20805         if(typeof w == 'number'){
20806             
20807             this.iframe.style.width = w + 'px';
20808         }
20809         if(typeof h == 'number'){
20810             
20811             this.iframe.style.height = h + 'px';
20812             if(this.doc){
20813                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20814             }
20815         }
20816         
20817     },
20818
20819     /**
20820      * Toggles the editor between standard and source edit mode.
20821      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20822      */
20823     toggleSourceEdit : function(sourceEditMode){
20824         
20825         this.sourceEditMode = sourceEditMode === true;
20826         
20827         if(this.sourceEditMode){
20828  
20829             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20830             
20831         }else{
20832             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20833             //this.iframe.className = '';
20834             this.deferFocus();
20835         }
20836         //this.setSize(this.owner.wrap.getSize());
20837         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20838     },
20839
20840     
20841   
20842
20843     /**
20844      * Protected method that will not generally be called directly. If you need/want
20845      * custom HTML cleanup, this is the method you should override.
20846      * @param {String} html The HTML to be cleaned
20847      * return {String} The cleaned HTML
20848      */
20849     cleanHtml : function(html){
20850         html = String(html);
20851         if(html.length > 5){
20852             if(Roo.isSafari){ // strip safari nonsense
20853                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20854             }
20855         }
20856         if(html == '&nbsp;'){
20857             html = '';
20858         }
20859         return html;
20860     },
20861
20862     /**
20863      * HTML Editor -> Textarea
20864      * Protected method that will not generally be called directly. Syncs the contents
20865      * of the editor iframe with the textarea.
20866      */
20867     syncValue : function(){
20868         if(this.initialized){
20869             var bd = (this.doc.body || this.doc.documentElement);
20870             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20871             var html = bd.innerHTML;
20872             if(Roo.isSafari){
20873                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20874                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20875                 if(m && m[1]){
20876                     html = '<div style="'+m[0]+'">' + html + '</div>';
20877                 }
20878             }
20879             html = this.cleanHtml(html);
20880             // fix up the special chars.. normaly like back quotes in word...
20881             // however we do not want to do this with chinese..
20882             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20883                 
20884                 var cc = match.charCodeAt();
20885
20886                 // Get the character value, handling surrogate pairs
20887                 if (match.length == 2) {
20888                     // It's a surrogate pair, calculate the Unicode code point
20889                     var high = match.charCodeAt(0) - 0xD800;
20890                     var low  = match.charCodeAt(1) - 0xDC00;
20891                     cc = (high * 0x400) + low + 0x10000;
20892                 }  else if (
20893                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20894                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20895                     (cc >= 0xf900 && cc < 0xfb00 )
20896                 ) {
20897                         return match;
20898                 }  
20899          
20900                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20901                 return "&#" + cc + ";";
20902                 
20903                 
20904             });
20905             
20906             
20907              
20908             if(this.owner.fireEvent('beforesync', this, html) !== false){
20909                 this.el.dom.value = html;
20910                 this.owner.fireEvent('sync', this, html);
20911             }
20912         }
20913     },
20914
20915     /**
20916      * Protected method that will not generally be called directly. Pushes the value of the textarea
20917      * into the iframe editor.
20918      */
20919     pushValue : function(){
20920         if(this.initialized){
20921             var v = this.el.dom.value.trim();
20922             
20923 //            if(v.length < 1){
20924 //                v = '&#160;';
20925 //            }
20926             
20927             if(this.owner.fireEvent('beforepush', this, v) !== false){
20928                 var d = (this.doc.body || this.doc.documentElement);
20929                 d.innerHTML = v;
20930                 this.cleanUpPaste();
20931                 this.el.dom.value = d.innerHTML;
20932                 this.owner.fireEvent('push', this, v);
20933             }
20934         }
20935     },
20936
20937     // private
20938     deferFocus : function(){
20939         this.focus.defer(10, this);
20940     },
20941
20942     // doc'ed in Field
20943     focus : function(){
20944         if(this.win && !this.sourceEditMode){
20945             this.win.focus();
20946         }else{
20947             this.el.focus();
20948         }
20949     },
20950     
20951     assignDocWin: function()
20952     {
20953         var iframe = this.iframe;
20954         
20955          if(Roo.isIE){
20956             this.doc = iframe.contentWindow.document;
20957             this.win = iframe.contentWindow;
20958         } else {
20959 //            if (!Roo.get(this.frameId)) {
20960 //                return;
20961 //            }
20962 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20963 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20964             
20965             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20966                 return;
20967             }
20968             
20969             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20970             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20971         }
20972     },
20973     
20974     // private
20975     initEditor : function(){
20976         //console.log("INIT EDITOR");
20977         this.assignDocWin();
20978         
20979         
20980         
20981         this.doc.designMode="on";
20982         this.doc.open();
20983         this.doc.write(this.getDocMarkup());
20984         this.doc.close();
20985         
20986         var dbody = (this.doc.body || this.doc.documentElement);
20987         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20988         // this copies styles from the containing element into thsi one..
20989         // not sure why we need all of this..
20990         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20991         
20992         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20993         //ss['background-attachment'] = 'fixed'; // w3c
20994         dbody.bgProperties = 'fixed'; // ie
20995         //Roo.DomHelper.applyStyles(dbody, ss);
20996         Roo.EventManager.on(this.doc, {
20997             //'mousedown': this.onEditorEvent,
20998             'mouseup': this.onEditorEvent,
20999             'dblclick': this.onEditorEvent,
21000             'click': this.onEditorEvent,
21001             'keyup': this.onEditorEvent,
21002             buffer:100,
21003             scope: this
21004         });
21005         if(Roo.isGecko){
21006             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21007         }
21008         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21009             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21010         }
21011         this.initialized = true;
21012
21013         this.owner.fireEvent('initialize', this);
21014         this.pushValue();
21015     },
21016
21017     // private
21018     onDestroy : function(){
21019         
21020         
21021         
21022         if(this.rendered){
21023             
21024             //for (var i =0; i < this.toolbars.length;i++) {
21025             //    // fixme - ask toolbars for heights?
21026             //    this.toolbars[i].onDestroy();
21027            // }
21028             
21029             //this.wrap.dom.innerHTML = '';
21030             //this.wrap.remove();
21031         }
21032     },
21033
21034     // private
21035     onFirstFocus : function(){
21036         
21037         this.assignDocWin();
21038         
21039         
21040         this.activated = true;
21041          
21042     
21043         if(Roo.isGecko){ // prevent silly gecko errors
21044             this.win.focus();
21045             var s = this.win.getSelection();
21046             if(!s.focusNode || s.focusNode.nodeType != 3){
21047                 var r = s.getRangeAt(0);
21048                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21049                 r.collapse(true);
21050                 this.deferFocus();
21051             }
21052             try{
21053                 this.execCmd('useCSS', true);
21054                 this.execCmd('styleWithCSS', false);
21055             }catch(e){}
21056         }
21057         this.owner.fireEvent('activate', this);
21058     },
21059
21060     // private
21061     adjustFont: function(btn){
21062         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21063         //if(Roo.isSafari){ // safari
21064         //    adjust *= 2;
21065        // }
21066         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21067         if(Roo.isSafari){ // safari
21068             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21069             v =  (v < 10) ? 10 : v;
21070             v =  (v > 48) ? 48 : v;
21071             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21072             
21073         }
21074         
21075         
21076         v = Math.max(1, v+adjust);
21077         
21078         this.execCmd('FontSize', v  );
21079     },
21080
21081     onEditorEvent : function(e)
21082     {
21083         this.owner.fireEvent('editorevent', this, e);
21084       //  this.updateToolbar();
21085         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21086     },
21087
21088     insertTag : function(tg)
21089     {
21090         // could be a bit smarter... -> wrap the current selected tRoo..
21091         if (tg.toLowerCase() == 'span' ||
21092             tg.toLowerCase() == 'code' ||
21093             tg.toLowerCase() == 'sup' ||
21094             tg.toLowerCase() == 'sub' 
21095             ) {
21096             
21097             range = this.createRange(this.getSelection());
21098             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21099             wrappingNode.appendChild(range.extractContents());
21100             range.insertNode(wrappingNode);
21101
21102             return;
21103             
21104             
21105             
21106         }
21107         this.execCmd("formatblock",   tg);
21108         
21109     },
21110     
21111     insertText : function(txt)
21112     {
21113         
21114         
21115         var range = this.createRange();
21116         range.deleteContents();
21117                //alert(Sender.getAttribute('label'));
21118                
21119         range.insertNode(this.doc.createTextNode(txt));
21120     } ,
21121     
21122      
21123
21124     /**
21125      * Executes a Midas editor command on the editor document and performs necessary focus and
21126      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21127      * @param {String} cmd The Midas command
21128      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21129      */
21130     relayCmd : function(cmd, value){
21131         this.win.focus();
21132         this.execCmd(cmd, value);
21133         this.owner.fireEvent('editorevent', this);
21134         //this.updateToolbar();
21135         this.owner.deferFocus();
21136     },
21137
21138     /**
21139      * Executes a Midas editor command directly on the editor document.
21140      * For visual commands, you should use {@link #relayCmd} instead.
21141      * <b>This should only be called after the editor is initialized.</b>
21142      * @param {String} cmd The Midas command
21143      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21144      */
21145     execCmd : function(cmd, value){
21146         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21147         this.syncValue();
21148     },
21149  
21150  
21151    
21152     /**
21153      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21154      * to insert tRoo.
21155      * @param {String} text | dom node.. 
21156      */
21157     insertAtCursor : function(text)
21158     {
21159         
21160         if(!this.activated){
21161             return;
21162         }
21163         /*
21164         if(Roo.isIE){
21165             this.win.focus();
21166             var r = this.doc.selection.createRange();
21167             if(r){
21168                 r.collapse(true);
21169                 r.pasteHTML(text);
21170                 this.syncValue();
21171                 this.deferFocus();
21172             
21173             }
21174             return;
21175         }
21176         */
21177         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21178             this.win.focus();
21179             
21180             
21181             // from jquery ui (MIT licenced)
21182             var range, node;
21183             var win = this.win;
21184             
21185             if (win.getSelection && win.getSelection().getRangeAt) {
21186                 range = win.getSelection().getRangeAt(0);
21187                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21188                 range.insertNode(node);
21189             } else if (win.document.selection && win.document.selection.createRange) {
21190                 // no firefox support
21191                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21192                 win.document.selection.createRange().pasteHTML(txt);
21193             } else {
21194                 // no firefox support
21195                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21196                 this.execCmd('InsertHTML', txt);
21197             } 
21198             
21199             this.syncValue();
21200             
21201             this.deferFocus();
21202         }
21203     },
21204  // private
21205     mozKeyPress : function(e){
21206         if(e.ctrlKey){
21207             var c = e.getCharCode(), cmd;
21208           
21209             if(c > 0){
21210                 c = String.fromCharCode(c).toLowerCase();
21211                 switch(c){
21212                     case 'b':
21213                         cmd = 'bold';
21214                         break;
21215                     case 'i':
21216                         cmd = 'italic';
21217                         break;
21218                     
21219                     case 'u':
21220                         cmd = 'underline';
21221                         break;
21222                     
21223                     case 'v':
21224                         this.cleanUpPaste.defer(100, this);
21225                         return;
21226                         
21227                 }
21228                 if(cmd){
21229                     this.win.focus();
21230                     this.execCmd(cmd);
21231                     this.deferFocus();
21232                     e.preventDefault();
21233                 }
21234                 
21235             }
21236         }
21237     },
21238
21239     // private
21240     fixKeys : function(){ // load time branching for fastest keydown performance
21241         if(Roo.isIE){
21242             return function(e){
21243                 var k = e.getKey(), r;
21244                 if(k == e.TAB){
21245                     e.stopEvent();
21246                     r = this.doc.selection.createRange();
21247                     if(r){
21248                         r.collapse(true);
21249                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21250                         this.deferFocus();
21251                     }
21252                     return;
21253                 }
21254                 
21255                 if(k == e.ENTER){
21256                     r = this.doc.selection.createRange();
21257                     if(r){
21258                         var target = r.parentElement();
21259                         if(!target || target.tagName.toLowerCase() != 'li'){
21260                             e.stopEvent();
21261                             r.pasteHTML('<br />');
21262                             r.collapse(false);
21263                             r.select();
21264                         }
21265                     }
21266                 }
21267                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21268                     this.cleanUpPaste.defer(100, this);
21269                     return;
21270                 }
21271                 
21272                 
21273             };
21274         }else if(Roo.isOpera){
21275             return function(e){
21276                 var k = e.getKey();
21277                 if(k == e.TAB){
21278                     e.stopEvent();
21279                     this.win.focus();
21280                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21281                     this.deferFocus();
21282                 }
21283                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21284                     this.cleanUpPaste.defer(100, this);
21285                     return;
21286                 }
21287                 
21288             };
21289         }else if(Roo.isSafari){
21290             return function(e){
21291                 var k = e.getKey();
21292                 
21293                 if(k == e.TAB){
21294                     e.stopEvent();
21295                     this.execCmd('InsertText','\t');
21296                     this.deferFocus();
21297                     return;
21298                 }
21299                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21300                     this.cleanUpPaste.defer(100, this);
21301                     return;
21302                 }
21303                 
21304              };
21305         }
21306     }(),
21307     
21308     getAllAncestors: function()
21309     {
21310         var p = this.getSelectedNode();
21311         var a = [];
21312         if (!p) {
21313             a.push(p); // push blank onto stack..
21314             p = this.getParentElement();
21315         }
21316         
21317         
21318         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21319             a.push(p);
21320             p = p.parentNode;
21321         }
21322         a.push(this.doc.body);
21323         return a;
21324     },
21325     lastSel : false,
21326     lastSelNode : false,
21327     
21328     
21329     getSelection : function() 
21330     {
21331         this.assignDocWin();
21332         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21333     },
21334     
21335     getSelectedNode: function() 
21336     {
21337         // this may only work on Gecko!!!
21338         
21339         // should we cache this!!!!
21340         
21341         
21342         
21343          
21344         var range = this.createRange(this.getSelection()).cloneRange();
21345         
21346         if (Roo.isIE) {
21347             var parent = range.parentElement();
21348             while (true) {
21349                 var testRange = range.duplicate();
21350                 testRange.moveToElementText(parent);
21351                 if (testRange.inRange(range)) {
21352                     break;
21353                 }
21354                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21355                     break;
21356                 }
21357                 parent = parent.parentElement;
21358             }
21359             return parent;
21360         }
21361         
21362         // is ancestor a text element.
21363         var ac =  range.commonAncestorContainer;
21364         if (ac.nodeType == 3) {
21365             ac = ac.parentNode;
21366         }
21367         
21368         var ar = ac.childNodes;
21369          
21370         var nodes = [];
21371         var other_nodes = [];
21372         var has_other_nodes = false;
21373         for (var i=0;i<ar.length;i++) {
21374             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21375                 continue;
21376             }
21377             // fullly contained node.
21378             
21379             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21380                 nodes.push(ar[i]);
21381                 continue;
21382             }
21383             
21384             // probably selected..
21385             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21386                 other_nodes.push(ar[i]);
21387                 continue;
21388             }
21389             // outer..
21390             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21391                 continue;
21392             }
21393             
21394             
21395             has_other_nodes = true;
21396         }
21397         if (!nodes.length && other_nodes.length) {
21398             nodes= other_nodes;
21399         }
21400         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21401             return false;
21402         }
21403         
21404         return nodes[0];
21405     },
21406     createRange: function(sel)
21407     {
21408         // this has strange effects when using with 
21409         // top toolbar - not sure if it's a great idea.
21410         //this.editor.contentWindow.focus();
21411         if (typeof sel != "undefined") {
21412             try {
21413                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21414             } catch(e) {
21415                 return this.doc.createRange();
21416             }
21417         } else {
21418             return this.doc.createRange();
21419         }
21420     },
21421     getParentElement: function()
21422     {
21423         
21424         this.assignDocWin();
21425         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21426         
21427         var range = this.createRange(sel);
21428          
21429         try {
21430             var p = range.commonAncestorContainer;
21431             while (p.nodeType == 3) { // text node
21432                 p = p.parentNode;
21433             }
21434             return p;
21435         } catch (e) {
21436             return null;
21437         }
21438     
21439     },
21440     /***
21441      *
21442      * Range intersection.. the hard stuff...
21443      *  '-1' = before
21444      *  '0' = hits..
21445      *  '1' = after.
21446      *         [ -- selected range --- ]
21447      *   [fail]                        [fail]
21448      *
21449      *    basically..
21450      *      if end is before start or  hits it. fail.
21451      *      if start is after end or hits it fail.
21452      *
21453      *   if either hits (but other is outside. - then it's not 
21454      *   
21455      *    
21456      **/
21457     
21458     
21459     // @see http://www.thismuchiknow.co.uk/?p=64.
21460     rangeIntersectsNode : function(range, node)
21461     {
21462         var nodeRange = node.ownerDocument.createRange();
21463         try {
21464             nodeRange.selectNode(node);
21465         } catch (e) {
21466             nodeRange.selectNodeContents(node);
21467         }
21468     
21469         var rangeStartRange = range.cloneRange();
21470         rangeStartRange.collapse(true);
21471     
21472         var rangeEndRange = range.cloneRange();
21473         rangeEndRange.collapse(false);
21474     
21475         var nodeStartRange = nodeRange.cloneRange();
21476         nodeStartRange.collapse(true);
21477     
21478         var nodeEndRange = nodeRange.cloneRange();
21479         nodeEndRange.collapse(false);
21480     
21481         return rangeStartRange.compareBoundaryPoints(
21482                  Range.START_TO_START, nodeEndRange) == -1 &&
21483                rangeEndRange.compareBoundaryPoints(
21484                  Range.START_TO_START, nodeStartRange) == 1;
21485         
21486          
21487     },
21488     rangeCompareNode : function(range, node)
21489     {
21490         var nodeRange = node.ownerDocument.createRange();
21491         try {
21492             nodeRange.selectNode(node);
21493         } catch (e) {
21494             nodeRange.selectNodeContents(node);
21495         }
21496         
21497         
21498         range.collapse(true);
21499     
21500         nodeRange.collapse(true);
21501      
21502         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21503         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21504          
21505         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21506         
21507         var nodeIsBefore   =  ss == 1;
21508         var nodeIsAfter    = ee == -1;
21509         
21510         if (nodeIsBefore && nodeIsAfter) {
21511             return 0; // outer
21512         }
21513         if (!nodeIsBefore && nodeIsAfter) {
21514             return 1; //right trailed.
21515         }
21516         
21517         if (nodeIsBefore && !nodeIsAfter) {
21518             return 2;  // left trailed.
21519         }
21520         // fully contined.
21521         return 3;
21522     },
21523
21524     // private? - in a new class?
21525     cleanUpPaste :  function()
21526     {
21527         // cleans up the whole document..
21528         Roo.log('cleanuppaste');
21529         
21530         this.cleanUpChildren(this.doc.body);
21531         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21532         if (clean != this.doc.body.innerHTML) {
21533             this.doc.body.innerHTML = clean;
21534         }
21535         
21536     },
21537     
21538     cleanWordChars : function(input) {// change the chars to hex code
21539         var he = Roo.HtmlEditorCore;
21540         
21541         var output = input;
21542         Roo.each(he.swapCodes, function(sw) { 
21543             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21544             
21545             output = output.replace(swapper, sw[1]);
21546         });
21547         
21548         return output;
21549     },
21550     
21551     
21552     cleanUpChildren : function (n)
21553     {
21554         if (!n.childNodes.length) {
21555             return;
21556         }
21557         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21558            this.cleanUpChild(n.childNodes[i]);
21559         }
21560     },
21561     
21562     
21563         
21564     
21565     cleanUpChild : function (node)
21566     {
21567         var ed = this;
21568         //console.log(node);
21569         if (node.nodeName == "#text") {
21570             // clean up silly Windows -- stuff?
21571             return; 
21572         }
21573         if (node.nodeName == "#comment") {
21574             if (!this.allowComments) {
21575                 node.parentNode.removeChild(node);
21576             }
21577             // clean up silly Windows -- stuff?
21578             return; 
21579         }
21580         var lcname = node.tagName.toLowerCase();
21581         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21582         // whitelist of tags..
21583         
21584         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21585             // remove node.
21586             node.parentNode.removeChild(node);
21587             return;
21588             
21589         }
21590         
21591         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21592         
21593         // spans with no attributes - just remove them..
21594         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21595             remove_keep_children = true;
21596         }
21597         
21598         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21599         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21600         
21601         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21602         //    remove_keep_children = true;
21603         //}
21604         
21605         if (remove_keep_children) {
21606             this.cleanUpChildren(node);
21607             // inserts everything just before this node...
21608             while (node.childNodes.length) {
21609                 var cn = node.childNodes[0];
21610                 node.removeChild(cn);
21611                 node.parentNode.insertBefore(cn, node);
21612             }
21613             node.parentNode.removeChild(node);
21614             return;
21615         }
21616         
21617         if (!node.attributes || !node.attributes.length) {
21618             
21619           
21620             
21621             
21622             this.cleanUpChildren(node);
21623             return;
21624         }
21625         
21626         function cleanAttr(n,v)
21627         {
21628             
21629             if (v.match(/^\./) || v.match(/^\//)) {
21630                 return;
21631             }
21632             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21633                 return;
21634             }
21635             if (v.match(/^#/)) {
21636                 return;
21637             }
21638             if (v.match(/^\{/)) { // allow template editing.
21639                 return;
21640             }
21641 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21642             node.removeAttribute(n);
21643             
21644         }
21645         
21646         var cwhite = this.cwhite;
21647         var cblack = this.cblack;
21648             
21649         function cleanStyle(n,v)
21650         {
21651             if (v.match(/expression/)) { //XSS?? should we even bother..
21652                 node.removeAttribute(n);
21653                 return;
21654             }
21655             
21656             var parts = v.split(/;/);
21657             var clean = [];
21658             
21659             Roo.each(parts, function(p) {
21660                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21661                 if (!p.length) {
21662                     return true;
21663                 }
21664                 var l = p.split(':').shift().replace(/\s+/g,'');
21665                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21666                 
21667                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21668 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21669                     //node.removeAttribute(n);
21670                     return true;
21671                 }
21672                 //Roo.log()
21673                 // only allow 'c whitelisted system attributes'
21674                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21675 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21676                     //node.removeAttribute(n);
21677                     return true;
21678                 }
21679                 
21680                 
21681                  
21682                 
21683                 clean.push(p);
21684                 return true;
21685             });
21686             if (clean.length) { 
21687                 node.setAttribute(n, clean.join(';'));
21688             } else {
21689                 node.removeAttribute(n);
21690             }
21691             
21692         }
21693         
21694         
21695         for (var i = node.attributes.length-1; i > -1 ; i--) {
21696             var a = node.attributes[i];
21697             //console.log(a);
21698             
21699             if (a.name.toLowerCase().substr(0,2)=='on')  {
21700                 node.removeAttribute(a.name);
21701                 continue;
21702             }
21703             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21704                 node.removeAttribute(a.name);
21705                 continue;
21706             }
21707             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21708                 cleanAttr(a.name,a.value); // fixme..
21709                 continue;
21710             }
21711             if (a.name == 'style') {
21712                 cleanStyle(a.name,a.value);
21713                 continue;
21714             }
21715             /// clean up MS crap..
21716             // tecnically this should be a list of valid class'es..
21717             
21718             
21719             if (a.name == 'class') {
21720                 if (a.value.match(/^Mso/)) {
21721                     node.removeAttribute('class');
21722                 }
21723                 
21724                 if (a.value.match(/^body$/)) {
21725                     node.removeAttribute('class');
21726                 }
21727                 continue;
21728             }
21729             
21730             // style cleanup!?
21731             // class cleanup?
21732             
21733         }
21734         
21735         
21736         this.cleanUpChildren(node);
21737         
21738         
21739     },
21740     
21741     /**
21742      * Clean up MS wordisms...
21743      */
21744     cleanWord : function(node)
21745     {
21746         if (!node) {
21747             this.cleanWord(this.doc.body);
21748             return;
21749         }
21750         
21751         if(
21752                 node.nodeName == 'SPAN' &&
21753                 !node.hasAttributes() &&
21754                 node.childNodes.length == 1 &&
21755                 node.firstChild.nodeName == "#text"  
21756         ) {
21757             var textNode = node.firstChild;
21758             node.removeChild(textNode);
21759             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21760                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21761             }
21762             node.parentNode.insertBefore(textNode, node);
21763             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21764                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21765             }
21766             node.parentNode.removeChild(node);
21767         }
21768         
21769         if (node.nodeName == "#text") {
21770             // clean up silly Windows -- stuff?
21771             return; 
21772         }
21773         if (node.nodeName == "#comment") {
21774             node.parentNode.removeChild(node);
21775             // clean up silly Windows -- stuff?
21776             return; 
21777         }
21778         
21779         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21780             node.parentNode.removeChild(node);
21781             return;
21782         }
21783         //Roo.log(node.tagName);
21784         // remove - but keep children..
21785         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21786             //Roo.log('-- removed');
21787             while (node.childNodes.length) {
21788                 var cn = node.childNodes[0];
21789                 node.removeChild(cn);
21790                 node.parentNode.insertBefore(cn, node);
21791                 // move node to parent - and clean it..
21792                 this.cleanWord(cn);
21793             }
21794             node.parentNode.removeChild(node);
21795             /// no need to iterate chidlren = it's got none..
21796             //this.iterateChildren(node, this.cleanWord);
21797             return;
21798         }
21799         // clean styles
21800         if (node.className.length) {
21801             
21802             var cn = node.className.split(/\W+/);
21803             var cna = [];
21804             Roo.each(cn, function(cls) {
21805                 if (cls.match(/Mso[a-zA-Z]+/)) {
21806                     return;
21807                 }
21808                 cna.push(cls);
21809             });
21810             node.className = cna.length ? cna.join(' ') : '';
21811             if (!cna.length) {
21812                 node.removeAttribute("class");
21813             }
21814         }
21815         
21816         if (node.hasAttribute("lang")) {
21817             node.removeAttribute("lang");
21818         }
21819         
21820         if (node.hasAttribute("style")) {
21821             
21822             var styles = node.getAttribute("style").split(";");
21823             var nstyle = [];
21824             Roo.each(styles, function(s) {
21825                 if (!s.match(/:/)) {
21826                     return;
21827                 }
21828                 var kv = s.split(":");
21829                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21830                     return;
21831                 }
21832                 // what ever is left... we allow.
21833                 nstyle.push(s);
21834             });
21835             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21836             if (!nstyle.length) {
21837                 node.removeAttribute('style');
21838             }
21839         }
21840         this.iterateChildren(node, this.cleanWord);
21841         
21842         
21843         
21844     },
21845     /**
21846      * iterateChildren of a Node, calling fn each time, using this as the scole..
21847      * @param {DomNode} node node to iterate children of.
21848      * @param {Function} fn method of this class to call on each item.
21849      */
21850     iterateChildren : function(node, fn)
21851     {
21852         if (!node.childNodes.length) {
21853                 return;
21854         }
21855         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21856            fn.call(this, node.childNodes[i])
21857         }
21858     },
21859     
21860     
21861     /**
21862      * cleanTableWidths.
21863      *
21864      * Quite often pasting from word etc.. results in tables with column and widths.
21865      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21866      *
21867      */
21868     cleanTableWidths : function(node)
21869     {
21870          
21871          
21872         if (!node) {
21873             this.cleanTableWidths(this.doc.body);
21874             return;
21875         }
21876         
21877         // ignore list...
21878         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21879             return; 
21880         }
21881         Roo.log(node.tagName);
21882         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21883             this.iterateChildren(node, this.cleanTableWidths);
21884             return;
21885         }
21886         if (node.hasAttribute('width')) {
21887             node.removeAttribute('width');
21888         }
21889         
21890          
21891         if (node.hasAttribute("style")) {
21892             // pretty basic...
21893             
21894             var styles = node.getAttribute("style").split(";");
21895             var nstyle = [];
21896             Roo.each(styles, function(s) {
21897                 if (!s.match(/:/)) {
21898                     return;
21899                 }
21900                 var kv = s.split(":");
21901                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21902                     return;
21903                 }
21904                 // what ever is left... we allow.
21905                 nstyle.push(s);
21906             });
21907             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21908             if (!nstyle.length) {
21909                 node.removeAttribute('style');
21910             }
21911         }
21912         
21913         this.iterateChildren(node, this.cleanTableWidths);
21914         
21915         
21916     },
21917     
21918     
21919     
21920     
21921     domToHTML : function(currentElement, depth, nopadtext) {
21922         
21923         depth = depth || 0;
21924         nopadtext = nopadtext || false;
21925     
21926         if (!currentElement) {
21927             return this.domToHTML(this.doc.body);
21928         }
21929         
21930         //Roo.log(currentElement);
21931         var j;
21932         var allText = false;
21933         var nodeName = currentElement.nodeName;
21934         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21935         
21936         if  (nodeName == '#text') {
21937             
21938             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21939         }
21940         
21941         
21942         var ret = '';
21943         if (nodeName != 'BODY') {
21944              
21945             var i = 0;
21946             // Prints the node tagName, such as <A>, <IMG>, etc
21947             if (tagName) {
21948                 var attr = [];
21949                 for(i = 0; i < currentElement.attributes.length;i++) {
21950                     // quoting?
21951                     var aname = currentElement.attributes.item(i).name;
21952                     if (!currentElement.attributes.item(i).value.length) {
21953                         continue;
21954                     }
21955                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21956                 }
21957                 
21958                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21959             } 
21960             else {
21961                 
21962                 // eack
21963             }
21964         } else {
21965             tagName = false;
21966         }
21967         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21968             return ret;
21969         }
21970         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21971             nopadtext = true;
21972         }
21973         
21974         
21975         // Traverse the tree
21976         i = 0;
21977         var currentElementChild = currentElement.childNodes.item(i);
21978         var allText = true;
21979         var innerHTML  = '';
21980         lastnode = '';
21981         while (currentElementChild) {
21982             // Formatting code (indent the tree so it looks nice on the screen)
21983             var nopad = nopadtext;
21984             if (lastnode == 'SPAN') {
21985                 nopad  = true;
21986             }
21987             // text
21988             if  (currentElementChild.nodeName == '#text') {
21989                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21990                 toadd = nopadtext ? toadd : toadd.trim();
21991                 if (!nopad && toadd.length > 80) {
21992                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21993                 }
21994                 innerHTML  += toadd;
21995                 
21996                 i++;
21997                 currentElementChild = currentElement.childNodes.item(i);
21998                 lastNode = '';
21999                 continue;
22000             }
22001             allText = false;
22002             
22003             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22004                 
22005             // Recursively traverse the tree structure of the child node
22006             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22007             lastnode = currentElementChild.nodeName;
22008             i++;
22009             currentElementChild=currentElement.childNodes.item(i);
22010         }
22011         
22012         ret += innerHTML;
22013         
22014         if (!allText) {
22015                 // The remaining code is mostly for formatting the tree
22016             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22017         }
22018         
22019         
22020         if (tagName) {
22021             ret+= "</"+tagName+">";
22022         }
22023         return ret;
22024         
22025     },
22026         
22027     applyBlacklists : function()
22028     {
22029         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22030         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22031         
22032         this.white = [];
22033         this.black = [];
22034         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22035             if (b.indexOf(tag) > -1) {
22036                 return;
22037             }
22038             this.white.push(tag);
22039             
22040         }, this);
22041         
22042         Roo.each(w, function(tag) {
22043             if (b.indexOf(tag) > -1) {
22044                 return;
22045             }
22046             if (this.white.indexOf(tag) > -1) {
22047                 return;
22048             }
22049             this.white.push(tag);
22050             
22051         }, this);
22052         
22053         
22054         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22055             if (w.indexOf(tag) > -1) {
22056                 return;
22057             }
22058             this.black.push(tag);
22059             
22060         }, this);
22061         
22062         Roo.each(b, function(tag) {
22063             if (w.indexOf(tag) > -1) {
22064                 return;
22065             }
22066             if (this.black.indexOf(tag) > -1) {
22067                 return;
22068             }
22069             this.black.push(tag);
22070             
22071         }, this);
22072         
22073         
22074         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22075         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22076         
22077         this.cwhite = [];
22078         this.cblack = [];
22079         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22080             if (b.indexOf(tag) > -1) {
22081                 return;
22082             }
22083             this.cwhite.push(tag);
22084             
22085         }, this);
22086         
22087         Roo.each(w, function(tag) {
22088             if (b.indexOf(tag) > -1) {
22089                 return;
22090             }
22091             if (this.cwhite.indexOf(tag) > -1) {
22092                 return;
22093             }
22094             this.cwhite.push(tag);
22095             
22096         }, this);
22097         
22098         
22099         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22100             if (w.indexOf(tag) > -1) {
22101                 return;
22102             }
22103             this.cblack.push(tag);
22104             
22105         }, this);
22106         
22107         Roo.each(b, function(tag) {
22108             if (w.indexOf(tag) > -1) {
22109                 return;
22110             }
22111             if (this.cblack.indexOf(tag) > -1) {
22112                 return;
22113             }
22114             this.cblack.push(tag);
22115             
22116         }, this);
22117     },
22118     
22119     setStylesheets : function(stylesheets)
22120     {
22121         if(typeof(stylesheets) == 'string'){
22122             Roo.get(this.iframe.contentDocument.head).createChild({
22123                 tag : 'link',
22124                 rel : 'stylesheet',
22125                 type : 'text/css',
22126                 href : stylesheets
22127             });
22128             
22129             return;
22130         }
22131         var _this = this;
22132      
22133         Roo.each(stylesheets, function(s) {
22134             if(!s.length){
22135                 return;
22136             }
22137             
22138             Roo.get(_this.iframe.contentDocument.head).createChild({
22139                 tag : 'link',
22140                 rel : 'stylesheet',
22141                 type : 'text/css',
22142                 href : s
22143             });
22144         });
22145
22146         
22147     },
22148     
22149     removeStylesheets : function()
22150     {
22151         var _this = this;
22152         
22153         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22154             s.remove();
22155         });
22156     },
22157     
22158     setStyle : function(style)
22159     {
22160         Roo.get(this.iframe.contentDocument.head).createChild({
22161             tag : 'style',
22162             type : 'text/css',
22163             html : style
22164         });
22165
22166         return;
22167     }
22168     
22169     // hide stuff that is not compatible
22170     /**
22171      * @event blur
22172      * @hide
22173      */
22174     /**
22175      * @event change
22176      * @hide
22177      */
22178     /**
22179      * @event focus
22180      * @hide
22181      */
22182     /**
22183      * @event specialkey
22184      * @hide
22185      */
22186     /**
22187      * @cfg {String} fieldClass @hide
22188      */
22189     /**
22190      * @cfg {String} focusClass @hide
22191      */
22192     /**
22193      * @cfg {String} autoCreate @hide
22194      */
22195     /**
22196      * @cfg {String} inputType @hide
22197      */
22198     /**
22199      * @cfg {String} invalidClass @hide
22200      */
22201     /**
22202      * @cfg {String} invalidText @hide
22203      */
22204     /**
22205      * @cfg {String} msgFx @hide
22206      */
22207     /**
22208      * @cfg {String} validateOnBlur @hide
22209      */
22210 });
22211
22212 Roo.HtmlEditorCore.white = [
22213         'area', 'br', 'img', 'input', 'hr', 'wbr',
22214         
22215        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22216        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22217        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22218        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22219        'table',   'ul',         'xmp', 
22220        
22221        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22222       'thead',   'tr', 
22223      
22224       'dir', 'menu', 'ol', 'ul', 'dl',
22225        
22226       'embed',  'object'
22227 ];
22228
22229
22230 Roo.HtmlEditorCore.black = [
22231     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22232         'applet', // 
22233         'base',   'basefont', 'bgsound', 'blink',  'body', 
22234         'frame',  'frameset', 'head',    'html',   'ilayer', 
22235         'iframe', 'layer',  'link',     'meta',    'object',   
22236         'script', 'style' ,'title',  'xml' // clean later..
22237 ];
22238 Roo.HtmlEditorCore.clean = [
22239     'script', 'style', 'title', 'xml'
22240 ];
22241 Roo.HtmlEditorCore.remove = [
22242     'font'
22243 ];
22244 // attributes..
22245
22246 Roo.HtmlEditorCore.ablack = [
22247     'on'
22248 ];
22249     
22250 Roo.HtmlEditorCore.aclean = [ 
22251     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22252 ];
22253
22254 // protocols..
22255 Roo.HtmlEditorCore.pwhite= [
22256         'http',  'https',  'mailto'
22257 ];
22258
22259 // white listed style attributes.
22260 Roo.HtmlEditorCore.cwhite= [
22261       //  'text-align', /// default is to allow most things..
22262       
22263          
22264 //        'font-size'//??
22265 ];
22266
22267 // black listed style attributes.
22268 Roo.HtmlEditorCore.cblack= [
22269       //  'font-size' -- this can be set by the project 
22270 ];
22271
22272
22273 Roo.HtmlEditorCore.swapCodes   =[ 
22274     [    8211, "&#8211;" ], 
22275     [    8212, "&#8212;" ], 
22276     [    8216,  "'" ],  
22277     [    8217, "'" ],  
22278     [    8220, '"' ],  
22279     [    8221, '"' ],  
22280     [    8226, "*" ],  
22281     [    8230, "..." ]
22282 ]; 
22283
22284     //<script type="text/javascript">
22285
22286 /*
22287  * Ext JS Library 1.1.1
22288  * Copyright(c) 2006-2007, Ext JS, LLC.
22289  * Licence LGPL
22290  * 
22291  */
22292  
22293  
22294 Roo.form.HtmlEditor = function(config){
22295     
22296     
22297     
22298     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22299     
22300     if (!this.toolbars) {
22301         this.toolbars = [];
22302     }
22303     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22304     
22305     
22306 };
22307
22308 /**
22309  * @class Roo.form.HtmlEditor
22310  * @extends Roo.form.Field
22311  * Provides a lightweight HTML Editor component.
22312  *
22313  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22314  * 
22315  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22316  * supported by this editor.</b><br/><br/>
22317  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22318  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22319  */
22320 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22321     /**
22322      * @cfg {Boolean} clearUp
22323      */
22324     clearUp : true,
22325       /**
22326      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22327      */
22328     toolbars : false,
22329    
22330      /**
22331      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22332      *                        Roo.resizable.
22333      */
22334     resizable : false,
22335      /**
22336      * @cfg {Number} height (in pixels)
22337      */   
22338     height: 300,
22339    /**
22340      * @cfg {Number} width (in pixels)
22341      */   
22342     width: 500,
22343     
22344     /**
22345      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22346      * 
22347      */
22348     stylesheets: false,
22349     
22350     
22351      /**
22352      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22353      * 
22354      */
22355     cblack: false,
22356     /**
22357      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22358      * 
22359      */
22360     cwhite: false,
22361     
22362      /**
22363      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22364      * 
22365      */
22366     black: false,
22367     /**
22368      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22369      * 
22370      */
22371     white: false,
22372     /**
22373      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22374      */
22375     allowComments: false,
22376     
22377     // id of frame..
22378     frameId: false,
22379     
22380     // private properties
22381     validationEvent : false,
22382     deferHeight: true,
22383     initialized : false,
22384     activated : false,
22385     
22386     onFocus : Roo.emptyFn,
22387     iframePad:3,
22388     hideMode:'offsets',
22389     
22390     actionMode : 'container', // defaults to hiding it...
22391     
22392     defaultAutoCreate : { // modified by initCompnoent..
22393         tag: "textarea",
22394         style:"width:500px;height:300px;",
22395         autocomplete: "new-password"
22396     },
22397
22398     // private
22399     initComponent : function(){
22400         this.addEvents({
22401             /**
22402              * @event initialize
22403              * Fires when the editor is fully initialized (including the iframe)
22404              * @param {HtmlEditor} this
22405              */
22406             initialize: true,
22407             /**
22408              * @event activate
22409              * Fires when the editor is first receives the focus. Any insertion must wait
22410              * until after this event.
22411              * @param {HtmlEditor} this
22412              */
22413             activate: true,
22414              /**
22415              * @event beforesync
22416              * Fires before the textarea is updated with content from the editor iframe. Return false
22417              * to cancel the sync.
22418              * @param {HtmlEditor} this
22419              * @param {String} html
22420              */
22421             beforesync: true,
22422              /**
22423              * @event beforepush
22424              * Fires before the iframe editor is updated with content from the textarea. Return false
22425              * to cancel the push.
22426              * @param {HtmlEditor} this
22427              * @param {String} html
22428              */
22429             beforepush: true,
22430              /**
22431              * @event sync
22432              * Fires when the textarea is updated with content from the editor iframe.
22433              * @param {HtmlEditor} this
22434              * @param {String} html
22435              */
22436             sync: true,
22437              /**
22438              * @event push
22439              * Fires when the iframe editor is updated with content from the textarea.
22440              * @param {HtmlEditor} this
22441              * @param {String} html
22442              */
22443             push: true,
22444              /**
22445              * @event editmodechange
22446              * Fires when the editor switches edit modes
22447              * @param {HtmlEditor} this
22448              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22449              */
22450             editmodechange: true,
22451             /**
22452              * @event editorevent
22453              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22454              * @param {HtmlEditor} this
22455              */
22456             editorevent: true,
22457             /**
22458              * @event firstfocus
22459              * Fires when on first focus - needed by toolbars..
22460              * @param {HtmlEditor} this
22461              */
22462             firstfocus: true,
22463             /**
22464              * @event autosave
22465              * Auto save the htmlEditor value as a file into Events
22466              * @param {HtmlEditor} this
22467              */
22468             autosave: true,
22469             /**
22470              * @event savedpreview
22471              * preview the saved version of htmlEditor
22472              * @param {HtmlEditor} this
22473              */
22474             savedpreview: true,
22475             
22476             /**
22477             * @event stylesheetsclick
22478             * Fires when press the Sytlesheets button
22479             * @param {Roo.HtmlEditorCore} this
22480             */
22481             stylesheetsclick: true
22482         });
22483         this.defaultAutoCreate =  {
22484             tag: "textarea",
22485             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22486             autocomplete: "new-password"
22487         };
22488     },
22489
22490     /**
22491      * Protected method that will not generally be called directly. It
22492      * is called when the editor creates its toolbar. Override this method if you need to
22493      * add custom toolbar buttons.
22494      * @param {HtmlEditor} editor
22495      */
22496     createToolbar : function(editor){
22497         Roo.log("create toolbars");
22498         if (!editor.toolbars || !editor.toolbars.length) {
22499             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22500         }
22501         
22502         for (var i =0 ; i < editor.toolbars.length;i++) {
22503             editor.toolbars[i] = Roo.factory(
22504                     typeof(editor.toolbars[i]) == 'string' ?
22505                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22506                 Roo.form.HtmlEditor);
22507             editor.toolbars[i].init(editor);
22508         }
22509          
22510         
22511     },
22512
22513      
22514     // private
22515     onRender : function(ct, position)
22516     {
22517         var _t = this;
22518         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22519         
22520         this.wrap = this.el.wrap({
22521             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22522         });
22523         
22524         this.editorcore.onRender(ct, position);
22525          
22526         if (this.resizable) {
22527             this.resizeEl = new Roo.Resizable(this.wrap, {
22528                 pinned : true,
22529                 wrap: true,
22530                 dynamic : true,
22531                 minHeight : this.height,
22532                 height: this.height,
22533                 handles : this.resizable,
22534                 width: this.width,
22535                 listeners : {
22536                     resize : function(r, w, h) {
22537                         _t.onResize(w,h); // -something
22538                     }
22539                 }
22540             });
22541             
22542         }
22543         this.createToolbar(this);
22544        
22545         
22546         if(!this.width){
22547             this.setSize(this.wrap.getSize());
22548         }
22549         if (this.resizeEl) {
22550             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22551             // should trigger onReize..
22552         }
22553         
22554         this.keyNav = new Roo.KeyNav(this.el, {
22555             
22556             "tab" : function(e){
22557                 e.preventDefault();
22558                 
22559                 var value = this.getValue();
22560                 
22561                 var start = this.el.dom.selectionStart;
22562                 var end = this.el.dom.selectionEnd;
22563                 
22564                 if(!e.shiftKey){
22565                     
22566                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22567                     this.el.dom.setSelectionRange(end + 1, end + 1);
22568                     return;
22569                 }
22570                 
22571                 var f = value.substring(0, start).split("\t");
22572                 
22573                 if(f.pop().length != 0){
22574                     return;
22575                 }
22576                 
22577                 this.setValue(f.join("\t") + value.substring(end));
22578                 this.el.dom.setSelectionRange(start - 1, start - 1);
22579                 
22580             },
22581             
22582             "home" : function(e){
22583                 e.preventDefault();
22584                 
22585                 var curr = this.el.dom.selectionStart;
22586                 var lines = this.getValue().split("\n");
22587                 
22588                 if(!lines.length){
22589                     return;
22590                 }
22591                 
22592                 if(e.ctrlKey){
22593                     this.el.dom.setSelectionRange(0, 0);
22594                     return;
22595                 }
22596                 
22597                 var pos = 0;
22598                 
22599                 for (var i = 0; i < lines.length;i++) {
22600                     pos += lines[i].length;
22601                     
22602                     if(i != 0){
22603                         pos += 1;
22604                     }
22605                     
22606                     if(pos < curr){
22607                         continue;
22608                     }
22609                     
22610                     pos -= lines[i].length;
22611                     
22612                     break;
22613                 }
22614                 
22615                 if(!e.shiftKey){
22616                     this.el.dom.setSelectionRange(pos, pos);
22617                     return;
22618                 }
22619                 
22620                 this.el.dom.selectionStart = pos;
22621                 this.el.dom.selectionEnd = curr;
22622             },
22623             
22624             "end" : function(e){
22625                 e.preventDefault();
22626                 
22627                 var curr = this.el.dom.selectionStart;
22628                 var lines = this.getValue().split("\n");
22629                 
22630                 if(!lines.length){
22631                     return;
22632                 }
22633                 
22634                 if(e.ctrlKey){
22635                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22636                     return;
22637                 }
22638                 
22639                 var pos = 0;
22640                 
22641                 for (var i = 0; i < lines.length;i++) {
22642                     
22643                     pos += lines[i].length;
22644                     
22645                     if(i != 0){
22646                         pos += 1;
22647                     }
22648                     
22649                     if(pos < curr){
22650                         continue;
22651                     }
22652                     
22653                     break;
22654                 }
22655                 
22656                 if(!e.shiftKey){
22657                     this.el.dom.setSelectionRange(pos, pos);
22658                     return;
22659                 }
22660                 
22661                 this.el.dom.selectionStart = curr;
22662                 this.el.dom.selectionEnd = pos;
22663             },
22664
22665             scope : this,
22666
22667             doRelay : function(foo, bar, hname){
22668                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22669             },
22670
22671             forceKeyDown: true
22672         });
22673         
22674 //        if(this.autosave && this.w){
22675 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22676 //        }
22677     },
22678
22679     // private
22680     onResize : function(w, h)
22681     {
22682         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22683         var ew = false;
22684         var eh = false;
22685         
22686         if(this.el ){
22687             if(typeof w == 'number'){
22688                 var aw = w - this.wrap.getFrameWidth('lr');
22689                 this.el.setWidth(this.adjustWidth('textarea', aw));
22690                 ew = aw;
22691             }
22692             if(typeof h == 'number'){
22693                 var tbh = 0;
22694                 for (var i =0; i < this.toolbars.length;i++) {
22695                     // fixme - ask toolbars for heights?
22696                     tbh += this.toolbars[i].tb.el.getHeight();
22697                     if (this.toolbars[i].footer) {
22698                         tbh += this.toolbars[i].footer.el.getHeight();
22699                     }
22700                 }
22701                 
22702                 
22703                 
22704                 
22705                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22706                 ah -= 5; // knock a few pixes off for look..
22707 //                Roo.log(ah);
22708                 this.el.setHeight(this.adjustWidth('textarea', ah));
22709                 var eh = ah;
22710             }
22711         }
22712         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22713         this.editorcore.onResize(ew,eh);
22714         
22715     },
22716
22717     /**
22718      * Toggles the editor between standard and source edit mode.
22719      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22720      */
22721     toggleSourceEdit : function(sourceEditMode)
22722     {
22723         this.editorcore.toggleSourceEdit(sourceEditMode);
22724         
22725         if(this.editorcore.sourceEditMode){
22726             Roo.log('editor - showing textarea');
22727             
22728 //            Roo.log('in');
22729 //            Roo.log(this.syncValue());
22730             this.editorcore.syncValue();
22731             this.el.removeClass('x-hidden');
22732             this.el.dom.removeAttribute('tabIndex');
22733             this.el.focus();
22734             
22735             for (var i = 0; i < this.toolbars.length; i++) {
22736                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22737                     this.toolbars[i].tb.hide();
22738                     this.toolbars[i].footer.hide();
22739                 }
22740             }
22741             
22742         }else{
22743             Roo.log('editor - hiding textarea');
22744 //            Roo.log('out')
22745 //            Roo.log(this.pushValue()); 
22746             this.editorcore.pushValue();
22747             
22748             this.el.addClass('x-hidden');
22749             this.el.dom.setAttribute('tabIndex', -1);
22750             
22751             for (var i = 0; i < this.toolbars.length; i++) {
22752                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22753                     this.toolbars[i].tb.show();
22754                     this.toolbars[i].footer.show();
22755                 }
22756             }
22757             
22758             //this.deferFocus();
22759         }
22760         
22761         this.setSize(this.wrap.getSize());
22762         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22763         
22764         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22765     },
22766  
22767     // private (for BoxComponent)
22768     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22769
22770     // private (for BoxComponent)
22771     getResizeEl : function(){
22772         return this.wrap;
22773     },
22774
22775     // private (for BoxComponent)
22776     getPositionEl : function(){
22777         return this.wrap;
22778     },
22779
22780     // private
22781     initEvents : function(){
22782         this.originalValue = this.getValue();
22783     },
22784
22785     /**
22786      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22787      * @method
22788      */
22789     markInvalid : Roo.emptyFn,
22790     /**
22791      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22792      * @method
22793      */
22794     clearInvalid : Roo.emptyFn,
22795
22796     setValue : function(v){
22797         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22798         this.editorcore.pushValue();
22799     },
22800
22801      
22802     // private
22803     deferFocus : function(){
22804         this.focus.defer(10, this);
22805     },
22806
22807     // doc'ed in Field
22808     focus : function(){
22809         this.editorcore.focus();
22810         
22811     },
22812       
22813
22814     // private
22815     onDestroy : function(){
22816         
22817         
22818         
22819         if(this.rendered){
22820             
22821             for (var i =0; i < this.toolbars.length;i++) {
22822                 // fixme - ask toolbars for heights?
22823                 this.toolbars[i].onDestroy();
22824             }
22825             
22826             this.wrap.dom.innerHTML = '';
22827             this.wrap.remove();
22828         }
22829     },
22830
22831     // private
22832     onFirstFocus : function(){
22833         //Roo.log("onFirstFocus");
22834         this.editorcore.onFirstFocus();
22835          for (var i =0; i < this.toolbars.length;i++) {
22836             this.toolbars[i].onFirstFocus();
22837         }
22838         
22839     },
22840     
22841     // private
22842     syncValue : function()
22843     {
22844         this.editorcore.syncValue();
22845     },
22846     
22847     pushValue : function()
22848     {
22849         this.editorcore.pushValue();
22850     },
22851     
22852     setStylesheets : function(stylesheets)
22853     {
22854         this.editorcore.setStylesheets(stylesheets);
22855     },
22856     
22857     removeStylesheets : function()
22858     {
22859         this.editorcore.removeStylesheets();
22860     }
22861      
22862     
22863     // hide stuff that is not compatible
22864     /**
22865      * @event blur
22866      * @hide
22867      */
22868     /**
22869      * @event change
22870      * @hide
22871      */
22872     /**
22873      * @event focus
22874      * @hide
22875      */
22876     /**
22877      * @event specialkey
22878      * @hide
22879      */
22880     /**
22881      * @cfg {String} fieldClass @hide
22882      */
22883     /**
22884      * @cfg {String} focusClass @hide
22885      */
22886     /**
22887      * @cfg {String} autoCreate @hide
22888      */
22889     /**
22890      * @cfg {String} inputType @hide
22891      */
22892     /**
22893      * @cfg {String} invalidClass @hide
22894      */
22895     /**
22896      * @cfg {String} invalidText @hide
22897      */
22898     /**
22899      * @cfg {String} msgFx @hide
22900      */
22901     /**
22902      * @cfg {String} validateOnBlur @hide
22903      */
22904 });
22905  
22906     // <script type="text/javascript">
22907 /*
22908  * Based on
22909  * Ext JS Library 1.1.1
22910  * Copyright(c) 2006-2007, Ext JS, LLC.
22911  *  
22912  
22913  */
22914
22915 /**
22916  * @class Roo.form.HtmlEditorToolbar1
22917  * Basic Toolbar
22918  * 
22919  * Usage:
22920  *
22921  new Roo.form.HtmlEditor({
22922     ....
22923     toolbars : [
22924         new Roo.form.HtmlEditorToolbar1({
22925             disable : { fonts: 1 , format: 1, ..., ... , ...],
22926             btns : [ .... ]
22927         })
22928     }
22929      
22930  * 
22931  * @cfg {Object} disable List of elements to disable..
22932  * @cfg {Array} btns List of additional buttons.
22933  * 
22934  * 
22935  * NEEDS Extra CSS? 
22936  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22937  */
22938  
22939 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22940 {
22941     
22942     Roo.apply(this, config);
22943     
22944     // default disabled, based on 'good practice'..
22945     this.disable = this.disable || {};
22946     Roo.applyIf(this.disable, {
22947         fontSize : true,
22948         colors : true,
22949         specialElements : true
22950     });
22951     
22952     
22953     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22954     // dont call parent... till later.
22955 }
22956
22957 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22958     
22959     tb: false,
22960     
22961     rendered: false,
22962     
22963     editor : false,
22964     editorcore : false,
22965     /**
22966      * @cfg {Object} disable  List of toolbar elements to disable
22967          
22968      */
22969     disable : false,
22970     
22971     
22972      /**
22973      * @cfg {String} createLinkText The default text for the create link prompt
22974      */
22975     createLinkText : 'Please enter the URL for the link:',
22976     /**
22977      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22978      */
22979     defaultLinkValue : 'http:/'+'/',
22980    
22981     
22982       /**
22983      * @cfg {Array} fontFamilies An array of available font families
22984      */
22985     fontFamilies : [
22986         'Arial',
22987         'Courier New',
22988         'Tahoma',
22989         'Times New Roman',
22990         'Verdana'
22991     ],
22992     
22993     specialChars : [
22994            "&#169;",
22995           "&#174;",     
22996           "&#8482;",    
22997           "&#163;" ,    
22998          // "&#8212;",    
22999           "&#8230;",    
23000           "&#247;" ,    
23001         //  "&#225;" ,     ?? a acute?
23002            "&#8364;"    , //Euro
23003        //   "&#8220;"    ,
23004         //  "&#8221;"    ,
23005         //  "&#8226;"    ,
23006           "&#176;"  //   , // degrees
23007
23008          // "&#233;"     , // e ecute
23009          // "&#250;"     , // u ecute?
23010     ],
23011     
23012     specialElements : [
23013         {
23014             text: "Insert Table",
23015             xtype: 'MenuItem',
23016             xns : Roo.Menu,
23017             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23018                 
23019         },
23020         {    
23021             text: "Insert Image",
23022             xtype: 'MenuItem',
23023             xns : Roo.Menu,
23024             ihtml : '<img src="about:blank"/>'
23025             
23026         }
23027         
23028          
23029     ],
23030     
23031     
23032     inputElements : [ 
23033             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23034             "input:submit", "input:button", "select", "textarea", "label" ],
23035     formats : [
23036         ["p"] ,  
23037         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23038         ["pre"],[ "code"], 
23039         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23040         ['div'],['span'],
23041         ['sup'],['sub']
23042     ],
23043     
23044     cleanStyles : [
23045         "font-size"
23046     ],
23047      /**
23048      * @cfg {String} defaultFont default font to use.
23049      */
23050     defaultFont: 'tahoma',
23051    
23052     fontSelect : false,
23053     
23054     
23055     formatCombo : false,
23056     
23057     init : function(editor)
23058     {
23059         this.editor = editor;
23060         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23061         var editorcore = this.editorcore;
23062         
23063         var _t = this;
23064         
23065         var fid = editorcore.frameId;
23066         var etb = this;
23067         function btn(id, toggle, handler){
23068             var xid = fid + '-'+ id ;
23069             return {
23070                 id : xid,
23071                 cmd : id,
23072                 cls : 'x-btn-icon x-edit-'+id,
23073                 enableToggle:toggle !== false,
23074                 scope: _t, // was editor...
23075                 handler:handler||_t.relayBtnCmd,
23076                 clickEvent:'mousedown',
23077                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23078                 tabIndex:-1
23079             };
23080         }
23081         
23082         
23083         
23084         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23085         this.tb = tb;
23086          // stop form submits
23087         tb.el.on('click', function(e){
23088             e.preventDefault(); // what does this do?
23089         });
23090
23091         if(!this.disable.font) { // && !Roo.isSafari){
23092             /* why no safari for fonts 
23093             editor.fontSelect = tb.el.createChild({
23094                 tag:'select',
23095                 tabIndex: -1,
23096                 cls:'x-font-select',
23097                 html: this.createFontOptions()
23098             });
23099             
23100             editor.fontSelect.on('change', function(){
23101                 var font = editor.fontSelect.dom.value;
23102                 editor.relayCmd('fontname', font);
23103                 editor.deferFocus();
23104             }, editor);
23105             
23106             tb.add(
23107                 editor.fontSelect.dom,
23108                 '-'
23109             );
23110             */
23111             
23112         };
23113         if(!this.disable.formats){
23114             this.formatCombo = new Roo.form.ComboBox({
23115                 store: new Roo.data.SimpleStore({
23116                     id : 'tag',
23117                     fields: ['tag'],
23118                     data : this.formats // from states.js
23119                 }),
23120                 blockFocus : true,
23121                 name : '',
23122                 //autoCreate : {tag: "div",  size: "20"},
23123                 displayField:'tag',
23124                 typeAhead: false,
23125                 mode: 'local',
23126                 editable : false,
23127                 triggerAction: 'all',
23128                 emptyText:'Add tag',
23129                 selectOnFocus:true,
23130                 width:135,
23131                 listeners : {
23132                     'select': function(c, r, i) {
23133                         editorcore.insertTag(r.get('tag'));
23134                         editor.focus();
23135                     }
23136                 }
23137
23138             });
23139             tb.addField(this.formatCombo);
23140             
23141         }
23142         
23143         if(!this.disable.format){
23144             tb.add(
23145                 btn('bold'),
23146                 btn('italic'),
23147                 btn('underline'),
23148                 btn('strikethrough')
23149             );
23150         };
23151         if(!this.disable.fontSize){
23152             tb.add(
23153                 '-',
23154                 
23155                 
23156                 btn('increasefontsize', false, editorcore.adjustFont),
23157                 btn('decreasefontsize', false, editorcore.adjustFont)
23158             );
23159         };
23160         
23161         
23162         if(!this.disable.colors){
23163             tb.add(
23164                 '-', {
23165                     id:editorcore.frameId +'-forecolor',
23166                     cls:'x-btn-icon x-edit-forecolor',
23167                     clickEvent:'mousedown',
23168                     tooltip: this.buttonTips['forecolor'] || undefined,
23169                     tabIndex:-1,
23170                     menu : new Roo.menu.ColorMenu({
23171                         allowReselect: true,
23172                         focus: Roo.emptyFn,
23173                         value:'000000',
23174                         plain:true,
23175                         selectHandler: function(cp, color){
23176                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23177                             editor.deferFocus();
23178                         },
23179                         scope: editorcore,
23180                         clickEvent:'mousedown'
23181                     })
23182                 }, {
23183                     id:editorcore.frameId +'backcolor',
23184                     cls:'x-btn-icon x-edit-backcolor',
23185                     clickEvent:'mousedown',
23186                     tooltip: this.buttonTips['backcolor'] || undefined,
23187                     tabIndex:-1,
23188                     menu : new Roo.menu.ColorMenu({
23189                         focus: Roo.emptyFn,
23190                         value:'FFFFFF',
23191                         plain:true,
23192                         allowReselect: true,
23193                         selectHandler: function(cp, color){
23194                             if(Roo.isGecko){
23195                                 editorcore.execCmd('useCSS', false);
23196                                 editorcore.execCmd('hilitecolor', color);
23197                                 editorcore.execCmd('useCSS', true);
23198                                 editor.deferFocus();
23199                             }else{
23200                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23201                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23202                                 editor.deferFocus();
23203                             }
23204                         },
23205                         scope:editorcore,
23206                         clickEvent:'mousedown'
23207                     })
23208                 }
23209             );
23210         };
23211         // now add all the items...
23212         
23213
23214         if(!this.disable.alignments){
23215             tb.add(
23216                 '-',
23217                 btn('justifyleft'),
23218                 btn('justifycenter'),
23219                 btn('justifyright')
23220             );
23221         };
23222
23223         //if(!Roo.isSafari){
23224             if(!this.disable.links){
23225                 tb.add(
23226                     '-',
23227                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23228                 );
23229             };
23230
23231             if(!this.disable.lists){
23232                 tb.add(
23233                     '-',
23234                     btn('insertorderedlist'),
23235                     btn('insertunorderedlist')
23236                 );
23237             }
23238             if(!this.disable.sourceEdit){
23239                 tb.add(
23240                     '-',
23241                     btn('sourceedit', true, function(btn){
23242                         this.toggleSourceEdit(btn.pressed);
23243                     })
23244                 );
23245             }
23246         //}
23247         
23248         var smenu = { };
23249         // special menu.. - needs to be tidied up..
23250         if (!this.disable.special) {
23251             smenu = {
23252                 text: "&#169;",
23253                 cls: 'x-edit-none',
23254                 
23255                 menu : {
23256                     items : []
23257                 }
23258             };
23259             for (var i =0; i < this.specialChars.length; i++) {
23260                 smenu.menu.items.push({
23261                     
23262                     html: this.specialChars[i],
23263                     handler: function(a,b) {
23264                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23265                         //editor.insertAtCursor(a.html);
23266                         
23267                     },
23268                     tabIndex:-1
23269                 });
23270             }
23271             
23272             
23273             tb.add(smenu);
23274             
23275             
23276         }
23277         
23278         var cmenu = { };
23279         if (!this.disable.cleanStyles) {
23280             cmenu = {
23281                 cls: 'x-btn-icon x-btn-clear',
23282                 
23283                 menu : {
23284                     items : []
23285                 }
23286             };
23287             for (var i =0; i < this.cleanStyles.length; i++) {
23288                 cmenu.menu.items.push({
23289                     actiontype : this.cleanStyles[i],
23290                     html: 'Remove ' + this.cleanStyles[i],
23291                     handler: function(a,b) {
23292 //                        Roo.log(a);
23293 //                        Roo.log(b);
23294                         var c = Roo.get(editorcore.doc.body);
23295                         c.select('[style]').each(function(s) {
23296                             s.dom.style.removeProperty(a.actiontype);
23297                         });
23298                         editorcore.syncValue();
23299                     },
23300                     tabIndex:-1
23301                 });
23302             }
23303              cmenu.menu.items.push({
23304                 actiontype : 'tablewidths',
23305                 html: 'Remove Table Widths',
23306                 handler: function(a,b) {
23307                     editorcore.cleanTableWidths();
23308                     editorcore.syncValue();
23309                 },
23310                 tabIndex:-1
23311             });
23312             cmenu.menu.items.push({
23313                 actiontype : 'word',
23314                 html: 'Remove MS Word Formating',
23315                 handler: function(a,b) {
23316                     editorcore.cleanWord();
23317                     editorcore.syncValue();
23318                 },
23319                 tabIndex:-1
23320             });
23321             
23322             cmenu.menu.items.push({
23323                 actiontype : 'all',
23324                 html: 'Remove All Styles',
23325                 handler: function(a,b) {
23326                     
23327                     var c = Roo.get(editorcore.doc.body);
23328                     c.select('[style]').each(function(s) {
23329                         s.dom.removeAttribute('style');
23330                     });
23331                     editorcore.syncValue();
23332                 },
23333                 tabIndex:-1
23334             });
23335             
23336             cmenu.menu.items.push({
23337                 actiontype : 'all',
23338                 html: 'Remove All CSS Classes',
23339                 handler: function(a,b) {
23340                     
23341                     var c = Roo.get(editorcore.doc.body);
23342                     c.select('[class]').each(function(s) {
23343                         s.dom.removeAttribute('class');
23344                     });
23345                     editorcore.cleanWord();
23346                     editorcore.syncValue();
23347                 },
23348                 tabIndex:-1
23349             });
23350             
23351              cmenu.menu.items.push({
23352                 actiontype : 'tidy',
23353                 html: 'Tidy HTML Source',
23354                 handler: function(a,b) {
23355                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23356                     editorcore.syncValue();
23357                 },
23358                 tabIndex:-1
23359             });
23360             
23361             
23362             tb.add(cmenu);
23363         }
23364          
23365         if (!this.disable.specialElements) {
23366             var semenu = {
23367                 text: "Other;",
23368                 cls: 'x-edit-none',
23369                 menu : {
23370                     items : []
23371                 }
23372             };
23373             for (var i =0; i < this.specialElements.length; i++) {
23374                 semenu.menu.items.push(
23375                     Roo.apply({ 
23376                         handler: function(a,b) {
23377                             editor.insertAtCursor(this.ihtml);
23378                         }
23379                     }, this.specialElements[i])
23380                 );
23381                     
23382             }
23383             
23384             tb.add(semenu);
23385             
23386             
23387         }
23388          
23389         
23390         if (this.btns) {
23391             for(var i =0; i< this.btns.length;i++) {
23392                 var b = Roo.factory(this.btns[i],Roo.form);
23393                 b.cls =  'x-edit-none';
23394                 
23395                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23396                     b.cls += ' x-init-enable';
23397                 }
23398                 
23399                 b.scope = editorcore;
23400                 tb.add(b);
23401             }
23402         
23403         }
23404         
23405         
23406         
23407         // disable everything...
23408         
23409         this.tb.items.each(function(item){
23410             
23411            if(
23412                 item.id != editorcore.frameId+ '-sourceedit' && 
23413                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23414             ){
23415                 
23416                 item.disable();
23417             }
23418         });
23419         this.rendered = true;
23420         
23421         // the all the btns;
23422         editor.on('editorevent', this.updateToolbar, this);
23423         // other toolbars need to implement this..
23424         //editor.on('editmodechange', this.updateToolbar, this);
23425     },
23426     
23427     
23428     relayBtnCmd : function(btn) {
23429         this.editorcore.relayCmd(btn.cmd);
23430     },
23431     // private used internally
23432     createLink : function(){
23433         Roo.log("create link?");
23434         var url = prompt(this.createLinkText, this.defaultLinkValue);
23435         if(url && url != 'http:/'+'/'){
23436             this.editorcore.relayCmd('createlink', url);
23437         }
23438     },
23439
23440     
23441     /**
23442      * Protected method that will not generally be called directly. It triggers
23443      * a toolbar update by reading the markup state of the current selection in the editor.
23444      */
23445     updateToolbar: function(){
23446
23447         if(!this.editorcore.activated){
23448             this.editor.onFirstFocus();
23449             return;
23450         }
23451
23452         var btns = this.tb.items.map, 
23453             doc = this.editorcore.doc,
23454             frameId = this.editorcore.frameId;
23455
23456         if(!this.disable.font && !Roo.isSafari){
23457             /*
23458             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23459             if(name != this.fontSelect.dom.value){
23460                 this.fontSelect.dom.value = name;
23461             }
23462             */
23463         }
23464         if(!this.disable.format){
23465             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23466             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23467             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23468             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23469         }
23470         if(!this.disable.alignments){
23471             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23472             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23473             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23474         }
23475         if(!Roo.isSafari && !this.disable.lists){
23476             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23477             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23478         }
23479         
23480         var ans = this.editorcore.getAllAncestors();
23481         if (this.formatCombo) {
23482             
23483             
23484             var store = this.formatCombo.store;
23485             this.formatCombo.setValue("");
23486             for (var i =0; i < ans.length;i++) {
23487                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23488                     // select it..
23489                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23490                     break;
23491                 }
23492             }
23493         }
23494         
23495         
23496         
23497         // hides menus... - so this cant be on a menu...
23498         Roo.menu.MenuMgr.hideAll();
23499
23500         //this.editorsyncValue();
23501     },
23502    
23503     
23504     createFontOptions : function(){
23505         var buf = [], fs = this.fontFamilies, ff, lc;
23506         
23507         
23508         
23509         for(var i = 0, len = fs.length; i< len; i++){
23510             ff = fs[i];
23511             lc = ff.toLowerCase();
23512             buf.push(
23513                 '<option value="',lc,'" style="font-family:',ff,';"',
23514                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23515                     ff,
23516                 '</option>'
23517             );
23518         }
23519         return buf.join('');
23520     },
23521     
23522     toggleSourceEdit : function(sourceEditMode){
23523         
23524         Roo.log("toolbar toogle");
23525         if(sourceEditMode === undefined){
23526             sourceEditMode = !this.sourceEditMode;
23527         }
23528         this.sourceEditMode = sourceEditMode === true;
23529         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23530         // just toggle the button?
23531         if(btn.pressed !== this.sourceEditMode){
23532             btn.toggle(this.sourceEditMode);
23533             return;
23534         }
23535         
23536         if(sourceEditMode){
23537             Roo.log("disabling buttons");
23538             this.tb.items.each(function(item){
23539                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23540                     item.disable();
23541                 }
23542             });
23543           
23544         }else{
23545             Roo.log("enabling buttons");
23546             if(this.editorcore.initialized){
23547                 this.tb.items.each(function(item){
23548                     item.enable();
23549                 });
23550             }
23551             
23552         }
23553         Roo.log("calling toggole on editor");
23554         // tell the editor that it's been pressed..
23555         this.editor.toggleSourceEdit(sourceEditMode);
23556        
23557     },
23558      /**
23559      * Object collection of toolbar tooltips for the buttons in the editor. The key
23560      * is the command id associated with that button and the value is a valid QuickTips object.
23561      * For example:
23562 <pre><code>
23563 {
23564     bold : {
23565         title: 'Bold (Ctrl+B)',
23566         text: 'Make the selected text bold.',
23567         cls: 'x-html-editor-tip'
23568     },
23569     italic : {
23570         title: 'Italic (Ctrl+I)',
23571         text: 'Make the selected text italic.',
23572         cls: 'x-html-editor-tip'
23573     },
23574     ...
23575 </code></pre>
23576     * @type Object
23577      */
23578     buttonTips : {
23579         bold : {
23580             title: 'Bold (Ctrl+B)',
23581             text: 'Make the selected text bold.',
23582             cls: 'x-html-editor-tip'
23583         },
23584         italic : {
23585             title: 'Italic (Ctrl+I)',
23586             text: 'Make the selected text italic.',
23587             cls: 'x-html-editor-tip'
23588         },
23589         underline : {
23590             title: 'Underline (Ctrl+U)',
23591             text: 'Underline the selected text.',
23592             cls: 'x-html-editor-tip'
23593         },
23594         strikethrough : {
23595             title: 'Strikethrough',
23596             text: 'Strikethrough the selected text.',
23597             cls: 'x-html-editor-tip'
23598         },
23599         increasefontsize : {
23600             title: 'Grow Text',
23601             text: 'Increase the font size.',
23602             cls: 'x-html-editor-tip'
23603         },
23604         decreasefontsize : {
23605             title: 'Shrink Text',
23606             text: 'Decrease the font size.',
23607             cls: 'x-html-editor-tip'
23608         },
23609         backcolor : {
23610             title: 'Text Highlight Color',
23611             text: 'Change the background color of the selected text.',
23612             cls: 'x-html-editor-tip'
23613         },
23614         forecolor : {
23615             title: 'Font Color',
23616             text: 'Change the color of the selected text.',
23617             cls: 'x-html-editor-tip'
23618         },
23619         justifyleft : {
23620             title: 'Align Text Left',
23621             text: 'Align text to the left.',
23622             cls: 'x-html-editor-tip'
23623         },
23624         justifycenter : {
23625             title: 'Center Text',
23626             text: 'Center text in the editor.',
23627             cls: 'x-html-editor-tip'
23628         },
23629         justifyright : {
23630             title: 'Align Text Right',
23631             text: 'Align text to the right.',
23632             cls: 'x-html-editor-tip'
23633         },
23634         insertunorderedlist : {
23635             title: 'Bullet List',
23636             text: 'Start a bulleted list.',
23637             cls: 'x-html-editor-tip'
23638         },
23639         insertorderedlist : {
23640             title: 'Numbered List',
23641             text: 'Start a numbered list.',
23642             cls: 'x-html-editor-tip'
23643         },
23644         createlink : {
23645             title: 'Hyperlink',
23646             text: 'Make the selected text a hyperlink.',
23647             cls: 'x-html-editor-tip'
23648         },
23649         sourceedit : {
23650             title: 'Source Edit',
23651             text: 'Switch to source editing mode.',
23652             cls: 'x-html-editor-tip'
23653         }
23654     },
23655     // private
23656     onDestroy : function(){
23657         if(this.rendered){
23658             
23659             this.tb.items.each(function(item){
23660                 if(item.menu){
23661                     item.menu.removeAll();
23662                     if(item.menu.el){
23663                         item.menu.el.destroy();
23664                     }
23665                 }
23666                 item.destroy();
23667             });
23668              
23669         }
23670     },
23671     onFirstFocus: function() {
23672         this.tb.items.each(function(item){
23673            item.enable();
23674         });
23675     }
23676 });
23677
23678
23679
23680
23681 // <script type="text/javascript">
23682 /*
23683  * Based on
23684  * Ext JS Library 1.1.1
23685  * Copyright(c) 2006-2007, Ext JS, LLC.
23686  *  
23687  
23688  */
23689
23690  
23691 /**
23692  * @class Roo.form.HtmlEditor.ToolbarContext
23693  * Context Toolbar
23694  * 
23695  * Usage:
23696  *
23697  new Roo.form.HtmlEditor({
23698     ....
23699     toolbars : [
23700         { xtype: 'ToolbarStandard', styles : {} }
23701         { xtype: 'ToolbarContext', disable : {} }
23702     ]
23703 })
23704
23705      
23706  * 
23707  * @config : {Object} disable List of elements to disable.. (not done yet.)
23708  * @config : {Object} styles  Map of styles available.
23709  * 
23710  */
23711
23712 Roo.form.HtmlEditor.ToolbarContext = function(config)
23713 {
23714     
23715     Roo.apply(this, config);
23716     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23717     // dont call parent... till later.
23718     this.styles = this.styles || {};
23719 }
23720
23721  
23722
23723 Roo.form.HtmlEditor.ToolbarContext.types = {
23724     'IMG' : {
23725         width : {
23726             title: "Width",
23727             width: 40
23728         },
23729         height:  {
23730             title: "Height",
23731             width: 40
23732         },
23733         align: {
23734             title: "Align",
23735             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23736             width : 80
23737             
23738         },
23739         border: {
23740             title: "Border",
23741             width: 40
23742         },
23743         alt: {
23744             title: "Alt",
23745             width: 120
23746         },
23747         src : {
23748             title: "Src",
23749             width: 220
23750         }
23751         
23752     },
23753     'A' : {
23754         name : {
23755             title: "Name",
23756             width: 50
23757         },
23758         target:  {
23759             title: "Target",
23760             width: 120
23761         },
23762         href:  {
23763             title: "Href",
23764             width: 220
23765         } // border?
23766         
23767     },
23768     'TABLE' : {
23769         rows : {
23770             title: "Rows",
23771             width: 20
23772         },
23773         cols : {
23774             title: "Cols",
23775             width: 20
23776         },
23777         width : {
23778             title: "Width",
23779             width: 40
23780         },
23781         height : {
23782             title: "Height",
23783             width: 40
23784         },
23785         border : {
23786             title: "Border",
23787             width: 20
23788         }
23789     },
23790     'TD' : {
23791         width : {
23792             title: "Width",
23793             width: 40
23794         },
23795         height : {
23796             title: "Height",
23797             width: 40
23798         },   
23799         align: {
23800             title: "Align",
23801             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23802             width: 80
23803         },
23804         valign: {
23805             title: "Valign",
23806             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23807             width: 80
23808         },
23809         colspan: {
23810             title: "Colspan",
23811             width: 20
23812             
23813         },
23814          'font-family'  : {
23815             title : "Font",
23816             style : 'fontFamily',
23817             displayField: 'display',
23818             optname : 'font-family',
23819             width: 140
23820         }
23821     },
23822     'INPUT' : {
23823         name : {
23824             title: "name",
23825             width: 120
23826         },
23827         value : {
23828             title: "Value",
23829             width: 120
23830         },
23831         width : {
23832             title: "Width",
23833             width: 40
23834         }
23835     },
23836     'LABEL' : {
23837         'for' : {
23838             title: "For",
23839             width: 120
23840         }
23841     },
23842     'TEXTAREA' : {
23843           name : {
23844             title: "name",
23845             width: 120
23846         },
23847         rows : {
23848             title: "Rows",
23849             width: 20
23850         },
23851         cols : {
23852             title: "Cols",
23853             width: 20
23854         }
23855     },
23856     'SELECT' : {
23857         name : {
23858             title: "name",
23859             width: 120
23860         },
23861         selectoptions : {
23862             title: "Options",
23863             width: 200
23864         }
23865     },
23866     
23867     // should we really allow this??
23868     // should this just be 
23869     'BODY' : {
23870         title : {
23871             title: "Title",
23872             width: 200,
23873             disabled : true
23874         }
23875     },
23876     'SPAN' : {
23877         'font-family'  : {
23878             title : "Font",
23879             style : 'fontFamily',
23880             displayField: 'display',
23881             optname : 'font-family',
23882             width: 140
23883         }
23884     },
23885     'DIV' : {
23886         'font-family'  : {
23887             title : "Font",
23888             style : 'fontFamily',
23889             displayField: 'display',
23890             optname : 'font-family',
23891             width: 140
23892         }
23893     },
23894      'P' : {
23895         'font-family'  : {
23896             title : "Font",
23897             style : 'fontFamily',
23898             displayField: 'display',
23899             optname : 'font-family',
23900             width: 140
23901         }
23902     },
23903     
23904     '*' : {
23905         // empty..
23906     }
23907
23908 };
23909
23910 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23911 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23912
23913 Roo.form.HtmlEditor.ToolbarContext.options = {
23914         'font-family'  : [ 
23915                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23916                 [ 'Courier New', 'Courier New'],
23917                 [ 'Tahoma', 'Tahoma'],
23918                 [ 'Times New Roman,serif', 'Times'],
23919                 [ 'Verdana','Verdana' ]
23920         ]
23921 };
23922
23923 // fixme - these need to be configurable..
23924  
23925
23926 //Roo.form.HtmlEditor.ToolbarContext.types
23927
23928
23929 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23930     
23931     tb: false,
23932     
23933     rendered: false,
23934     
23935     editor : false,
23936     editorcore : false,
23937     /**
23938      * @cfg {Object} disable  List of toolbar elements to disable
23939          
23940      */
23941     disable : false,
23942     /**
23943      * @cfg {Object} styles List of styles 
23944      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23945      *
23946      * These must be defined in the page, so they get rendered correctly..
23947      * .headline { }
23948      * TD.underline { }
23949      * 
23950      */
23951     styles : false,
23952     
23953     options: false,
23954     
23955     toolbars : false,
23956     
23957     init : function(editor)
23958     {
23959         this.editor = editor;
23960         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23961         var editorcore = this.editorcore;
23962         
23963         var fid = editorcore.frameId;
23964         var etb = this;
23965         function btn(id, toggle, handler){
23966             var xid = fid + '-'+ id ;
23967             return {
23968                 id : xid,
23969                 cmd : id,
23970                 cls : 'x-btn-icon x-edit-'+id,
23971                 enableToggle:toggle !== false,
23972                 scope: editorcore, // was editor...
23973                 handler:handler||editorcore.relayBtnCmd,
23974                 clickEvent:'mousedown',
23975                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23976                 tabIndex:-1
23977             };
23978         }
23979         // create a new element.
23980         var wdiv = editor.wrap.createChild({
23981                 tag: 'div'
23982             }, editor.wrap.dom.firstChild.nextSibling, true);
23983         
23984         // can we do this more than once??
23985         
23986          // stop form submits
23987       
23988  
23989         // disable everything...
23990         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23991         this.toolbars = {};
23992            
23993         for (var i in  ty) {
23994           
23995             this.toolbars[i] = this.buildToolbar(ty[i],i);
23996         }
23997         this.tb = this.toolbars.BODY;
23998         this.tb.el.show();
23999         this.buildFooter();
24000         this.footer.show();
24001         editor.on('hide', function( ) { this.footer.hide() }, this);
24002         editor.on('show', function( ) { this.footer.show() }, this);
24003         
24004          
24005         this.rendered = true;
24006         
24007         // the all the btns;
24008         editor.on('editorevent', this.updateToolbar, this);
24009         // other toolbars need to implement this..
24010         //editor.on('editmodechange', this.updateToolbar, this);
24011     },
24012     
24013     
24014     
24015     /**
24016      * Protected method that will not generally be called directly. It triggers
24017      * a toolbar update by reading the markup state of the current selection in the editor.
24018      *
24019      * Note you can force an update by calling on('editorevent', scope, false)
24020      */
24021     updateToolbar: function(editor,ev,sel){
24022
24023         //Roo.log(ev);
24024         // capture mouse up - this is handy for selecting images..
24025         // perhaps should go somewhere else...
24026         if(!this.editorcore.activated){
24027              this.editor.onFirstFocus();
24028             return;
24029         }
24030         
24031         
24032         
24033         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24034         // selectNode - might want to handle IE?
24035         if (ev &&
24036             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24037             ev.target && ev.target.tagName == 'IMG') {
24038             // they have click on an image...
24039             // let's see if we can change the selection...
24040             sel = ev.target;
24041          
24042               var nodeRange = sel.ownerDocument.createRange();
24043             try {
24044                 nodeRange.selectNode(sel);
24045             } catch (e) {
24046                 nodeRange.selectNodeContents(sel);
24047             }
24048             //nodeRange.collapse(true);
24049             var s = this.editorcore.win.getSelection();
24050             s.removeAllRanges();
24051             s.addRange(nodeRange);
24052         }  
24053         
24054       
24055         var updateFooter = sel ? false : true;
24056         
24057         
24058         var ans = this.editorcore.getAllAncestors();
24059         
24060         // pick
24061         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24062         
24063         if (!sel) { 
24064             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24065             sel = sel ? sel : this.editorcore.doc.body;
24066             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24067             
24068         }
24069         // pick a menu that exists..
24070         var tn = sel.tagName.toUpperCase();
24071         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24072         
24073         tn = sel.tagName.toUpperCase();
24074         
24075         var lastSel = this.tb.selectedNode;
24076         
24077         this.tb.selectedNode = sel;
24078         
24079         // if current menu does not match..
24080         
24081         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24082                 
24083             this.tb.el.hide();
24084             ///console.log("show: " + tn);
24085             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24086             this.tb.el.show();
24087             // update name
24088             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24089             
24090             
24091             // update attributes
24092             if (this.tb.fields) {
24093                 this.tb.fields.each(function(e) {
24094                     if (e.stylename) {
24095                         e.setValue(sel.style[e.stylename]);
24096                         return;
24097                     } 
24098                    e.setValue(sel.getAttribute(e.attrname));
24099                 });
24100             }
24101             
24102             var hasStyles = false;
24103             for(var i in this.styles) {
24104                 hasStyles = true;
24105                 break;
24106             }
24107             
24108             // update styles
24109             if (hasStyles) { 
24110                 var st = this.tb.fields.item(0);
24111                 
24112                 st.store.removeAll();
24113                
24114                 
24115                 var cn = sel.className.split(/\s+/);
24116                 
24117                 var avs = [];
24118                 if (this.styles['*']) {
24119                     
24120                     Roo.each(this.styles['*'], function(v) {
24121                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24122                     });
24123                 }
24124                 if (this.styles[tn]) { 
24125                     Roo.each(this.styles[tn], function(v) {
24126                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24127                     });
24128                 }
24129                 
24130                 st.store.loadData(avs);
24131                 st.collapse();
24132                 st.setValue(cn);
24133             }
24134             // flag our selected Node.
24135             this.tb.selectedNode = sel;
24136            
24137            
24138             Roo.menu.MenuMgr.hideAll();
24139
24140         }
24141         
24142         if (!updateFooter) {
24143             //this.footDisp.dom.innerHTML = ''; 
24144             return;
24145         }
24146         // update the footer
24147         //
24148         var html = '';
24149         
24150         this.footerEls = ans.reverse();
24151         Roo.each(this.footerEls, function(a,i) {
24152             if (!a) { return; }
24153             html += html.length ? ' &gt; '  :  '';
24154             
24155             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24156             
24157         });
24158        
24159         // 
24160         var sz = this.footDisp.up('td').getSize();
24161         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24162         this.footDisp.dom.style.marginLeft = '5px';
24163         
24164         this.footDisp.dom.style.overflow = 'hidden';
24165         
24166         this.footDisp.dom.innerHTML = html;
24167             
24168         //this.editorsyncValue();
24169     },
24170      
24171     
24172    
24173        
24174     // private
24175     onDestroy : function(){
24176         if(this.rendered){
24177             
24178             this.tb.items.each(function(item){
24179                 if(item.menu){
24180                     item.menu.removeAll();
24181                     if(item.menu.el){
24182                         item.menu.el.destroy();
24183                     }
24184                 }
24185                 item.destroy();
24186             });
24187              
24188         }
24189     },
24190     onFirstFocus: function() {
24191         // need to do this for all the toolbars..
24192         this.tb.items.each(function(item){
24193            item.enable();
24194         });
24195     },
24196     buildToolbar: function(tlist, nm)
24197     {
24198         var editor = this.editor;
24199         var editorcore = this.editorcore;
24200          // create a new element.
24201         var wdiv = editor.wrap.createChild({
24202                 tag: 'div'
24203             }, editor.wrap.dom.firstChild.nextSibling, true);
24204         
24205        
24206         var tb = new Roo.Toolbar(wdiv);
24207         // add the name..
24208         
24209         tb.add(nm+ ":&nbsp;");
24210         
24211         var styles = [];
24212         for(var i in this.styles) {
24213             styles.push(i);
24214         }
24215         
24216         // styles...
24217         if (styles && styles.length) {
24218             
24219             // this needs a multi-select checkbox...
24220             tb.addField( new Roo.form.ComboBox({
24221                 store: new Roo.data.SimpleStore({
24222                     id : 'val',
24223                     fields: ['val', 'selected'],
24224                     data : [] 
24225                 }),
24226                 name : '-roo-edit-className',
24227                 attrname : 'className',
24228                 displayField: 'val',
24229                 typeAhead: false,
24230                 mode: 'local',
24231                 editable : false,
24232                 triggerAction: 'all',
24233                 emptyText:'Select Style',
24234                 selectOnFocus:true,
24235                 width: 130,
24236                 listeners : {
24237                     'select': function(c, r, i) {
24238                         // initial support only for on class per el..
24239                         tb.selectedNode.className =  r ? r.get('val') : '';
24240                         editorcore.syncValue();
24241                     }
24242                 }
24243     
24244             }));
24245         }
24246         
24247         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24248         var tbops = tbc.options;
24249         
24250         for (var i in tlist) {
24251             
24252             var item = tlist[i];
24253             tb.add(item.title + ":&nbsp;");
24254             
24255             
24256             //optname == used so you can configure the options available..
24257             var opts = item.opts ? item.opts : false;
24258             if (item.optname) {
24259                 opts = tbops[item.optname];
24260            
24261             }
24262             
24263             if (opts) {
24264                 // opts == pulldown..
24265                 tb.addField( new Roo.form.ComboBox({
24266                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24267                         id : 'val',
24268                         fields: ['val', 'display'],
24269                         data : opts  
24270                     }),
24271                     name : '-roo-edit-' + i,
24272                     attrname : i,
24273                     stylename : item.style ? item.style : false,
24274                     displayField: item.displayField ? item.displayField : 'val',
24275                     valueField :  'val',
24276                     typeAhead: false,
24277                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24278                     editable : false,
24279                     triggerAction: 'all',
24280                     emptyText:'Select',
24281                     selectOnFocus:true,
24282                     width: item.width ? item.width  : 130,
24283                     listeners : {
24284                         'select': function(c, r, i) {
24285                             if (c.stylename) {
24286                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24287                                 return;
24288                             }
24289                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24290                         }
24291                     }
24292
24293                 }));
24294                 continue;
24295                     
24296                  
24297                 
24298                 tb.addField( new Roo.form.TextField({
24299                     name: i,
24300                     width: 100,
24301                     //allowBlank:false,
24302                     value: ''
24303                 }));
24304                 continue;
24305             }
24306             tb.addField( new Roo.form.TextField({
24307                 name: '-roo-edit-' + i,
24308                 attrname : i,
24309                 
24310                 width: item.width,
24311                 //allowBlank:true,
24312                 value: '',
24313                 listeners: {
24314                     'change' : function(f, nv, ov) {
24315                         tb.selectedNode.setAttribute(f.attrname, nv);
24316                         editorcore.syncValue();
24317                     }
24318                 }
24319             }));
24320              
24321         }
24322         
24323         var _this = this;
24324         
24325         if(nm == 'BODY'){
24326             tb.addSeparator();
24327         
24328             tb.addButton( {
24329                 text: 'Stylesheets',
24330
24331                 listeners : {
24332                     click : function ()
24333                     {
24334                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24335                     }
24336                 }
24337             });
24338         }
24339         
24340         tb.addFill();
24341         tb.addButton( {
24342             text: 'Remove Tag',
24343     
24344             listeners : {
24345                 click : function ()
24346                 {
24347                     // remove
24348                     // undo does not work.
24349                      
24350                     var sn = tb.selectedNode;
24351                     
24352                     var pn = sn.parentNode;
24353                     
24354                     var stn =  sn.childNodes[0];
24355                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24356                     while (sn.childNodes.length) {
24357                         var node = sn.childNodes[0];
24358                         sn.removeChild(node);
24359                         //Roo.log(node);
24360                         pn.insertBefore(node, sn);
24361                         
24362                     }
24363                     pn.removeChild(sn);
24364                     var range = editorcore.createRange();
24365         
24366                     range.setStart(stn,0);
24367                     range.setEnd(en,0); //????
24368                     //range.selectNode(sel);
24369                     
24370                     
24371                     var selection = editorcore.getSelection();
24372                     selection.removeAllRanges();
24373                     selection.addRange(range);
24374                     
24375                     
24376                     
24377                     //_this.updateToolbar(null, null, pn);
24378                     _this.updateToolbar(null, null, null);
24379                     _this.footDisp.dom.innerHTML = ''; 
24380                 }
24381             }
24382             
24383                     
24384                 
24385             
24386         });
24387         
24388         
24389         tb.el.on('click', function(e){
24390             e.preventDefault(); // what does this do?
24391         });
24392         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24393         tb.el.hide();
24394         tb.name = nm;
24395         // dont need to disable them... as they will get hidden
24396         return tb;
24397          
24398         
24399     },
24400     buildFooter : function()
24401     {
24402         
24403         var fel = this.editor.wrap.createChild();
24404         this.footer = new Roo.Toolbar(fel);
24405         // toolbar has scrolly on left / right?
24406         var footDisp= new Roo.Toolbar.Fill();
24407         var _t = this;
24408         this.footer.add(
24409             {
24410                 text : '&lt;',
24411                 xtype: 'Button',
24412                 handler : function() {
24413                     _t.footDisp.scrollTo('left',0,true)
24414                 }
24415             }
24416         );
24417         this.footer.add( footDisp );
24418         this.footer.add( 
24419             {
24420                 text : '&gt;',
24421                 xtype: 'Button',
24422                 handler : function() {
24423                     // no animation..
24424                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24425                 }
24426             }
24427         );
24428         var fel = Roo.get(footDisp.el);
24429         fel.addClass('x-editor-context');
24430         this.footDispWrap = fel; 
24431         this.footDispWrap.overflow  = 'hidden';
24432         
24433         this.footDisp = fel.createChild();
24434         this.footDispWrap.on('click', this.onContextClick, this)
24435         
24436         
24437     },
24438     onContextClick : function (ev,dom)
24439     {
24440         ev.preventDefault();
24441         var  cn = dom.className;
24442         //Roo.log(cn);
24443         if (!cn.match(/x-ed-loc-/)) {
24444             return;
24445         }
24446         var n = cn.split('-').pop();
24447         var ans = this.footerEls;
24448         var sel = ans[n];
24449         
24450          // pick
24451         var range = this.editorcore.createRange();
24452         
24453         range.selectNodeContents(sel);
24454         //range.selectNode(sel);
24455         
24456         
24457         var selection = this.editorcore.getSelection();
24458         selection.removeAllRanges();
24459         selection.addRange(range);
24460         
24461         
24462         
24463         this.updateToolbar(null, null, sel);
24464         
24465         
24466     }
24467     
24468     
24469     
24470     
24471     
24472 });
24473
24474
24475
24476
24477
24478 /*
24479  * Based on:
24480  * Ext JS Library 1.1.1
24481  * Copyright(c) 2006-2007, Ext JS, LLC.
24482  *
24483  * Originally Released Under LGPL - original licence link has changed is not relivant.
24484  *
24485  * Fork - LGPL
24486  * <script type="text/javascript">
24487  */
24488  
24489 /**
24490  * @class Roo.form.BasicForm
24491  * @extends Roo.util.Observable
24492  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24493  * @constructor
24494  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24495  * @param {Object} config Configuration options
24496  */
24497 Roo.form.BasicForm = function(el, config){
24498     this.allItems = [];
24499     this.childForms = [];
24500     Roo.apply(this, config);
24501     /*
24502      * The Roo.form.Field items in this form.
24503      * @type MixedCollection
24504      */
24505      
24506      
24507     this.items = new Roo.util.MixedCollection(false, function(o){
24508         return o.id || (o.id = Roo.id());
24509     });
24510     this.addEvents({
24511         /**
24512          * @event beforeaction
24513          * Fires before any action is performed. Return false to cancel the action.
24514          * @param {Form} this
24515          * @param {Action} action The action to be performed
24516          */
24517         beforeaction: true,
24518         /**
24519          * @event actionfailed
24520          * Fires when an action fails.
24521          * @param {Form} this
24522          * @param {Action} action The action that failed
24523          */
24524         actionfailed : true,
24525         /**
24526          * @event actioncomplete
24527          * Fires when an action is completed.
24528          * @param {Form} this
24529          * @param {Action} action The action that completed
24530          */
24531         actioncomplete : true
24532     });
24533     if(el){
24534         this.initEl(el);
24535     }
24536     Roo.form.BasicForm.superclass.constructor.call(this);
24537     
24538     Roo.form.BasicForm.popover.apply();
24539 };
24540
24541 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24542     /**
24543      * @cfg {String} method
24544      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24545      */
24546     /**
24547      * @cfg {DataReader} reader
24548      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24549      * This is optional as there is built-in support for processing JSON.
24550      */
24551     /**
24552      * @cfg {DataReader} errorReader
24553      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24554      * This is completely optional as there is built-in support for processing JSON.
24555      */
24556     /**
24557      * @cfg {String} url
24558      * The URL to use for form actions if one isn't supplied in the action options.
24559      */
24560     /**
24561      * @cfg {Boolean} fileUpload
24562      * Set to true if this form is a file upload.
24563      */
24564      
24565     /**
24566      * @cfg {Object} baseParams
24567      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24568      */
24569      /**
24570      
24571     /**
24572      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24573      */
24574     timeout: 30,
24575
24576     // private
24577     activeAction : null,
24578
24579     /**
24580      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24581      * or setValues() data instead of when the form was first created.
24582      */
24583     trackResetOnLoad : false,
24584     
24585     
24586     /**
24587      * childForms - used for multi-tab forms
24588      * @type {Array}
24589      */
24590     childForms : false,
24591     
24592     /**
24593      * allItems - full list of fields.
24594      * @type {Array}
24595      */
24596     allItems : false,
24597     
24598     /**
24599      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24600      * element by passing it or its id or mask the form itself by passing in true.
24601      * @type Mixed
24602      */
24603     waitMsgTarget : false,
24604     
24605     /**
24606      * @type Boolean
24607      */
24608     disableMask : false,
24609     
24610     /**
24611      * @cfg {Boolean} errorMask (true|false) default false
24612      */
24613     errorMask : false,
24614     
24615     /**
24616      * @cfg {Number} maskOffset Default 100
24617      */
24618     maskOffset : 100,
24619
24620     // private
24621     initEl : function(el){
24622         this.el = Roo.get(el);
24623         this.id = this.el.id || Roo.id();
24624         this.el.on('submit', this.onSubmit, this);
24625         this.el.addClass('x-form');
24626     },
24627
24628     // private
24629     onSubmit : function(e){
24630         e.stopEvent();
24631     },
24632
24633     /**
24634      * Returns true if client-side validation on the form is successful.
24635      * @return Boolean
24636      */
24637     isValid : function(){
24638         var valid = true;
24639         var target = false;
24640         this.items.each(function(f){
24641             if(f.validate()){
24642                 return;
24643             }
24644             
24645             valid = false;
24646                 
24647             if(!target && f.el.isVisible(true)){
24648                 target = f;
24649             }
24650         });
24651         
24652         if(this.errorMask && !valid){
24653             Roo.form.BasicForm.popover.mask(this, target);
24654         }
24655         
24656         return valid;
24657     },
24658     /**
24659      * Returns array of invalid form fields.
24660      * @return Array
24661      */
24662     
24663     invalidFields : function()
24664     {
24665         var ret = [];
24666         this.items.each(function(f){
24667             if(f.validate()){
24668                 return;
24669             }
24670             ret.push(f);
24671             
24672         });
24673         
24674         return ret;
24675     },
24676     
24677     
24678     /**
24679      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24680      * @return Boolean
24681      */
24682     isDirty : function(){
24683         var dirty = false;
24684         this.items.each(function(f){
24685            if(f.isDirty()){
24686                dirty = true;
24687                return false;
24688            }
24689         });
24690         return dirty;
24691     },
24692     
24693     /**
24694      * Returns true if any fields in this form have changed since their original load. (New version)
24695      * @return Boolean
24696      */
24697     
24698     hasChanged : function()
24699     {
24700         var dirty = false;
24701         this.items.each(function(f){
24702            if(f.hasChanged()){
24703                dirty = true;
24704                return false;
24705            }
24706         });
24707         return dirty;
24708         
24709     },
24710     /**
24711      * Resets all hasChanged to 'false' -
24712      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24713      * So hasChanged storage is only to be used for this purpose
24714      * @return Boolean
24715      */
24716     resetHasChanged : function()
24717     {
24718         this.items.each(function(f){
24719            f.resetHasChanged();
24720         });
24721         
24722     },
24723     
24724     
24725     /**
24726      * Performs a predefined action (submit or load) or custom actions you define on this form.
24727      * @param {String} actionName The name of the action type
24728      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24729      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24730      * accept other config options):
24731      * <pre>
24732 Property          Type             Description
24733 ----------------  ---------------  ----------------------------------------------------------------------------------
24734 url               String           The url for the action (defaults to the form's url)
24735 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24736 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24737 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24738                                    validate the form on the client (defaults to false)
24739      * </pre>
24740      * @return {BasicForm} this
24741      */
24742     doAction : function(action, options){
24743         if(typeof action == 'string'){
24744             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24745         }
24746         if(this.fireEvent('beforeaction', this, action) !== false){
24747             this.beforeAction(action);
24748             action.run.defer(100, action);
24749         }
24750         return this;
24751     },
24752
24753     /**
24754      * Shortcut to do a submit action.
24755      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24756      * @return {BasicForm} this
24757      */
24758     submit : function(options){
24759         this.doAction('submit', options);
24760         return this;
24761     },
24762
24763     /**
24764      * Shortcut to do a load action.
24765      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24766      * @return {BasicForm} this
24767      */
24768     load : function(options){
24769         this.doAction('load', options);
24770         return this;
24771     },
24772
24773     /**
24774      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24775      * @param {Record} record The record to edit
24776      * @return {BasicForm} this
24777      */
24778     updateRecord : function(record){
24779         record.beginEdit();
24780         var fs = record.fields;
24781         fs.each(function(f){
24782             var field = this.findField(f.name);
24783             if(field){
24784                 record.set(f.name, field.getValue());
24785             }
24786         }, this);
24787         record.endEdit();
24788         return this;
24789     },
24790
24791     /**
24792      * Loads an Roo.data.Record into this form.
24793      * @param {Record} record The record to load
24794      * @return {BasicForm} this
24795      */
24796     loadRecord : function(record){
24797         this.setValues(record.data);
24798         return this;
24799     },
24800
24801     // private
24802     beforeAction : function(action){
24803         var o = action.options;
24804         
24805         if(!this.disableMask) {
24806             if(this.waitMsgTarget === true){
24807                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24808             }else if(this.waitMsgTarget){
24809                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24810                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24811             }else {
24812                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24813             }
24814         }
24815         
24816          
24817     },
24818
24819     // private
24820     afterAction : function(action, success){
24821         this.activeAction = null;
24822         var o = action.options;
24823         
24824         if(!this.disableMask) {
24825             if(this.waitMsgTarget === true){
24826                 this.el.unmask();
24827             }else if(this.waitMsgTarget){
24828                 this.waitMsgTarget.unmask();
24829             }else{
24830                 Roo.MessageBox.updateProgress(1);
24831                 Roo.MessageBox.hide();
24832             }
24833         }
24834         
24835         if(success){
24836             if(o.reset){
24837                 this.reset();
24838             }
24839             Roo.callback(o.success, o.scope, [this, action]);
24840             this.fireEvent('actioncomplete', this, action);
24841             
24842         }else{
24843             
24844             // failure condition..
24845             // we have a scenario where updates need confirming.
24846             // eg. if a locking scenario exists..
24847             // we look for { errors : { needs_confirm : true }} in the response.
24848             if (
24849                 (typeof(action.result) != 'undefined')  &&
24850                 (typeof(action.result.errors) != 'undefined')  &&
24851                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24852            ){
24853                 var _t = this;
24854                 Roo.MessageBox.confirm(
24855                     "Change requires confirmation",
24856                     action.result.errorMsg,
24857                     function(r) {
24858                         if (r != 'yes') {
24859                             return;
24860                         }
24861                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24862                     }
24863                     
24864                 );
24865                 
24866                 
24867                 
24868                 return;
24869             }
24870             
24871             Roo.callback(o.failure, o.scope, [this, action]);
24872             // show an error message if no failed handler is set..
24873             if (!this.hasListener('actionfailed')) {
24874                 Roo.MessageBox.alert("Error",
24875                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24876                         action.result.errorMsg :
24877                         "Saving Failed, please check your entries or try again"
24878                 );
24879             }
24880             
24881             this.fireEvent('actionfailed', this, action);
24882         }
24883         
24884     },
24885
24886     /**
24887      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24888      * @param {String} id The value to search for
24889      * @return Field
24890      */
24891     findField : function(id){
24892         var field = this.items.get(id);
24893         if(!field){
24894             this.items.each(function(f){
24895                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24896                     field = f;
24897                     return false;
24898                 }
24899             });
24900         }
24901         return field || null;
24902     },
24903
24904     /**
24905      * Add a secondary form to this one, 
24906      * Used to provide tabbed forms. One form is primary, with hidden values 
24907      * which mirror the elements from the other forms.
24908      * 
24909      * @param {Roo.form.Form} form to add.
24910      * 
24911      */
24912     addForm : function(form)
24913     {
24914        
24915         if (this.childForms.indexOf(form) > -1) {
24916             // already added..
24917             return;
24918         }
24919         this.childForms.push(form);
24920         var n = '';
24921         Roo.each(form.allItems, function (fe) {
24922             
24923             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24924             if (this.findField(n)) { // already added..
24925                 return;
24926             }
24927             var add = new Roo.form.Hidden({
24928                 name : n
24929             });
24930             add.render(this.el);
24931             
24932             this.add( add );
24933         }, this);
24934         
24935     },
24936     /**
24937      * Mark fields in this form invalid in bulk.
24938      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24939      * @return {BasicForm} this
24940      */
24941     markInvalid : function(errors){
24942         if(errors instanceof Array){
24943             for(var i = 0, len = errors.length; i < len; i++){
24944                 var fieldError = errors[i];
24945                 var f = this.findField(fieldError.id);
24946                 if(f){
24947                     f.markInvalid(fieldError.msg);
24948                 }
24949             }
24950         }else{
24951             var field, id;
24952             for(id in errors){
24953                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24954                     field.markInvalid(errors[id]);
24955                 }
24956             }
24957         }
24958         Roo.each(this.childForms || [], function (f) {
24959             f.markInvalid(errors);
24960         });
24961         
24962         return this;
24963     },
24964
24965     /**
24966      * Set values for fields in this form in bulk.
24967      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24968      * @return {BasicForm} this
24969      */
24970     setValues : function(values){
24971         if(values instanceof Array){ // array of objects
24972             for(var i = 0, len = values.length; i < len; i++){
24973                 var v = values[i];
24974                 var f = this.findField(v.id);
24975                 if(f){
24976                     f.setValue(v.value);
24977                     if(this.trackResetOnLoad){
24978                         f.originalValue = f.getValue();
24979                     }
24980                 }
24981             }
24982         }else{ // object hash
24983             var field, id;
24984             for(id in values){
24985                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24986                     
24987                     if (field.setFromData && 
24988                         field.valueField && 
24989                         field.displayField &&
24990                         // combos' with local stores can 
24991                         // be queried via setValue()
24992                         // to set their value..
24993                         (field.store && !field.store.isLocal)
24994                         ) {
24995                         // it's a combo
24996                         var sd = { };
24997                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24998                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24999                         field.setFromData(sd);
25000                         
25001                     } else {
25002                         field.setValue(values[id]);
25003                     }
25004                     
25005                     
25006                     if(this.trackResetOnLoad){
25007                         field.originalValue = field.getValue();
25008                     }
25009                 }
25010             }
25011         }
25012         this.resetHasChanged();
25013         
25014         
25015         Roo.each(this.childForms || [], function (f) {
25016             f.setValues(values);
25017             f.resetHasChanged();
25018         });
25019                 
25020         return this;
25021     },
25022  
25023     /**
25024      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25025      * they are returned as an array.
25026      * @param {Boolean} asString
25027      * @return {Object}
25028      */
25029     getValues : function(asString){
25030         if (this.childForms) {
25031             // copy values from the child forms
25032             Roo.each(this.childForms, function (f) {
25033                 this.setValues(f.getValues());
25034             }, this);
25035         }
25036         
25037         // use formdata
25038         if (typeof(FormData) != 'undefined' && asString !== true) {
25039             // this relies on a 'recent' version of chrome apparently...
25040             try {
25041                 var fd = (new FormData(this.el.dom)).entries();
25042                 var ret = {};
25043                 var ent = fd.next();
25044                 while (!ent.done) {
25045                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25046                     ent = fd.next();
25047                 };
25048                 return ret;
25049             } catch(e) {
25050                 
25051             }
25052             
25053         }
25054         
25055         
25056         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25057         if(asString === true){
25058             return fs;
25059         }
25060         return Roo.urlDecode(fs);
25061     },
25062     
25063     /**
25064      * Returns the fields in this form as an object with key/value pairs. 
25065      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25066      * @return {Object}
25067      */
25068     getFieldValues : function(with_hidden)
25069     {
25070         if (this.childForms) {
25071             // copy values from the child forms
25072             // should this call getFieldValues - probably not as we do not currently copy
25073             // hidden fields when we generate..
25074             Roo.each(this.childForms, function (f) {
25075                 this.setValues(f.getValues());
25076             }, this);
25077         }
25078         
25079         var ret = {};
25080         this.items.each(function(f){
25081             if (!f.getName()) {
25082                 return;
25083             }
25084             var v = f.getValue();
25085             if (f.inputType =='radio') {
25086                 if (typeof(ret[f.getName()]) == 'undefined') {
25087                     ret[f.getName()] = ''; // empty..
25088                 }
25089                 
25090                 if (!f.el.dom.checked) {
25091                     return;
25092                     
25093                 }
25094                 v = f.el.dom.value;
25095                 
25096             }
25097             
25098             // not sure if this supported any more..
25099             if ((typeof(v) == 'object') && f.getRawValue) {
25100                 v = f.getRawValue() ; // dates..
25101             }
25102             // combo boxes where name != hiddenName...
25103             if (f.name != f.getName()) {
25104                 ret[f.name] = f.getRawValue();
25105             }
25106             ret[f.getName()] = v;
25107         });
25108         
25109         return ret;
25110     },
25111
25112     /**
25113      * Clears all invalid messages in this form.
25114      * @return {BasicForm} this
25115      */
25116     clearInvalid : function(){
25117         this.items.each(function(f){
25118            f.clearInvalid();
25119         });
25120         
25121         Roo.each(this.childForms || [], function (f) {
25122             f.clearInvalid();
25123         });
25124         
25125         
25126         return this;
25127     },
25128
25129     /**
25130      * Resets this form.
25131      * @return {BasicForm} this
25132      */
25133     reset : function(){
25134         this.items.each(function(f){
25135             f.reset();
25136         });
25137         
25138         Roo.each(this.childForms || [], function (f) {
25139             f.reset();
25140         });
25141         this.resetHasChanged();
25142         
25143         return this;
25144     },
25145
25146     /**
25147      * Add Roo.form components to this form.
25148      * @param {Field} field1
25149      * @param {Field} field2 (optional)
25150      * @param {Field} etc (optional)
25151      * @return {BasicForm} this
25152      */
25153     add : function(){
25154         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25155         return this;
25156     },
25157
25158
25159     /**
25160      * Removes a field from the items collection (does NOT remove its markup).
25161      * @param {Field} field
25162      * @return {BasicForm} this
25163      */
25164     remove : function(field){
25165         this.items.remove(field);
25166         return this;
25167     },
25168
25169     /**
25170      * Looks at the fields in this form, checks them for an id attribute,
25171      * and calls applyTo on the existing dom element with that id.
25172      * @return {BasicForm} this
25173      */
25174     render : function(){
25175         this.items.each(function(f){
25176             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25177                 f.applyTo(f.id);
25178             }
25179         });
25180         return this;
25181     },
25182
25183     /**
25184      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25185      * @param {Object} values
25186      * @return {BasicForm} this
25187      */
25188     applyToFields : function(o){
25189         this.items.each(function(f){
25190            Roo.apply(f, o);
25191         });
25192         return this;
25193     },
25194
25195     /**
25196      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25197      * @param {Object} values
25198      * @return {BasicForm} this
25199      */
25200     applyIfToFields : function(o){
25201         this.items.each(function(f){
25202            Roo.applyIf(f, o);
25203         });
25204         return this;
25205     }
25206 });
25207
25208 // back compat
25209 Roo.BasicForm = Roo.form.BasicForm;
25210
25211 Roo.apply(Roo.form.BasicForm, {
25212     
25213     popover : {
25214         
25215         padding : 5,
25216         
25217         isApplied : false,
25218         
25219         isMasked : false,
25220         
25221         form : false,
25222         
25223         target : false,
25224         
25225         intervalID : false,
25226         
25227         maskEl : false,
25228         
25229         apply : function()
25230         {
25231             if(this.isApplied){
25232                 return;
25233             }
25234             
25235             this.maskEl = {
25236                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25237                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25238                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25239                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25240             };
25241             
25242             this.maskEl.top.enableDisplayMode("block");
25243             this.maskEl.left.enableDisplayMode("block");
25244             this.maskEl.bottom.enableDisplayMode("block");
25245             this.maskEl.right.enableDisplayMode("block");
25246             
25247             Roo.get(document.body).on('click', function(){
25248                 this.unmask();
25249             }, this);
25250             
25251             Roo.get(document.body).on('touchstart', function(){
25252                 this.unmask();
25253             }, this);
25254             
25255             this.isApplied = true
25256         },
25257         
25258         mask : function(form, target)
25259         {
25260             this.form = form;
25261             
25262             this.target = target;
25263             
25264             if(!this.form.errorMask || !target.el){
25265                 return;
25266             }
25267             
25268             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25269             
25270             var ot = this.target.el.calcOffsetsTo(scrollable);
25271             
25272             var scrollTo = ot[1] - this.form.maskOffset;
25273             
25274             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25275             
25276             scrollable.scrollTo('top', scrollTo);
25277             
25278             var el = this.target.wrap || this.target.el;
25279             
25280             var box = el.getBox();
25281             
25282             this.maskEl.top.setStyle('position', 'absolute');
25283             this.maskEl.top.setStyle('z-index', 10000);
25284             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25285             this.maskEl.top.setLeft(0);
25286             this.maskEl.top.setTop(0);
25287             this.maskEl.top.show();
25288             
25289             this.maskEl.left.setStyle('position', 'absolute');
25290             this.maskEl.left.setStyle('z-index', 10000);
25291             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25292             this.maskEl.left.setLeft(0);
25293             this.maskEl.left.setTop(box.y - this.padding);
25294             this.maskEl.left.show();
25295
25296             this.maskEl.bottom.setStyle('position', 'absolute');
25297             this.maskEl.bottom.setStyle('z-index', 10000);
25298             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25299             this.maskEl.bottom.setLeft(0);
25300             this.maskEl.bottom.setTop(box.bottom + this.padding);
25301             this.maskEl.bottom.show();
25302
25303             this.maskEl.right.setStyle('position', 'absolute');
25304             this.maskEl.right.setStyle('z-index', 10000);
25305             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25306             this.maskEl.right.setLeft(box.right + this.padding);
25307             this.maskEl.right.setTop(box.y - this.padding);
25308             this.maskEl.right.show();
25309
25310             this.intervalID = window.setInterval(function() {
25311                 Roo.form.BasicForm.popover.unmask();
25312             }, 10000);
25313
25314             window.onwheel = function(){ return false;};
25315             
25316             (function(){ this.isMasked = true; }).defer(500, this);
25317             
25318         },
25319         
25320         unmask : function()
25321         {
25322             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25323                 return;
25324             }
25325             
25326             this.maskEl.top.setStyle('position', 'absolute');
25327             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25328             this.maskEl.top.hide();
25329
25330             this.maskEl.left.setStyle('position', 'absolute');
25331             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25332             this.maskEl.left.hide();
25333
25334             this.maskEl.bottom.setStyle('position', 'absolute');
25335             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25336             this.maskEl.bottom.hide();
25337
25338             this.maskEl.right.setStyle('position', 'absolute');
25339             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25340             this.maskEl.right.hide();
25341             
25342             window.onwheel = function(){ return true;};
25343             
25344             if(this.intervalID){
25345                 window.clearInterval(this.intervalID);
25346                 this.intervalID = false;
25347             }
25348             
25349             this.isMasked = false;
25350             
25351         }
25352         
25353     }
25354     
25355 });/*
25356  * Based on:
25357  * Ext JS Library 1.1.1
25358  * Copyright(c) 2006-2007, Ext JS, LLC.
25359  *
25360  * Originally Released Under LGPL - original licence link has changed is not relivant.
25361  *
25362  * Fork - LGPL
25363  * <script type="text/javascript">
25364  */
25365
25366 /**
25367  * @class Roo.form.Form
25368  * @extends Roo.form.BasicForm
25369  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25370  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25371  * @constructor
25372  * @param {Object} config Configuration options
25373  */
25374 Roo.form.Form = function(config){
25375     var xitems =  [];
25376     if (config.items) {
25377         xitems = config.items;
25378         delete config.items;
25379     }
25380    
25381     
25382     Roo.form.Form.superclass.constructor.call(this, null, config);
25383     this.url = this.url || this.action;
25384     if(!this.root){
25385         this.root = new Roo.form.Layout(Roo.applyIf({
25386             id: Roo.id()
25387         }, config));
25388     }
25389     this.active = this.root;
25390     /**
25391      * Array of all the buttons that have been added to this form via {@link addButton}
25392      * @type Array
25393      */
25394     this.buttons = [];
25395     this.allItems = [];
25396     this.addEvents({
25397         /**
25398          * @event clientvalidation
25399          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25400          * @param {Form} this
25401          * @param {Boolean} valid true if the form has passed client-side validation
25402          */
25403         clientvalidation: true,
25404         /**
25405          * @event rendered
25406          * Fires when the form is rendered
25407          * @param {Roo.form.Form} form
25408          */
25409         rendered : true
25410     });
25411     
25412     if (this.progressUrl) {
25413             // push a hidden field onto the list of fields..
25414             this.addxtype( {
25415                     xns: Roo.form, 
25416                     xtype : 'Hidden', 
25417                     name : 'UPLOAD_IDENTIFIER' 
25418             });
25419         }
25420         
25421     
25422     Roo.each(xitems, this.addxtype, this);
25423     
25424 };
25425
25426 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25427      /**
25428      * @cfg {Roo.Button} buttons[] buttons at bottom of form
25429      */
25430     
25431     /**
25432      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25433      */
25434     /**
25435      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25436      */
25437     /**
25438      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25439      */
25440     buttonAlign:'center',
25441
25442     /**
25443      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25444      */
25445     minButtonWidth:75,
25446
25447     /**
25448      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25449      * This property cascades to child containers if not set.
25450      */
25451     labelAlign:'left',
25452
25453     /**
25454      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25455      * fires a looping event with that state. This is required to bind buttons to the valid
25456      * state using the config value formBind:true on the button.
25457      */
25458     monitorValid : false,
25459
25460     /**
25461      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25462      */
25463     monitorPoll : 200,
25464     
25465     /**
25466      * @cfg {String} progressUrl - Url to return progress data 
25467      */
25468     
25469     progressUrl : false,
25470     /**
25471      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25472      * sending a formdata with extra parameters - eg uploaded elements.
25473      */
25474     
25475     formData : false,
25476     
25477     /**
25478      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25479      * fields are added and the column is closed. If no fields are passed the column remains open
25480      * until end() is called.
25481      * @param {Object} config The config to pass to the column
25482      * @param {Field} field1 (optional)
25483      * @param {Field} field2 (optional)
25484      * @param {Field} etc (optional)
25485      * @return Column The column container object
25486      */
25487     column : function(c){
25488         var col = new Roo.form.Column(c);
25489         this.start(col);
25490         if(arguments.length > 1){ // duplicate code required because of Opera
25491             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25492             this.end();
25493         }
25494         return col;
25495     },
25496
25497     /**
25498      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25499      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25500      * until end() is called.
25501      * @param {Object} config The config to pass to the fieldset
25502      * @param {Field} field1 (optional)
25503      * @param {Field} field2 (optional)
25504      * @param {Field} etc (optional)
25505      * @return FieldSet The fieldset container object
25506      */
25507     fieldset : function(c){
25508         var fs = new Roo.form.FieldSet(c);
25509         this.start(fs);
25510         if(arguments.length > 1){ // duplicate code required because of Opera
25511             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25512             this.end();
25513         }
25514         return fs;
25515     },
25516
25517     /**
25518      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25519      * fields are added and the container is closed. If no fields are passed the container remains open
25520      * until end() is called.
25521      * @param {Object} config The config to pass to the Layout
25522      * @param {Field} field1 (optional)
25523      * @param {Field} field2 (optional)
25524      * @param {Field} etc (optional)
25525      * @return Layout The container object
25526      */
25527     container : function(c){
25528         var l = new Roo.form.Layout(c);
25529         this.start(l);
25530         if(arguments.length > 1){ // duplicate code required because of Opera
25531             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25532             this.end();
25533         }
25534         return l;
25535     },
25536
25537     /**
25538      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25539      * @param {Object} container A Roo.form.Layout or subclass of Layout
25540      * @return {Form} this
25541      */
25542     start : function(c){
25543         // cascade label info
25544         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25545         this.active.stack.push(c);
25546         c.ownerCt = this.active;
25547         this.active = c;
25548         return this;
25549     },
25550
25551     /**
25552      * Closes the current open container
25553      * @return {Form} this
25554      */
25555     end : function(){
25556         if(this.active == this.root){
25557             return this;
25558         }
25559         this.active = this.active.ownerCt;
25560         return this;
25561     },
25562
25563     /**
25564      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25565      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25566      * as the label of the field.
25567      * @param {Field} field1
25568      * @param {Field} field2 (optional)
25569      * @param {Field} etc. (optional)
25570      * @return {Form} this
25571      */
25572     add : function(){
25573         this.active.stack.push.apply(this.active.stack, arguments);
25574         this.allItems.push.apply(this.allItems,arguments);
25575         var r = [];
25576         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25577             if(a[i].isFormField){
25578                 r.push(a[i]);
25579             }
25580         }
25581         if(r.length > 0){
25582             Roo.form.Form.superclass.add.apply(this, r);
25583         }
25584         return this;
25585     },
25586     
25587
25588     
25589     
25590     
25591      /**
25592      * Find any element that has been added to a form, using it's ID or name
25593      * This can include framesets, columns etc. along with regular fields..
25594      * @param {String} id - id or name to find.
25595      
25596      * @return {Element} e - or false if nothing found.
25597      */
25598     findbyId : function(id)
25599     {
25600         var ret = false;
25601         if (!id) {
25602             return ret;
25603         }
25604         Roo.each(this.allItems, function(f){
25605             if (f.id == id || f.name == id ){
25606                 ret = f;
25607                 return false;
25608             }
25609         });
25610         return ret;
25611     },
25612
25613     
25614     
25615     /**
25616      * Render this form into the passed container. This should only be called once!
25617      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25618      * @return {Form} this
25619      */
25620     render : function(ct)
25621     {
25622         
25623         
25624         
25625         ct = Roo.get(ct);
25626         var o = this.autoCreate || {
25627             tag: 'form',
25628             method : this.method || 'POST',
25629             id : this.id || Roo.id()
25630         };
25631         this.initEl(ct.createChild(o));
25632
25633         this.root.render(this.el);
25634         
25635        
25636              
25637         this.items.each(function(f){
25638             f.render('x-form-el-'+f.id);
25639         });
25640
25641         if(this.buttons.length > 0){
25642             // tables are required to maintain order and for correct IE layout
25643             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25644                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25645                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25646             }}, null, true);
25647             var tr = tb.getElementsByTagName('tr')[0];
25648             for(var i = 0, len = this.buttons.length; i < len; i++) {
25649                 var b = this.buttons[i];
25650                 var td = document.createElement('td');
25651                 td.className = 'x-form-btn-td';
25652                 b.render(tr.appendChild(td));
25653             }
25654         }
25655         if(this.monitorValid){ // initialize after render
25656             this.startMonitoring();
25657         }
25658         this.fireEvent('rendered', this);
25659         return this;
25660     },
25661
25662     /**
25663      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25664      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25665      * object or a valid Roo.DomHelper element config
25666      * @param {Function} handler The function called when the button is clicked
25667      * @param {Object} scope (optional) The scope of the handler function
25668      * @return {Roo.Button}
25669      */
25670     addButton : function(config, handler, scope){
25671         var bc = {
25672             handler: handler,
25673             scope: scope,
25674             minWidth: this.minButtonWidth,
25675             hideParent:true
25676         };
25677         if(typeof config == "string"){
25678             bc.text = config;
25679         }else{
25680             Roo.apply(bc, config);
25681         }
25682         var btn = new Roo.Button(null, bc);
25683         this.buttons.push(btn);
25684         return btn;
25685     },
25686
25687      /**
25688      * Adds a series of form elements (using the xtype property as the factory method.
25689      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25690      * @param {Object} config 
25691      */
25692     
25693     addxtype : function()
25694     {
25695         var ar = Array.prototype.slice.call(arguments, 0);
25696         var ret = false;
25697         for(var i = 0; i < ar.length; i++) {
25698             if (!ar[i]) {
25699                 continue; // skip -- if this happends something invalid got sent, we 
25700                 // should ignore it, as basically that interface element will not show up
25701                 // and that should be pretty obvious!!
25702             }
25703             
25704             if (Roo.form[ar[i].xtype]) {
25705                 ar[i].form = this;
25706                 var fe = Roo.factory(ar[i], Roo.form);
25707                 if (!ret) {
25708                     ret = fe;
25709                 }
25710                 fe.form = this;
25711                 if (fe.store) {
25712                     fe.store.form = this;
25713                 }
25714                 if (fe.isLayout) {  
25715                          
25716                     this.start(fe);
25717                     this.allItems.push(fe);
25718                     if (fe.items && fe.addxtype) {
25719                         fe.addxtype.apply(fe, fe.items);
25720                         delete fe.items;
25721                     }
25722                      this.end();
25723                     continue;
25724                 }
25725                 
25726                 
25727                  
25728                 this.add(fe);
25729               //  console.log('adding ' + ar[i].xtype);
25730             }
25731             if (ar[i].xtype == 'Button') {  
25732                 //console.log('adding button');
25733                 //console.log(ar[i]);
25734                 this.addButton(ar[i]);
25735                 this.allItems.push(fe);
25736                 continue;
25737             }
25738             
25739             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25740                 alert('end is not supported on xtype any more, use items');
25741             //    this.end();
25742             //    //console.log('adding end');
25743             }
25744             
25745         }
25746         return ret;
25747     },
25748     
25749     /**
25750      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25751      * option "monitorValid"
25752      */
25753     startMonitoring : function(){
25754         if(!this.bound){
25755             this.bound = true;
25756             Roo.TaskMgr.start({
25757                 run : this.bindHandler,
25758                 interval : this.monitorPoll || 200,
25759                 scope: this
25760             });
25761         }
25762     },
25763
25764     /**
25765      * Stops monitoring of the valid state of this form
25766      */
25767     stopMonitoring : function(){
25768         this.bound = false;
25769     },
25770
25771     // private
25772     bindHandler : function(){
25773         if(!this.bound){
25774             return false; // stops binding
25775         }
25776         var valid = true;
25777         this.items.each(function(f){
25778             if(!f.isValid(true)){
25779                 valid = false;
25780                 return false;
25781             }
25782         });
25783         for(var i = 0, len = this.buttons.length; i < len; i++){
25784             var btn = this.buttons[i];
25785             if(btn.formBind === true && btn.disabled === valid){
25786                 btn.setDisabled(!valid);
25787             }
25788         }
25789         this.fireEvent('clientvalidation', this, valid);
25790     }
25791     
25792     
25793     
25794     
25795     
25796     
25797     
25798     
25799 });
25800
25801
25802 // back compat
25803 Roo.Form = Roo.form.Form;
25804 /*
25805  * Based on:
25806  * Ext JS Library 1.1.1
25807  * Copyright(c) 2006-2007, Ext JS, LLC.
25808  *
25809  * Originally Released Under LGPL - original licence link has changed is not relivant.
25810  *
25811  * Fork - LGPL
25812  * <script type="text/javascript">
25813  */
25814
25815 // as we use this in bootstrap.
25816 Roo.namespace('Roo.form');
25817  /**
25818  * @class Roo.form.Action
25819  * Internal Class used to handle form actions
25820  * @constructor
25821  * @param {Roo.form.BasicForm} el The form element or its id
25822  * @param {Object} config Configuration options
25823  */
25824
25825  
25826  
25827 // define the action interface
25828 Roo.form.Action = function(form, options){
25829     this.form = form;
25830     this.options = options || {};
25831 };
25832 /**
25833  * Client Validation Failed
25834  * @const 
25835  */
25836 Roo.form.Action.CLIENT_INVALID = 'client';
25837 /**
25838  * Server Validation Failed
25839  * @const 
25840  */
25841 Roo.form.Action.SERVER_INVALID = 'server';
25842  /**
25843  * Connect to Server Failed
25844  * @const 
25845  */
25846 Roo.form.Action.CONNECT_FAILURE = 'connect';
25847 /**
25848  * Reading Data from Server Failed
25849  * @const 
25850  */
25851 Roo.form.Action.LOAD_FAILURE = 'load';
25852
25853 Roo.form.Action.prototype = {
25854     type : 'default',
25855     failureType : undefined,
25856     response : undefined,
25857     result : undefined,
25858
25859     // interface method
25860     run : function(options){
25861
25862     },
25863
25864     // interface method
25865     success : function(response){
25866
25867     },
25868
25869     // interface method
25870     handleResponse : function(response){
25871
25872     },
25873
25874     // default connection failure
25875     failure : function(response){
25876         
25877         this.response = response;
25878         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25879         this.form.afterAction(this, false);
25880     },
25881
25882     processResponse : function(response){
25883         this.response = response;
25884         if(!response.responseText){
25885             return true;
25886         }
25887         this.result = this.handleResponse(response);
25888         return this.result;
25889     },
25890
25891     // utility functions used internally
25892     getUrl : function(appendParams){
25893         var url = this.options.url || this.form.url || this.form.el.dom.action;
25894         if(appendParams){
25895             var p = this.getParams();
25896             if(p){
25897                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25898             }
25899         }
25900         return url;
25901     },
25902
25903     getMethod : function(){
25904         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25905     },
25906
25907     getParams : function(){
25908         var bp = this.form.baseParams;
25909         var p = this.options.params;
25910         if(p){
25911             if(typeof p == "object"){
25912                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25913             }else if(typeof p == 'string' && bp){
25914                 p += '&' + Roo.urlEncode(bp);
25915             }
25916         }else if(bp){
25917             p = Roo.urlEncode(bp);
25918         }
25919         return p;
25920     },
25921
25922     createCallback : function(){
25923         return {
25924             success: this.success,
25925             failure: this.failure,
25926             scope: this,
25927             timeout: (this.form.timeout*1000),
25928             upload: this.form.fileUpload ? this.success : undefined
25929         };
25930     }
25931 };
25932
25933 Roo.form.Action.Submit = function(form, options){
25934     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25935 };
25936
25937 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25938     type : 'submit',
25939
25940     haveProgress : false,
25941     uploadComplete : false,
25942     
25943     // uploadProgress indicator.
25944     uploadProgress : function()
25945     {
25946         if (!this.form.progressUrl) {
25947             return;
25948         }
25949         
25950         if (!this.haveProgress) {
25951             Roo.MessageBox.progress("Uploading", "Uploading");
25952         }
25953         if (this.uploadComplete) {
25954            Roo.MessageBox.hide();
25955            return;
25956         }
25957         
25958         this.haveProgress = true;
25959    
25960         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25961         
25962         var c = new Roo.data.Connection();
25963         c.request({
25964             url : this.form.progressUrl,
25965             params: {
25966                 id : uid
25967             },
25968             method: 'GET',
25969             success : function(req){
25970                //console.log(data);
25971                 var rdata = false;
25972                 var edata;
25973                 try  {
25974                    rdata = Roo.decode(req.responseText)
25975                 } catch (e) {
25976                     Roo.log("Invalid data from server..");
25977                     Roo.log(edata);
25978                     return;
25979                 }
25980                 if (!rdata || !rdata.success) {
25981                     Roo.log(rdata);
25982                     Roo.MessageBox.alert(Roo.encode(rdata));
25983                     return;
25984                 }
25985                 var data = rdata.data;
25986                 
25987                 if (this.uploadComplete) {
25988                    Roo.MessageBox.hide();
25989                    return;
25990                 }
25991                    
25992                 if (data){
25993                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25994                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25995                     );
25996                 }
25997                 this.uploadProgress.defer(2000,this);
25998             },
25999        
26000             failure: function(data) {
26001                 Roo.log('progress url failed ');
26002                 Roo.log(data);
26003             },
26004             scope : this
26005         });
26006            
26007     },
26008     
26009     
26010     run : function()
26011     {
26012         // run get Values on the form, so it syncs any secondary forms.
26013         this.form.getValues();
26014         
26015         var o = this.options;
26016         var method = this.getMethod();
26017         var isPost = method == 'POST';
26018         if(o.clientValidation === false || this.form.isValid()){
26019             
26020             if (this.form.progressUrl) {
26021                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26022                     (new Date() * 1) + '' + Math.random());
26023                     
26024             } 
26025             
26026             
26027             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26028                 form:this.form.el.dom,
26029                 url:this.getUrl(!isPost),
26030                 method: method,
26031                 params:isPost ? this.getParams() : null,
26032                 isUpload: this.form.fileUpload,
26033                 formData : this.form.formData
26034             }));
26035             
26036             this.uploadProgress();
26037
26038         }else if (o.clientValidation !== false){ // client validation failed
26039             this.failureType = Roo.form.Action.CLIENT_INVALID;
26040             this.form.afterAction(this, false);
26041         }
26042     },
26043
26044     success : function(response)
26045     {
26046         this.uploadComplete= true;
26047         if (this.haveProgress) {
26048             Roo.MessageBox.hide();
26049         }
26050         
26051         
26052         var result = this.processResponse(response);
26053         if(result === true || result.success){
26054             this.form.afterAction(this, true);
26055             return;
26056         }
26057         if(result.errors){
26058             this.form.markInvalid(result.errors);
26059             this.failureType = Roo.form.Action.SERVER_INVALID;
26060         }
26061         this.form.afterAction(this, false);
26062     },
26063     failure : function(response)
26064     {
26065         this.uploadComplete= true;
26066         if (this.haveProgress) {
26067             Roo.MessageBox.hide();
26068         }
26069         
26070         this.response = response;
26071         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26072         this.form.afterAction(this, false);
26073     },
26074     
26075     handleResponse : function(response){
26076         if(this.form.errorReader){
26077             var rs = this.form.errorReader.read(response);
26078             var errors = [];
26079             if(rs.records){
26080                 for(var i = 0, len = rs.records.length; i < len; i++) {
26081                     var r = rs.records[i];
26082                     errors[i] = r.data;
26083                 }
26084             }
26085             if(errors.length < 1){
26086                 errors = null;
26087             }
26088             return {
26089                 success : rs.success,
26090                 errors : errors
26091             };
26092         }
26093         var ret = false;
26094         try {
26095             ret = Roo.decode(response.responseText);
26096         } catch (e) {
26097             ret = {
26098                 success: false,
26099                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26100                 errors : []
26101             };
26102         }
26103         return ret;
26104         
26105     }
26106 });
26107
26108
26109 Roo.form.Action.Load = function(form, options){
26110     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26111     this.reader = this.form.reader;
26112 };
26113
26114 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26115     type : 'load',
26116
26117     run : function(){
26118         
26119         Roo.Ajax.request(Roo.apply(
26120                 this.createCallback(), {
26121                     method:this.getMethod(),
26122                     url:this.getUrl(false),
26123                     params:this.getParams()
26124         }));
26125     },
26126
26127     success : function(response){
26128         
26129         var result = this.processResponse(response);
26130         if(result === true || !result.success || !result.data){
26131             this.failureType = Roo.form.Action.LOAD_FAILURE;
26132             this.form.afterAction(this, false);
26133             return;
26134         }
26135         this.form.clearInvalid();
26136         this.form.setValues(result.data);
26137         this.form.afterAction(this, true);
26138     },
26139
26140     handleResponse : function(response){
26141         if(this.form.reader){
26142             var rs = this.form.reader.read(response);
26143             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26144             return {
26145                 success : rs.success,
26146                 data : data
26147             };
26148         }
26149         return Roo.decode(response.responseText);
26150     }
26151 });
26152
26153 Roo.form.Action.ACTION_TYPES = {
26154     'load' : Roo.form.Action.Load,
26155     'submit' : Roo.form.Action.Submit
26156 };/*
26157  * Based on:
26158  * Ext JS Library 1.1.1
26159  * Copyright(c) 2006-2007, Ext JS, LLC.
26160  *
26161  * Originally Released Under LGPL - original licence link has changed is not relivant.
26162  *
26163  * Fork - LGPL
26164  * <script type="text/javascript">
26165  */
26166  
26167 /**
26168  * @class Roo.form.Layout
26169  * @extends Roo.Component
26170  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26171  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26172  * @constructor
26173  * @param {Object} config Configuration options
26174  */
26175 Roo.form.Layout = function(config){
26176     var xitems = [];
26177     if (config.items) {
26178         xitems = config.items;
26179         delete config.items;
26180     }
26181     Roo.form.Layout.superclass.constructor.call(this, config);
26182     this.stack = [];
26183     Roo.each(xitems, this.addxtype, this);
26184      
26185 };
26186
26187 Roo.extend(Roo.form.Layout, Roo.Component, {
26188     /**
26189      * @cfg {String/Object} autoCreate
26190      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26191      */
26192     /**
26193      * @cfg {String/Object/Function} style
26194      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26195      * a function which returns such a specification.
26196      */
26197     /**
26198      * @cfg {String} labelAlign
26199      * Valid values are "left," "top" and "right" (defaults to "left")
26200      */
26201     /**
26202      * @cfg {Number} labelWidth
26203      * Fixed width in pixels of all field labels (defaults to undefined)
26204      */
26205     /**
26206      * @cfg {Boolean} clear
26207      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26208      */
26209     clear : true,
26210     /**
26211      * @cfg {String} labelSeparator
26212      * The separator to use after field labels (defaults to ':')
26213      */
26214     labelSeparator : ':',
26215     /**
26216      * @cfg {Boolean} hideLabels
26217      * True to suppress the display of field labels in this layout (defaults to false)
26218      */
26219     hideLabels : false,
26220
26221     // private
26222     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26223     
26224     isLayout : true,
26225     
26226     // private
26227     onRender : function(ct, position){
26228         if(this.el){ // from markup
26229             this.el = Roo.get(this.el);
26230         }else {  // generate
26231             var cfg = this.getAutoCreate();
26232             this.el = ct.createChild(cfg, position);
26233         }
26234         if(this.style){
26235             this.el.applyStyles(this.style);
26236         }
26237         if(this.labelAlign){
26238             this.el.addClass('x-form-label-'+this.labelAlign);
26239         }
26240         if(this.hideLabels){
26241             this.labelStyle = "display:none";
26242             this.elementStyle = "padding-left:0;";
26243         }else{
26244             if(typeof this.labelWidth == 'number'){
26245                 this.labelStyle = "width:"+this.labelWidth+"px;";
26246                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26247             }
26248             if(this.labelAlign == 'top'){
26249                 this.labelStyle = "width:auto;";
26250                 this.elementStyle = "padding-left:0;";
26251             }
26252         }
26253         var stack = this.stack;
26254         var slen = stack.length;
26255         if(slen > 0){
26256             if(!this.fieldTpl){
26257                 var t = new Roo.Template(
26258                     '<div class="x-form-item {5}">',
26259                         '<label for="{0}" style="{2}">{1}{4}</label>',
26260                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26261                         '</div>',
26262                     '</div><div class="x-form-clear-left"></div>'
26263                 );
26264                 t.disableFormats = true;
26265                 t.compile();
26266                 Roo.form.Layout.prototype.fieldTpl = t;
26267             }
26268             for(var i = 0; i < slen; i++) {
26269                 if(stack[i].isFormField){
26270                     this.renderField(stack[i]);
26271                 }else{
26272                     this.renderComponent(stack[i]);
26273                 }
26274             }
26275         }
26276         if(this.clear){
26277             this.el.createChild({cls:'x-form-clear'});
26278         }
26279     },
26280
26281     // private
26282     renderField : function(f){
26283         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26284                f.id, //0
26285                f.fieldLabel, //1
26286                f.labelStyle||this.labelStyle||'', //2
26287                this.elementStyle||'', //3
26288                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26289                f.itemCls||this.itemCls||''  //5
26290        ], true).getPrevSibling());
26291     },
26292
26293     // private
26294     renderComponent : function(c){
26295         c.render(c.isLayout ? this.el : this.el.createChild());    
26296     },
26297     /**
26298      * Adds a object form elements (using the xtype property as the factory method.)
26299      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26300      * @param {Object} config 
26301      */
26302     addxtype : function(o)
26303     {
26304         // create the lement.
26305         o.form = this.form;
26306         var fe = Roo.factory(o, Roo.form);
26307         this.form.allItems.push(fe);
26308         this.stack.push(fe);
26309         
26310         if (fe.isFormField) {
26311             this.form.items.add(fe);
26312         }
26313          
26314         return fe;
26315     }
26316 });
26317
26318 /**
26319  * @class Roo.form.Column
26320  * @extends Roo.form.Layout
26321  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26322  * @constructor
26323  * @param {Object} config Configuration options
26324  */
26325 Roo.form.Column = function(config){
26326     Roo.form.Column.superclass.constructor.call(this, config);
26327 };
26328
26329 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26330     /**
26331      * @cfg {Number/String} width
26332      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26333      */
26334     /**
26335      * @cfg {String/Object} autoCreate
26336      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26337      */
26338
26339     // private
26340     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26341
26342     // private
26343     onRender : function(ct, position){
26344         Roo.form.Column.superclass.onRender.call(this, ct, position);
26345         if(this.width){
26346             this.el.setWidth(this.width);
26347         }
26348     }
26349 });
26350
26351
26352 /**
26353  * @class Roo.form.Row
26354  * @extends Roo.form.Layout
26355  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26356  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26357  * @constructor
26358  * @param {Object} config Configuration options
26359  */
26360
26361  
26362 Roo.form.Row = function(config){
26363     Roo.form.Row.superclass.constructor.call(this, config);
26364 };
26365  
26366 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26367       /**
26368      * @cfg {Number/String} width
26369      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26370      */
26371     /**
26372      * @cfg {Number/String} height
26373      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26374      */
26375     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26376     
26377     padWidth : 20,
26378     // private
26379     onRender : function(ct, position){
26380         //console.log('row render');
26381         if(!this.rowTpl){
26382             var t = new Roo.Template(
26383                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26384                     '<label for="{0}" style="{2}">{1}{4}</label>',
26385                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26386                     '</div>',
26387                 '</div>'
26388             );
26389             t.disableFormats = true;
26390             t.compile();
26391             Roo.form.Layout.prototype.rowTpl = t;
26392         }
26393         this.fieldTpl = this.rowTpl;
26394         
26395         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26396         var labelWidth = 100;
26397         
26398         if ((this.labelAlign != 'top')) {
26399             if (typeof this.labelWidth == 'number') {
26400                 labelWidth = this.labelWidth
26401             }
26402             this.padWidth =  20 + labelWidth;
26403             
26404         }
26405         
26406         Roo.form.Column.superclass.onRender.call(this, ct, position);
26407         if(this.width){
26408             this.el.setWidth(this.width);
26409         }
26410         if(this.height){
26411             this.el.setHeight(this.height);
26412         }
26413     },
26414     
26415     // private
26416     renderField : function(f){
26417         f.fieldEl = this.fieldTpl.append(this.el, [
26418                f.id, f.fieldLabel,
26419                f.labelStyle||this.labelStyle||'',
26420                this.elementStyle||'',
26421                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26422                f.itemCls||this.itemCls||'',
26423                f.width ? f.width + this.padWidth : 160 + this.padWidth
26424        ],true);
26425     }
26426 });
26427  
26428
26429 /**
26430  * @class Roo.form.FieldSet
26431  * @extends Roo.form.Layout
26432  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26433  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26434  * @constructor
26435  * @param {Object} config Configuration options
26436  */
26437 Roo.form.FieldSet = function(config){
26438     Roo.form.FieldSet.superclass.constructor.call(this, config);
26439 };
26440
26441 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26442     /**
26443      * @cfg {String} legend
26444      * The text to display as the legend for the FieldSet (defaults to '')
26445      */
26446     /**
26447      * @cfg {String/Object} autoCreate
26448      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26449      */
26450
26451     // private
26452     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26453
26454     // private
26455     onRender : function(ct, position){
26456         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26457         if(this.legend){
26458             this.setLegend(this.legend);
26459         }
26460     },
26461
26462     // private
26463     setLegend : function(text){
26464         if(this.rendered){
26465             this.el.child('legend').update(text);
26466         }
26467     }
26468 });/*
26469  * Based on:
26470  * Ext JS Library 1.1.1
26471  * Copyright(c) 2006-2007, Ext JS, LLC.
26472  *
26473  * Originally Released Under LGPL - original licence link has changed is not relivant.
26474  *
26475  * Fork - LGPL
26476  * <script type="text/javascript">
26477  */
26478 /**
26479  * @class Roo.form.VTypes
26480  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26481  * @static
26482  */
26483 Roo.form.VTypes = function(){
26484     // closure these in so they are only created once.
26485     var alpha = /^[a-zA-Z_]+$/;
26486     var alphanum = /^[a-zA-Z0-9_]+$/;
26487     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26488     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26489
26490     // All these messages and functions are configurable
26491     return {
26492         /**
26493          * The function used to validate email addresses
26494          * @param {String} value The email address
26495          */
26496         'email' : function(v){
26497             return email.test(v);
26498         },
26499         /**
26500          * The error text to display when the email validation function returns false
26501          * @type String
26502          */
26503         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26504         /**
26505          * The keystroke filter mask to be applied on email input
26506          * @type RegExp
26507          */
26508         'emailMask' : /[a-z0-9_\.\-@]/i,
26509
26510         /**
26511          * The function used to validate URLs
26512          * @param {String} value The URL
26513          */
26514         'url' : function(v){
26515             return url.test(v);
26516         },
26517         /**
26518          * The error text to display when the url validation function returns false
26519          * @type String
26520          */
26521         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26522         
26523         /**
26524          * The function used to validate alpha values
26525          * @param {String} value The value
26526          */
26527         'alpha' : function(v){
26528             return alpha.test(v);
26529         },
26530         /**
26531          * The error text to display when the alpha validation function returns false
26532          * @type String
26533          */
26534         'alphaText' : 'This field should only contain letters and _',
26535         /**
26536          * The keystroke filter mask to be applied on alpha input
26537          * @type RegExp
26538          */
26539         'alphaMask' : /[a-z_]/i,
26540
26541         /**
26542          * The function used to validate alphanumeric values
26543          * @param {String} value The value
26544          */
26545         'alphanum' : function(v){
26546             return alphanum.test(v);
26547         },
26548         /**
26549          * The error text to display when the alphanumeric validation function returns false
26550          * @type String
26551          */
26552         'alphanumText' : 'This field should only contain letters, numbers and _',
26553         /**
26554          * The keystroke filter mask to be applied on alphanumeric input
26555          * @type RegExp
26556          */
26557         'alphanumMask' : /[a-z0-9_]/i
26558     };
26559 }();//<script type="text/javascript">
26560
26561 /**
26562  * @class Roo.form.FCKeditor
26563  * @extends Roo.form.TextArea
26564  * Wrapper around the FCKEditor http://www.fckeditor.net
26565  * @constructor
26566  * Creates a new FCKeditor
26567  * @param {Object} config Configuration options
26568  */
26569 Roo.form.FCKeditor = function(config){
26570     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26571     this.addEvents({
26572          /**
26573          * @event editorinit
26574          * Fired when the editor is initialized - you can add extra handlers here..
26575          * @param {FCKeditor} this
26576          * @param {Object} the FCK object.
26577          */
26578         editorinit : true
26579     });
26580     
26581     
26582 };
26583 Roo.form.FCKeditor.editors = { };
26584 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26585 {
26586     //defaultAutoCreate : {
26587     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26588     //},
26589     // private
26590     /**
26591      * @cfg {Object} fck options - see fck manual for details.
26592      */
26593     fckconfig : false,
26594     
26595     /**
26596      * @cfg {Object} fck toolbar set (Basic or Default)
26597      */
26598     toolbarSet : 'Basic',
26599     /**
26600      * @cfg {Object} fck BasePath
26601      */ 
26602     basePath : '/fckeditor/',
26603     
26604     
26605     frame : false,
26606     
26607     value : '',
26608     
26609    
26610     onRender : function(ct, position)
26611     {
26612         if(!this.el){
26613             this.defaultAutoCreate = {
26614                 tag: "textarea",
26615                 style:"width:300px;height:60px;",
26616                 autocomplete: "new-password"
26617             };
26618         }
26619         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26620         /*
26621         if(this.grow){
26622             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26623             if(this.preventScrollbars){
26624                 this.el.setStyle("overflow", "hidden");
26625             }
26626             this.el.setHeight(this.growMin);
26627         }
26628         */
26629         //console.log('onrender' + this.getId() );
26630         Roo.form.FCKeditor.editors[this.getId()] = this;
26631          
26632
26633         this.replaceTextarea() ;
26634         
26635     },
26636     
26637     getEditor : function() {
26638         return this.fckEditor;
26639     },
26640     /**
26641      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26642      * @param {Mixed} value The value to set
26643      */
26644     
26645     
26646     setValue : function(value)
26647     {
26648         //console.log('setValue: ' + value);
26649         
26650         if(typeof(value) == 'undefined') { // not sure why this is happending...
26651             return;
26652         }
26653         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26654         
26655         //if(!this.el || !this.getEditor()) {
26656         //    this.value = value;
26657             //this.setValue.defer(100,this,[value]);    
26658         //    return;
26659         //} 
26660         
26661         if(!this.getEditor()) {
26662             return;
26663         }
26664         
26665         this.getEditor().SetData(value);
26666         
26667         //
26668
26669     },
26670
26671     /**
26672      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26673      * @return {Mixed} value The field value
26674      */
26675     getValue : function()
26676     {
26677         
26678         if (this.frame && this.frame.dom.style.display == 'none') {
26679             return Roo.form.FCKeditor.superclass.getValue.call(this);
26680         }
26681         
26682         if(!this.el || !this.getEditor()) {
26683            
26684            // this.getValue.defer(100,this); 
26685             return this.value;
26686         }
26687        
26688         
26689         var value=this.getEditor().GetData();
26690         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26691         return Roo.form.FCKeditor.superclass.getValue.call(this);
26692         
26693
26694     },
26695
26696     /**
26697      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26698      * @return {Mixed} value The field value
26699      */
26700     getRawValue : function()
26701     {
26702         if (this.frame && this.frame.dom.style.display == 'none') {
26703             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26704         }
26705         
26706         if(!this.el || !this.getEditor()) {
26707             //this.getRawValue.defer(100,this); 
26708             return this.value;
26709             return;
26710         }
26711         
26712         
26713         
26714         var value=this.getEditor().GetData();
26715         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26716         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26717          
26718     },
26719     
26720     setSize : function(w,h) {
26721         
26722         
26723         
26724         //if (this.frame && this.frame.dom.style.display == 'none') {
26725         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26726         //    return;
26727         //}
26728         //if(!this.el || !this.getEditor()) {
26729         //    this.setSize.defer(100,this, [w,h]); 
26730         //    return;
26731         //}
26732         
26733         
26734         
26735         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26736         
26737         this.frame.dom.setAttribute('width', w);
26738         this.frame.dom.setAttribute('height', h);
26739         this.frame.setSize(w,h);
26740         
26741     },
26742     
26743     toggleSourceEdit : function(value) {
26744         
26745       
26746          
26747         this.el.dom.style.display = value ? '' : 'none';
26748         this.frame.dom.style.display = value ?  'none' : '';
26749         
26750     },
26751     
26752     
26753     focus: function(tag)
26754     {
26755         if (this.frame.dom.style.display == 'none') {
26756             return Roo.form.FCKeditor.superclass.focus.call(this);
26757         }
26758         if(!this.el || !this.getEditor()) {
26759             this.focus.defer(100,this, [tag]); 
26760             return;
26761         }
26762         
26763         
26764         
26765         
26766         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26767         this.getEditor().Focus();
26768         if (tgs.length) {
26769             if (!this.getEditor().Selection.GetSelection()) {
26770                 this.focus.defer(100,this, [tag]); 
26771                 return;
26772             }
26773             
26774             
26775             var r = this.getEditor().EditorDocument.createRange();
26776             r.setStart(tgs[0],0);
26777             r.setEnd(tgs[0],0);
26778             this.getEditor().Selection.GetSelection().removeAllRanges();
26779             this.getEditor().Selection.GetSelection().addRange(r);
26780             this.getEditor().Focus();
26781         }
26782         
26783     },
26784     
26785     
26786     
26787     replaceTextarea : function()
26788     {
26789         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26790             return ;
26791         }
26792         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26793         //{
26794             // We must check the elements firstly using the Id and then the name.
26795         var oTextarea = document.getElementById( this.getId() );
26796         
26797         var colElementsByName = document.getElementsByName( this.getId() ) ;
26798          
26799         oTextarea.style.display = 'none' ;
26800
26801         if ( oTextarea.tabIndex ) {            
26802             this.TabIndex = oTextarea.tabIndex ;
26803         }
26804         
26805         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26806         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26807         this.frame = Roo.get(this.getId() + '___Frame')
26808     },
26809     
26810     _getConfigHtml : function()
26811     {
26812         var sConfig = '' ;
26813
26814         for ( var o in this.fckconfig ) {
26815             sConfig += sConfig.length > 0  ? '&amp;' : '';
26816             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26817         }
26818
26819         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26820     },
26821     
26822     
26823     _getIFrameHtml : function()
26824     {
26825         var sFile = 'fckeditor.html' ;
26826         /* no idea what this is about..
26827         try
26828         {
26829             if ( (/fcksource=true/i).test( window.top.location.search ) )
26830                 sFile = 'fckeditor.original.html' ;
26831         }
26832         catch (e) { 
26833         */
26834
26835         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26836         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26837         
26838         
26839         var html = '<iframe id="' + this.getId() +
26840             '___Frame" src="' + sLink +
26841             '" width="' + this.width +
26842             '" height="' + this.height + '"' +
26843             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26844             ' frameborder="0" scrolling="no"></iframe>' ;
26845
26846         return html ;
26847     },
26848     
26849     _insertHtmlBefore : function( html, element )
26850     {
26851         if ( element.insertAdjacentHTML )       {
26852             // IE
26853             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26854         } else { // Gecko
26855             var oRange = document.createRange() ;
26856             oRange.setStartBefore( element ) ;
26857             var oFragment = oRange.createContextualFragment( html );
26858             element.parentNode.insertBefore( oFragment, element ) ;
26859         }
26860     }
26861     
26862     
26863   
26864     
26865     
26866     
26867     
26868
26869 });
26870
26871 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26872
26873 function FCKeditor_OnComplete(editorInstance){
26874     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26875     f.fckEditor = editorInstance;
26876     //console.log("loaded");
26877     f.fireEvent('editorinit', f, editorInstance);
26878
26879   
26880
26881  
26882
26883
26884
26885
26886
26887
26888
26889
26890
26891
26892
26893
26894
26895
26896
26897 //<script type="text/javascript">
26898 /**
26899  * @class Roo.form.GridField
26900  * @extends Roo.form.Field
26901  * Embed a grid (or editable grid into a form)
26902  * STATUS ALPHA
26903  * 
26904  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26905  * it needs 
26906  * xgrid.store = Roo.data.Store
26907  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26908  * xgrid.store.reader = Roo.data.JsonReader 
26909  * 
26910  * 
26911  * @constructor
26912  * Creates a new GridField
26913  * @param {Object} config Configuration options
26914  */
26915 Roo.form.GridField = function(config){
26916     Roo.form.GridField.superclass.constructor.call(this, config);
26917      
26918 };
26919
26920 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26921     /**
26922      * @cfg {Number} width  - used to restrict width of grid..
26923      */
26924     width : 100,
26925     /**
26926      * @cfg {Number} height - used to restrict height of grid..
26927      */
26928     height : 50,
26929      /**
26930      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26931          * 
26932          *}
26933      */
26934     xgrid : false, 
26935     /**
26936      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26937      * {tag: "input", type: "checkbox", autocomplete: "off"})
26938      */
26939    // defaultAutoCreate : { tag: 'div' },
26940     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26941     /**
26942      * @cfg {String} addTitle Text to include for adding a title.
26943      */
26944     addTitle : false,
26945     //
26946     onResize : function(){
26947         Roo.form.Field.superclass.onResize.apply(this, arguments);
26948     },
26949
26950     initEvents : function(){
26951         // Roo.form.Checkbox.superclass.initEvents.call(this);
26952         // has no events...
26953        
26954     },
26955
26956
26957     getResizeEl : function(){
26958         return this.wrap;
26959     },
26960
26961     getPositionEl : function(){
26962         return this.wrap;
26963     },
26964
26965     // private
26966     onRender : function(ct, position){
26967         
26968         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26969         var style = this.style;
26970         delete this.style;
26971         
26972         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26973         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26974         this.viewEl = this.wrap.createChild({ tag: 'div' });
26975         if (style) {
26976             this.viewEl.applyStyles(style);
26977         }
26978         if (this.width) {
26979             this.viewEl.setWidth(this.width);
26980         }
26981         if (this.height) {
26982             this.viewEl.setHeight(this.height);
26983         }
26984         //if(this.inputValue !== undefined){
26985         //this.setValue(this.value);
26986         
26987         
26988         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26989         
26990         
26991         this.grid.render();
26992         this.grid.getDataSource().on('remove', this.refreshValue, this);
26993         this.grid.getDataSource().on('update', this.refreshValue, this);
26994         this.grid.on('afteredit', this.refreshValue, this);
26995  
26996     },
26997      
26998     
26999     /**
27000      * Sets the value of the item. 
27001      * @param {String} either an object  or a string..
27002      */
27003     setValue : function(v){
27004         //this.value = v;
27005         v = v || []; // empty set..
27006         // this does not seem smart - it really only affects memoryproxy grids..
27007         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27008             var ds = this.grid.getDataSource();
27009             // assumes a json reader..
27010             var data = {}
27011             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27012             ds.loadData( data);
27013         }
27014         // clear selection so it does not get stale.
27015         if (this.grid.sm) { 
27016             this.grid.sm.clearSelections();
27017         }
27018         
27019         Roo.form.GridField.superclass.setValue.call(this, v);
27020         this.refreshValue();
27021         // should load data in the grid really....
27022     },
27023     
27024     // private
27025     refreshValue: function() {
27026          var val = [];
27027         this.grid.getDataSource().each(function(r) {
27028             val.push(r.data);
27029         });
27030         this.el.dom.value = Roo.encode(val);
27031     }
27032     
27033      
27034     
27035     
27036 });/*
27037  * Based on:
27038  * Ext JS Library 1.1.1
27039  * Copyright(c) 2006-2007, Ext JS, LLC.
27040  *
27041  * Originally Released Under LGPL - original licence link has changed is not relivant.
27042  *
27043  * Fork - LGPL
27044  * <script type="text/javascript">
27045  */
27046 /**
27047  * @class Roo.form.DisplayField
27048  * @extends Roo.form.Field
27049  * A generic Field to display non-editable data.
27050  * @cfg {Boolean} closable (true|false) default false
27051  * @constructor
27052  * Creates a new Display Field item.
27053  * @param {Object} config Configuration options
27054  */
27055 Roo.form.DisplayField = function(config){
27056     Roo.form.DisplayField.superclass.constructor.call(this, config);
27057     
27058     this.addEvents({
27059         /**
27060          * @event close
27061          * Fires after the click the close btn
27062              * @param {Roo.form.DisplayField} this
27063              */
27064         close : true
27065     });
27066 };
27067
27068 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27069     inputType:      'hidden',
27070     allowBlank:     true,
27071     readOnly:         true,
27072     
27073  
27074     /**
27075      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27076      */
27077     focusClass : undefined,
27078     /**
27079      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27080      */
27081     fieldClass: 'x-form-field',
27082     
27083      /**
27084      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27085      */
27086     valueRenderer: undefined,
27087     
27088     width: 100,
27089     /**
27090      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27091      * {tag: "input", type: "checkbox", autocomplete: "off"})
27092      */
27093      
27094  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27095  
27096     closable : false,
27097     
27098     onResize : function(){
27099         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27100         
27101     },
27102
27103     initEvents : function(){
27104         // Roo.form.Checkbox.superclass.initEvents.call(this);
27105         // has no events...
27106         
27107         if(this.closable){
27108             this.closeEl.on('click', this.onClose, this);
27109         }
27110        
27111     },
27112
27113
27114     getResizeEl : function(){
27115         return this.wrap;
27116     },
27117
27118     getPositionEl : function(){
27119         return this.wrap;
27120     },
27121
27122     // private
27123     onRender : function(ct, position){
27124         
27125         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27126         //if(this.inputValue !== undefined){
27127         this.wrap = this.el.wrap();
27128         
27129         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27130         
27131         if(this.closable){
27132             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27133         }
27134         
27135         if (this.bodyStyle) {
27136             this.viewEl.applyStyles(this.bodyStyle);
27137         }
27138         //this.viewEl.setStyle('padding', '2px');
27139         
27140         this.setValue(this.value);
27141         
27142     },
27143 /*
27144     // private
27145     initValue : Roo.emptyFn,
27146
27147   */
27148
27149         // private
27150     onClick : function(){
27151         
27152     },
27153
27154     /**
27155      * Sets the checked state of the checkbox.
27156      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27157      */
27158     setValue : function(v){
27159         this.value = v;
27160         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27161         // this might be called before we have a dom element..
27162         if (!this.viewEl) {
27163             return;
27164         }
27165         this.viewEl.dom.innerHTML = html;
27166         Roo.form.DisplayField.superclass.setValue.call(this, v);
27167
27168     },
27169     
27170     onClose : function(e)
27171     {
27172         e.preventDefault();
27173         
27174         this.fireEvent('close', this);
27175     }
27176 });/*
27177  * 
27178  * Licence- LGPL
27179  * 
27180  */
27181
27182 /**
27183  * @class Roo.form.DayPicker
27184  * @extends Roo.form.Field
27185  * A Day picker show [M] [T] [W] ....
27186  * @constructor
27187  * Creates a new Day Picker
27188  * @param {Object} config Configuration options
27189  */
27190 Roo.form.DayPicker= function(config){
27191     Roo.form.DayPicker.superclass.constructor.call(this, config);
27192      
27193 };
27194
27195 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27196     /**
27197      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27198      */
27199     focusClass : undefined,
27200     /**
27201      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27202      */
27203     fieldClass: "x-form-field",
27204    
27205     /**
27206      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27207      * {tag: "input", type: "checkbox", autocomplete: "off"})
27208      */
27209     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27210     
27211    
27212     actionMode : 'viewEl', 
27213     //
27214     // private
27215  
27216     inputType : 'hidden',
27217     
27218      
27219     inputElement: false, // real input element?
27220     basedOn: false, // ????
27221     
27222     isFormField: true, // not sure where this is needed!!!!
27223
27224     onResize : function(){
27225         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27226         if(!this.boxLabel){
27227             this.el.alignTo(this.wrap, 'c-c');
27228         }
27229     },
27230
27231     initEvents : function(){
27232         Roo.form.Checkbox.superclass.initEvents.call(this);
27233         this.el.on("click", this.onClick,  this);
27234         this.el.on("change", this.onClick,  this);
27235     },
27236
27237
27238     getResizeEl : function(){
27239         return this.wrap;
27240     },
27241
27242     getPositionEl : function(){
27243         return this.wrap;
27244     },
27245
27246     
27247     // private
27248     onRender : function(ct, position){
27249         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27250        
27251         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27252         
27253         var r1 = '<table><tr>';
27254         var r2 = '<tr class="x-form-daypick-icons">';
27255         for (var i=0; i < 7; i++) {
27256             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27257             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27258         }
27259         
27260         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27261         viewEl.select('img').on('click', this.onClick, this);
27262         this.viewEl = viewEl;   
27263         
27264         
27265         // this will not work on Chrome!!!
27266         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27267         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27268         
27269         
27270           
27271
27272     },
27273
27274     // private
27275     initValue : Roo.emptyFn,
27276
27277     /**
27278      * Returns the checked state of the checkbox.
27279      * @return {Boolean} True if checked, else false
27280      */
27281     getValue : function(){
27282         return this.el.dom.value;
27283         
27284     },
27285
27286         // private
27287     onClick : function(e){ 
27288         //this.setChecked(!this.checked);
27289         Roo.get(e.target).toggleClass('x-menu-item-checked');
27290         this.refreshValue();
27291         //if(this.el.dom.checked != this.checked){
27292         //    this.setValue(this.el.dom.checked);
27293        // }
27294     },
27295     
27296     // private
27297     refreshValue : function()
27298     {
27299         var val = '';
27300         this.viewEl.select('img',true).each(function(e,i,n)  {
27301             val += e.is(".x-menu-item-checked") ? String(n) : '';
27302         });
27303         this.setValue(val, true);
27304     },
27305
27306     /**
27307      * Sets the checked state of the checkbox.
27308      * On is always based on a string comparison between inputValue and the param.
27309      * @param {Boolean/String} value - the value to set 
27310      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27311      */
27312     setValue : function(v,suppressEvent){
27313         if (!this.el.dom) {
27314             return;
27315         }
27316         var old = this.el.dom.value ;
27317         this.el.dom.value = v;
27318         if (suppressEvent) {
27319             return ;
27320         }
27321          
27322         // update display..
27323         this.viewEl.select('img',true).each(function(e,i,n)  {
27324             
27325             var on = e.is(".x-menu-item-checked");
27326             var newv = v.indexOf(String(n)) > -1;
27327             if (on != newv) {
27328                 e.toggleClass('x-menu-item-checked');
27329             }
27330             
27331         });
27332         
27333         
27334         this.fireEvent('change', this, v, old);
27335         
27336         
27337     },
27338    
27339     // handle setting of hidden value by some other method!!?!?
27340     setFromHidden: function()
27341     {
27342         if(!this.el){
27343             return;
27344         }
27345         //console.log("SET FROM HIDDEN");
27346         //alert('setFrom hidden');
27347         this.setValue(this.el.dom.value);
27348     },
27349     
27350     onDestroy : function()
27351     {
27352         if(this.viewEl){
27353             Roo.get(this.viewEl).remove();
27354         }
27355          
27356         Roo.form.DayPicker.superclass.onDestroy.call(this);
27357     }
27358
27359 });/*
27360  * RooJS Library 1.1.1
27361  * Copyright(c) 2008-2011  Alan Knowles
27362  *
27363  * License - LGPL
27364  */
27365  
27366
27367 /**
27368  * @class Roo.form.ComboCheck
27369  * @extends Roo.form.ComboBox
27370  * A combobox for multiple select items.
27371  *
27372  * FIXME - could do with a reset button..
27373  * 
27374  * @constructor
27375  * Create a new ComboCheck
27376  * @param {Object} config Configuration options
27377  */
27378 Roo.form.ComboCheck = function(config){
27379     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27380     // should verify some data...
27381     // like
27382     // hiddenName = required..
27383     // displayField = required
27384     // valudField == required
27385     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27386     var _t = this;
27387     Roo.each(req, function(e) {
27388         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27389             throw "Roo.form.ComboCheck : missing value for: " + e;
27390         }
27391     });
27392     
27393     
27394 };
27395
27396 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27397      
27398      
27399     editable : false,
27400      
27401     selectedClass: 'x-menu-item-checked', 
27402     
27403     // private
27404     onRender : function(ct, position){
27405         var _t = this;
27406         
27407         
27408         
27409         if(!this.tpl){
27410             var cls = 'x-combo-list';
27411
27412             
27413             this.tpl =  new Roo.Template({
27414                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27415                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27416                    '<span>{' + this.displayField + '}</span>' +
27417                     '</div>' 
27418                 
27419             });
27420         }
27421  
27422         
27423         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27424         this.view.singleSelect = false;
27425         this.view.multiSelect = true;
27426         this.view.toggleSelect = true;
27427         this.pageTb.add(new Roo.Toolbar.Fill(), {
27428             
27429             text: 'Done',
27430             handler: function()
27431             {
27432                 _t.collapse();
27433             }
27434         });
27435     },
27436     
27437     onViewOver : function(e, t){
27438         // do nothing...
27439         return;
27440         
27441     },
27442     
27443     onViewClick : function(doFocus,index){
27444         return;
27445         
27446     },
27447     select: function () {
27448         //Roo.log("SELECT CALLED");
27449     },
27450      
27451     selectByValue : function(xv, scrollIntoView){
27452         var ar = this.getValueArray();
27453         var sels = [];
27454         
27455         Roo.each(ar, function(v) {
27456             if(v === undefined || v === null){
27457                 return;
27458             }
27459             var r = this.findRecord(this.valueField, v);
27460             if(r){
27461                 sels.push(this.store.indexOf(r))
27462                 
27463             }
27464         },this);
27465         this.view.select(sels);
27466         return false;
27467     },
27468     
27469     
27470     
27471     onSelect : function(record, index){
27472        // Roo.log("onselect Called");
27473        // this is only called by the clear button now..
27474         this.view.clearSelections();
27475         this.setValue('[]');
27476         if (this.value != this.valueBefore) {
27477             this.fireEvent('change', this, this.value, this.valueBefore);
27478             this.valueBefore = this.value;
27479         }
27480     },
27481     getValueArray : function()
27482     {
27483         var ar = [] ;
27484         
27485         try {
27486             //Roo.log(this.value);
27487             if (typeof(this.value) == 'undefined') {
27488                 return [];
27489             }
27490             var ar = Roo.decode(this.value);
27491             return  ar instanceof Array ? ar : []; //?? valid?
27492             
27493         } catch(e) {
27494             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27495             return [];
27496         }
27497          
27498     },
27499     expand : function ()
27500     {
27501         
27502         Roo.form.ComboCheck.superclass.expand.call(this);
27503         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27504         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27505         
27506
27507     },
27508     
27509     collapse : function(){
27510         Roo.form.ComboCheck.superclass.collapse.call(this);
27511         var sl = this.view.getSelectedIndexes();
27512         var st = this.store;
27513         var nv = [];
27514         var tv = [];
27515         var r;
27516         Roo.each(sl, function(i) {
27517             r = st.getAt(i);
27518             nv.push(r.get(this.valueField));
27519         },this);
27520         this.setValue(Roo.encode(nv));
27521         if (this.value != this.valueBefore) {
27522
27523             this.fireEvent('change', this, this.value, this.valueBefore);
27524             this.valueBefore = this.value;
27525         }
27526         
27527     },
27528     
27529     setValue : function(v){
27530         // Roo.log(v);
27531         this.value = v;
27532         
27533         var vals = this.getValueArray();
27534         var tv = [];
27535         Roo.each(vals, function(k) {
27536             var r = this.findRecord(this.valueField, k);
27537             if(r){
27538                 tv.push(r.data[this.displayField]);
27539             }else if(this.valueNotFoundText !== undefined){
27540                 tv.push( this.valueNotFoundText );
27541             }
27542         },this);
27543        // Roo.log(tv);
27544         
27545         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27546         this.hiddenField.value = v;
27547         this.value = v;
27548     }
27549     
27550 });/*
27551  * Based on:
27552  * Ext JS Library 1.1.1
27553  * Copyright(c) 2006-2007, Ext JS, LLC.
27554  *
27555  * Originally Released Under LGPL - original licence link has changed is not relivant.
27556  *
27557  * Fork - LGPL
27558  * <script type="text/javascript">
27559  */
27560  
27561 /**
27562  * @class Roo.form.Signature
27563  * @extends Roo.form.Field
27564  * Signature field.  
27565  * @constructor
27566  * 
27567  * @param {Object} config Configuration options
27568  */
27569
27570 Roo.form.Signature = function(config){
27571     Roo.form.Signature.superclass.constructor.call(this, config);
27572     
27573     this.addEvents({// not in used??
27574          /**
27575          * @event confirm
27576          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27577              * @param {Roo.form.Signature} combo This combo box
27578              */
27579         'confirm' : true,
27580         /**
27581          * @event reset
27582          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27583              * @param {Roo.form.ComboBox} combo This combo box
27584              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27585              */
27586         'reset' : true
27587     });
27588 };
27589
27590 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27591     /**
27592      * @cfg {Object} labels Label to use when rendering a form.
27593      * defaults to 
27594      * labels : { 
27595      *      clear : "Clear",
27596      *      confirm : "Confirm"
27597      *  }
27598      */
27599     labels : { 
27600         clear : "Clear",
27601         confirm : "Confirm"
27602     },
27603     /**
27604      * @cfg {Number} width The signature panel width (defaults to 300)
27605      */
27606     width: 300,
27607     /**
27608      * @cfg {Number} height The signature panel height (defaults to 100)
27609      */
27610     height : 100,
27611     /**
27612      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27613      */
27614     allowBlank : false,
27615     
27616     //private
27617     // {Object} signPanel The signature SVG panel element (defaults to {})
27618     signPanel : {},
27619     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27620     isMouseDown : false,
27621     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27622     isConfirmed : false,
27623     // {String} signatureTmp SVG mapping string (defaults to empty string)
27624     signatureTmp : '',
27625     
27626     
27627     defaultAutoCreate : { // modified by initCompnoent..
27628         tag: "input",
27629         type:"hidden"
27630     },
27631
27632     // private
27633     onRender : function(ct, position){
27634         
27635         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27636         
27637         this.wrap = this.el.wrap({
27638             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27639         });
27640         
27641         this.createToolbar(this);
27642         this.signPanel = this.wrap.createChild({
27643                 tag: 'div',
27644                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27645             }, this.el
27646         );
27647             
27648         this.svgID = Roo.id();
27649         this.svgEl = this.signPanel.createChild({
27650               xmlns : 'http://www.w3.org/2000/svg',
27651               tag : 'svg',
27652               id : this.svgID + "-svg",
27653               width: this.width,
27654               height: this.height,
27655               viewBox: '0 0 '+this.width+' '+this.height,
27656               cn : [
27657                 {
27658                     tag: "rect",
27659                     id: this.svgID + "-svg-r",
27660                     width: this.width,
27661                     height: this.height,
27662                     fill: "#ffa"
27663                 },
27664                 {
27665                     tag: "line",
27666                     id: this.svgID + "-svg-l",
27667                     x1: "0", // start
27668                     y1: (this.height*0.8), // start set the line in 80% of height
27669                     x2: this.width, // end
27670                     y2: (this.height*0.8), // end set the line in 80% of height
27671                     'stroke': "#666",
27672                     'stroke-width': "1",
27673                     'stroke-dasharray': "3",
27674                     'shape-rendering': "crispEdges",
27675                     'pointer-events': "none"
27676                 },
27677                 {
27678                     tag: "path",
27679                     id: this.svgID + "-svg-p",
27680                     'stroke': "navy",
27681                     'stroke-width': "3",
27682                     'fill': "none",
27683                     'pointer-events': 'none'
27684                 }
27685               ]
27686         });
27687         this.createSVG();
27688         this.svgBox = this.svgEl.dom.getScreenCTM();
27689     },
27690     createSVG : function(){ 
27691         var svg = this.signPanel;
27692         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27693         var t = this;
27694
27695         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27696         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27697         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27698         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27699         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27700         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27701         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27702         
27703     },
27704     isTouchEvent : function(e){
27705         return e.type.match(/^touch/);
27706     },
27707     getCoords : function (e) {
27708         var pt    = this.svgEl.dom.createSVGPoint();
27709         pt.x = e.clientX; 
27710         pt.y = e.clientY;
27711         if (this.isTouchEvent(e)) {
27712             pt.x =  e.targetTouches[0].clientX;
27713             pt.y = e.targetTouches[0].clientY;
27714         }
27715         var a = this.svgEl.dom.getScreenCTM();
27716         var b = a.inverse();
27717         var mx = pt.matrixTransform(b);
27718         return mx.x + ',' + mx.y;
27719     },
27720     //mouse event headler 
27721     down : function (e) {
27722         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27723         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27724         
27725         this.isMouseDown = true;
27726         
27727         e.preventDefault();
27728     },
27729     move : function (e) {
27730         if (this.isMouseDown) {
27731             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27732             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27733         }
27734         
27735         e.preventDefault();
27736     },
27737     up : function (e) {
27738         this.isMouseDown = false;
27739         var sp = this.signatureTmp.split(' ');
27740         
27741         if(sp.length > 1){
27742             if(!sp[sp.length-2].match(/^L/)){
27743                 sp.pop();
27744                 sp.pop();
27745                 sp.push("");
27746                 this.signatureTmp = sp.join(" ");
27747             }
27748         }
27749         if(this.getValue() != this.signatureTmp){
27750             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27751             this.isConfirmed = false;
27752         }
27753         e.preventDefault();
27754     },
27755     
27756     /**
27757      * Protected method that will not generally be called directly. It
27758      * is called when the editor creates its toolbar. Override this method if you need to
27759      * add custom toolbar buttons.
27760      * @param {HtmlEditor} editor
27761      */
27762     createToolbar : function(editor){
27763          function btn(id, toggle, handler){
27764             var xid = fid + '-'+ id ;
27765             return {
27766                 id : xid,
27767                 cmd : id,
27768                 cls : 'x-btn-icon x-edit-'+id,
27769                 enableToggle:toggle !== false,
27770                 scope: editor, // was editor...
27771                 handler:handler||editor.relayBtnCmd,
27772                 clickEvent:'mousedown',
27773                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27774                 tabIndex:-1
27775             };
27776         }
27777         
27778         
27779         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27780         this.tb = tb;
27781         this.tb.add(
27782            {
27783                 cls : ' x-signature-btn x-signature-'+id,
27784                 scope: editor, // was editor...
27785                 handler: this.reset,
27786                 clickEvent:'mousedown',
27787                 text: this.labels.clear
27788             },
27789             {
27790                  xtype : 'Fill',
27791                  xns: Roo.Toolbar
27792             }, 
27793             {
27794                 cls : '  x-signature-btn x-signature-'+id,
27795                 scope: editor, // was editor...
27796                 handler: this.confirmHandler,
27797                 clickEvent:'mousedown',
27798                 text: this.labels.confirm
27799             }
27800         );
27801     
27802     },
27803     //public
27804     /**
27805      * when user is clicked confirm then show this image.....
27806      * 
27807      * @return {String} Image Data URI
27808      */
27809     getImageDataURI : function(){
27810         var svg = this.svgEl.dom.parentNode.innerHTML;
27811         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27812         return src; 
27813     },
27814     /**
27815      * 
27816      * @return {Boolean} this.isConfirmed
27817      */
27818     getConfirmed : function(){
27819         return this.isConfirmed;
27820     },
27821     /**
27822      * 
27823      * @return {Number} this.width
27824      */
27825     getWidth : function(){
27826         return this.width;
27827     },
27828     /**
27829      * 
27830      * @return {Number} this.height
27831      */
27832     getHeight : function(){
27833         return this.height;
27834     },
27835     // private
27836     getSignature : function(){
27837         return this.signatureTmp;
27838     },
27839     // private
27840     reset : function(){
27841         this.signatureTmp = '';
27842         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27843         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27844         this.isConfirmed = false;
27845         Roo.form.Signature.superclass.reset.call(this);
27846     },
27847     setSignature : function(s){
27848         this.signatureTmp = s;
27849         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27850         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27851         this.setValue(s);
27852         this.isConfirmed = false;
27853         Roo.form.Signature.superclass.reset.call(this);
27854     }, 
27855     test : function(){
27856 //        Roo.log(this.signPanel.dom.contentWindow.up())
27857     },
27858     //private
27859     setConfirmed : function(){
27860         
27861         
27862         
27863 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27864     },
27865     // private
27866     confirmHandler : function(){
27867         if(!this.getSignature()){
27868             return;
27869         }
27870         
27871         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27872         this.setValue(this.getSignature());
27873         this.isConfirmed = true;
27874         
27875         this.fireEvent('confirm', this);
27876     },
27877     // private
27878     // Subclasses should provide the validation implementation by overriding this
27879     validateValue : function(value){
27880         if(this.allowBlank){
27881             return true;
27882         }
27883         
27884         if(this.isConfirmed){
27885             return true;
27886         }
27887         return false;
27888     }
27889 });/*
27890  * Based on:
27891  * Ext JS Library 1.1.1
27892  * Copyright(c) 2006-2007, Ext JS, LLC.
27893  *
27894  * Originally Released Under LGPL - original licence link has changed is not relivant.
27895  *
27896  * Fork - LGPL
27897  * <script type="text/javascript">
27898  */
27899  
27900
27901 /**
27902  * @class Roo.form.ComboBox
27903  * @extends Roo.form.TriggerField
27904  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27905  * @constructor
27906  * Create a new ComboBox.
27907  * @param {Object} config Configuration options
27908  */
27909 Roo.form.Select = function(config){
27910     Roo.form.Select.superclass.constructor.call(this, config);
27911      
27912 };
27913
27914 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27915     /**
27916      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27917      */
27918     /**
27919      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27920      * rendering into an Roo.Editor, defaults to false)
27921      */
27922     /**
27923      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27924      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27925      */
27926     /**
27927      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27928      */
27929     /**
27930      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27931      * the dropdown list (defaults to undefined, with no header element)
27932      */
27933
27934      /**
27935      * @cfg {String/Roo.Template} tpl The template to use to render the output
27936      */
27937      
27938     // private
27939     defaultAutoCreate : {tag: "select"  },
27940     /**
27941      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27942      */
27943     listWidth: undefined,
27944     /**
27945      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27946      * mode = 'remote' or 'text' if mode = 'local')
27947      */
27948     displayField: undefined,
27949     /**
27950      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27951      * mode = 'remote' or 'value' if mode = 'local'). 
27952      * Note: use of a valueField requires the user make a selection
27953      * in order for a value to be mapped.
27954      */
27955     valueField: undefined,
27956     
27957     
27958     /**
27959      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27960      * field's data value (defaults to the underlying DOM element's name)
27961      */
27962     hiddenName: undefined,
27963     /**
27964      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27965      */
27966     listClass: '',
27967     /**
27968      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27969      */
27970     selectedClass: 'x-combo-selected',
27971     /**
27972      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27973      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27974      * which displays a downward arrow icon).
27975      */
27976     triggerClass : 'x-form-arrow-trigger',
27977     /**
27978      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27979      */
27980     shadow:'sides',
27981     /**
27982      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27983      * anchor positions (defaults to 'tl-bl')
27984      */
27985     listAlign: 'tl-bl?',
27986     /**
27987      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27988      */
27989     maxHeight: 300,
27990     /**
27991      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27992      * query specified by the allQuery config option (defaults to 'query')
27993      */
27994     triggerAction: 'query',
27995     /**
27996      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27997      * (defaults to 4, does not apply if editable = false)
27998      */
27999     minChars : 4,
28000     /**
28001      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28002      * delay (typeAheadDelay) if it matches a known value (defaults to false)
28003      */
28004     typeAhead: false,
28005     /**
28006      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28007      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28008      */
28009     queryDelay: 500,
28010     /**
28011      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28012      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28013      */
28014     pageSize: 0,
28015     /**
28016      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28017      * when editable = true (defaults to false)
28018      */
28019     selectOnFocus:false,
28020     /**
28021      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28022      */
28023     queryParam: 'query',
28024     /**
28025      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28026      * when mode = 'remote' (defaults to 'Loading...')
28027      */
28028     loadingText: 'Loading...',
28029     /**
28030      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28031      */
28032     resizable: false,
28033     /**
28034      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28035      */
28036     handleHeight : 8,
28037     /**
28038      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28039      * traditional select (defaults to true)
28040      */
28041     editable: true,
28042     /**
28043      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28044      */
28045     allQuery: '',
28046     /**
28047      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28048      */
28049     mode: 'remote',
28050     /**
28051      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28052      * listWidth has a higher value)
28053      */
28054     minListWidth : 70,
28055     /**
28056      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28057      * allow the user to set arbitrary text into the field (defaults to false)
28058      */
28059     forceSelection:false,
28060     /**
28061      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28062      * if typeAhead = true (defaults to 250)
28063      */
28064     typeAheadDelay : 250,
28065     /**
28066      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28067      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28068      */
28069     valueNotFoundText : undefined,
28070     
28071     /**
28072      * @cfg {String} defaultValue The value displayed after loading the store.
28073      */
28074     defaultValue: '',
28075     
28076     /**
28077      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28078      */
28079     blockFocus : false,
28080     
28081     /**
28082      * @cfg {Boolean} disableClear Disable showing of clear button.
28083      */
28084     disableClear : false,
28085     /**
28086      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28087      */
28088     alwaysQuery : false,
28089     
28090     //private
28091     addicon : false,
28092     editicon: false,
28093     
28094     // element that contains real text value.. (when hidden is used..)
28095      
28096     // private
28097     onRender : function(ct, position){
28098         Roo.form.Field.prototype.onRender.call(this, ct, position);
28099         
28100         if(this.store){
28101             this.store.on('beforeload', this.onBeforeLoad, this);
28102             this.store.on('load', this.onLoad, this);
28103             this.store.on('loadexception', this.onLoadException, this);
28104             this.store.load({});
28105         }
28106         
28107         
28108         
28109     },
28110
28111     // private
28112     initEvents : function(){
28113         //Roo.form.ComboBox.superclass.initEvents.call(this);
28114  
28115     },
28116
28117     onDestroy : function(){
28118        
28119         if(this.store){
28120             this.store.un('beforeload', this.onBeforeLoad, this);
28121             this.store.un('load', this.onLoad, this);
28122             this.store.un('loadexception', this.onLoadException, this);
28123         }
28124         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28125     },
28126
28127     // private
28128     fireKey : function(e){
28129         if(e.isNavKeyPress() && !this.list.isVisible()){
28130             this.fireEvent("specialkey", this, e);
28131         }
28132     },
28133
28134     // private
28135     onResize: function(w, h){
28136         
28137         return; 
28138     
28139         
28140     },
28141
28142     /**
28143      * Allow or prevent the user from directly editing the field text.  If false is passed,
28144      * the user will only be able to select from the items defined in the dropdown list.  This method
28145      * is the runtime equivalent of setting the 'editable' config option at config time.
28146      * @param {Boolean} value True to allow the user to directly edit the field text
28147      */
28148     setEditable : function(value){
28149          
28150     },
28151
28152     // private
28153     onBeforeLoad : function(){
28154         
28155         Roo.log("Select before load");
28156         return;
28157     
28158         this.innerList.update(this.loadingText ?
28159                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28160         //this.restrictHeight();
28161         this.selectedIndex = -1;
28162     },
28163
28164     // private
28165     onLoad : function(){
28166
28167     
28168         var dom = this.el.dom;
28169         dom.innerHTML = '';
28170          var od = dom.ownerDocument;
28171          
28172         if (this.emptyText) {
28173             var op = od.createElement('option');
28174             op.setAttribute('value', '');
28175             op.innerHTML = String.format('{0}', this.emptyText);
28176             dom.appendChild(op);
28177         }
28178         if(this.store.getCount() > 0){
28179            
28180             var vf = this.valueField;
28181             var df = this.displayField;
28182             this.store.data.each(function(r) {
28183                 // which colmsn to use... testing - cdoe / title..
28184                 var op = od.createElement('option');
28185                 op.setAttribute('value', r.data[vf]);
28186                 op.innerHTML = String.format('{0}', r.data[df]);
28187                 dom.appendChild(op);
28188             });
28189             if (typeof(this.defaultValue != 'undefined')) {
28190                 this.setValue(this.defaultValue);
28191             }
28192             
28193              
28194         }else{
28195             //this.onEmptyResults();
28196         }
28197         //this.el.focus();
28198     },
28199     // private
28200     onLoadException : function()
28201     {
28202         dom.innerHTML = '';
28203             
28204         Roo.log("Select on load exception");
28205         return;
28206     
28207         this.collapse();
28208         Roo.log(this.store.reader.jsonData);
28209         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28210             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28211         }
28212         
28213         
28214     },
28215     // private
28216     onTypeAhead : function(){
28217          
28218     },
28219
28220     // private
28221     onSelect : function(record, index){
28222         Roo.log('on select?');
28223         return;
28224         if(this.fireEvent('beforeselect', this, record, index) !== false){
28225             this.setFromData(index > -1 ? record.data : false);
28226             this.collapse();
28227             this.fireEvent('select', this, record, index);
28228         }
28229     },
28230
28231     /**
28232      * Returns the currently selected field value or empty string if no value is set.
28233      * @return {String} value The selected value
28234      */
28235     getValue : function(){
28236         var dom = this.el.dom;
28237         this.value = dom.options[dom.selectedIndex].value;
28238         return this.value;
28239         
28240     },
28241
28242     /**
28243      * Clears any text/value currently set in the field
28244      */
28245     clearValue : function(){
28246         this.value = '';
28247         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28248         
28249     },
28250
28251     /**
28252      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28253      * will be displayed in the field.  If the value does not match the data value of an existing item,
28254      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28255      * Otherwise the field will be blank (although the value will still be set).
28256      * @param {String} value The value to match
28257      */
28258     setValue : function(v){
28259         var d = this.el.dom;
28260         for (var i =0; i < d.options.length;i++) {
28261             if (v == d.options[i].value) {
28262                 d.selectedIndex = i;
28263                 this.value = v;
28264                 return;
28265             }
28266         }
28267         this.clearValue();
28268     },
28269     /**
28270      * @property {Object} the last set data for the element
28271      */
28272     
28273     lastData : false,
28274     /**
28275      * Sets the value of the field based on a object which is related to the record format for the store.
28276      * @param {Object} value the value to set as. or false on reset?
28277      */
28278     setFromData : function(o){
28279         Roo.log('setfrom data?');
28280          
28281         
28282         
28283     },
28284     // private
28285     reset : function(){
28286         this.clearValue();
28287     },
28288     // private
28289     findRecord : function(prop, value){
28290         
28291         return false;
28292     
28293         var record;
28294         if(this.store.getCount() > 0){
28295             this.store.each(function(r){
28296                 if(r.data[prop] == value){
28297                     record = r;
28298                     return false;
28299                 }
28300                 return true;
28301             });
28302         }
28303         return record;
28304     },
28305     
28306     getName: function()
28307     {
28308         // returns hidden if it's set..
28309         if (!this.rendered) {return ''};
28310         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28311         
28312     },
28313      
28314
28315     
28316
28317     // private
28318     onEmptyResults : function(){
28319         Roo.log('empty results');
28320         //this.collapse();
28321     },
28322
28323     /**
28324      * Returns true if the dropdown list is expanded, else false.
28325      */
28326     isExpanded : function(){
28327         return false;
28328     },
28329
28330     /**
28331      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28332      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28333      * @param {String} value The data value of the item to select
28334      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28335      * selected item if it is not currently in view (defaults to true)
28336      * @return {Boolean} True if the value matched an item in the list, else false
28337      */
28338     selectByValue : function(v, scrollIntoView){
28339         Roo.log('select By Value');
28340         return false;
28341     
28342         if(v !== undefined && v !== null){
28343             var r = this.findRecord(this.valueField || this.displayField, v);
28344             if(r){
28345                 this.select(this.store.indexOf(r), scrollIntoView);
28346                 return true;
28347             }
28348         }
28349         return false;
28350     },
28351
28352     /**
28353      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28354      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28355      * @param {Number} index The zero-based index of the list item to select
28356      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28357      * selected item if it is not currently in view (defaults to true)
28358      */
28359     select : function(index, scrollIntoView){
28360         Roo.log('select ');
28361         return  ;
28362         
28363         this.selectedIndex = index;
28364         this.view.select(index);
28365         if(scrollIntoView !== false){
28366             var el = this.view.getNode(index);
28367             if(el){
28368                 this.innerList.scrollChildIntoView(el, false);
28369             }
28370         }
28371     },
28372
28373       
28374
28375     // private
28376     validateBlur : function(){
28377         
28378         return;
28379         
28380     },
28381
28382     // private
28383     initQuery : function(){
28384         this.doQuery(this.getRawValue());
28385     },
28386
28387     // private
28388     doForce : function(){
28389         if(this.el.dom.value.length > 0){
28390             this.el.dom.value =
28391                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28392              
28393         }
28394     },
28395
28396     /**
28397      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28398      * query allowing the query action to be canceled if needed.
28399      * @param {String} query The SQL query to execute
28400      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28401      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28402      * saved in the current store (defaults to false)
28403      */
28404     doQuery : function(q, forceAll){
28405         
28406         Roo.log('doQuery?');
28407         if(q === undefined || q === null){
28408             q = '';
28409         }
28410         var qe = {
28411             query: q,
28412             forceAll: forceAll,
28413             combo: this,
28414             cancel:false
28415         };
28416         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28417             return false;
28418         }
28419         q = qe.query;
28420         forceAll = qe.forceAll;
28421         if(forceAll === true || (q.length >= this.minChars)){
28422             if(this.lastQuery != q || this.alwaysQuery){
28423                 this.lastQuery = q;
28424                 if(this.mode == 'local'){
28425                     this.selectedIndex = -1;
28426                     if(forceAll){
28427                         this.store.clearFilter();
28428                     }else{
28429                         this.store.filter(this.displayField, q);
28430                     }
28431                     this.onLoad();
28432                 }else{
28433                     this.store.baseParams[this.queryParam] = q;
28434                     this.store.load({
28435                         params: this.getParams(q)
28436                     });
28437                     this.expand();
28438                 }
28439             }else{
28440                 this.selectedIndex = -1;
28441                 this.onLoad();   
28442             }
28443         }
28444     },
28445
28446     // private
28447     getParams : function(q){
28448         var p = {};
28449         //p[this.queryParam] = q;
28450         if(this.pageSize){
28451             p.start = 0;
28452             p.limit = this.pageSize;
28453         }
28454         return p;
28455     },
28456
28457     /**
28458      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28459      */
28460     collapse : function(){
28461         
28462     },
28463
28464     // private
28465     collapseIf : function(e){
28466         
28467     },
28468
28469     /**
28470      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28471      */
28472     expand : function(){
28473         
28474     } ,
28475
28476     // private
28477      
28478
28479     /** 
28480     * @cfg {Boolean} grow 
28481     * @hide 
28482     */
28483     /** 
28484     * @cfg {Number} growMin 
28485     * @hide 
28486     */
28487     /** 
28488     * @cfg {Number} growMax 
28489     * @hide 
28490     */
28491     /**
28492      * @hide
28493      * @method autoSize
28494      */
28495     
28496     setWidth : function()
28497     {
28498         
28499     },
28500     getResizeEl : function(){
28501         return this.el;
28502     }
28503 });//<script type="text/javasscript">
28504  
28505
28506 /**
28507  * @class Roo.DDView
28508  * A DnD enabled version of Roo.View.
28509  * @param {Element/String} container The Element in which to create the View.
28510  * @param {String} tpl The template string used to create the markup for each element of the View
28511  * @param {Object} config The configuration properties. These include all the config options of
28512  * {@link Roo.View} plus some specific to this class.<br>
28513  * <p>
28514  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28515  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28516  * <p>
28517  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28518 .x-view-drag-insert-above {
28519         border-top:1px dotted #3366cc;
28520 }
28521 .x-view-drag-insert-below {
28522         border-bottom:1px dotted #3366cc;
28523 }
28524 </code></pre>
28525  * 
28526  */
28527  
28528 Roo.DDView = function(container, tpl, config) {
28529     Roo.DDView.superclass.constructor.apply(this, arguments);
28530     this.getEl().setStyle("outline", "0px none");
28531     this.getEl().unselectable();
28532     if (this.dragGroup) {
28533         this.setDraggable(this.dragGroup.split(","));
28534     }
28535     if (this.dropGroup) {
28536         this.setDroppable(this.dropGroup.split(","));
28537     }
28538     if (this.deletable) {
28539         this.setDeletable();
28540     }
28541     this.isDirtyFlag = false;
28542         this.addEvents({
28543                 "drop" : true
28544         });
28545 };
28546
28547 Roo.extend(Roo.DDView, Roo.View, {
28548 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28549 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28550 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28551 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28552
28553         isFormField: true,
28554
28555         reset: Roo.emptyFn,
28556         
28557         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28558
28559         validate: function() {
28560                 return true;
28561         },
28562         
28563         destroy: function() {
28564                 this.purgeListeners();
28565                 this.getEl.removeAllListeners();
28566                 this.getEl().remove();
28567                 if (this.dragZone) {
28568                         if (this.dragZone.destroy) {
28569                                 this.dragZone.destroy();
28570                         }
28571                 }
28572                 if (this.dropZone) {
28573                         if (this.dropZone.destroy) {
28574                                 this.dropZone.destroy();
28575                         }
28576                 }
28577         },
28578
28579 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28580         getName: function() {
28581                 return this.name;
28582         },
28583
28584 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28585         setValue: function(v) {
28586                 if (!this.store) {
28587                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28588                 }
28589                 var data = {};
28590                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28591                 this.store.proxy = new Roo.data.MemoryProxy(data);
28592                 this.store.load();
28593         },
28594
28595 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28596         getValue: function() {
28597                 var result = '(';
28598                 this.store.each(function(rec) {
28599                         result += rec.id + ',';
28600                 });
28601                 return result.substr(0, result.length - 1) + ')';
28602         },
28603         
28604         getIds: function() {
28605                 var i = 0, result = new Array(this.store.getCount());
28606                 this.store.each(function(rec) {
28607                         result[i++] = rec.id;
28608                 });
28609                 return result;
28610         },
28611         
28612         isDirty: function() {
28613                 return this.isDirtyFlag;
28614         },
28615
28616 /**
28617  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28618  *      whole Element becomes the target, and this causes the drop gesture to append.
28619  */
28620     getTargetFromEvent : function(e) {
28621                 var target = e.getTarget();
28622                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28623                 target = target.parentNode;
28624                 }
28625                 if (!target) {
28626                         target = this.el.dom.lastChild || this.el.dom;
28627                 }
28628                 return target;
28629     },
28630
28631 /**
28632  *      Create the drag data which consists of an object which has the property "ddel" as
28633  *      the drag proxy element. 
28634  */
28635     getDragData : function(e) {
28636         var target = this.findItemFromChild(e.getTarget());
28637                 if(target) {
28638                         this.handleSelection(e);
28639                         var selNodes = this.getSelectedNodes();
28640             var dragData = {
28641                 source: this,
28642                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28643                 nodes: selNodes,
28644                 records: []
28645                         };
28646                         var selectedIndices = this.getSelectedIndexes();
28647                         for (var i = 0; i < selectedIndices.length; i++) {
28648                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28649                         }
28650                         if (selNodes.length == 1) {
28651                                 dragData.ddel = target.cloneNode(true); // the div element
28652                         } else {
28653                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28654                                 div.className = 'multi-proxy';
28655                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28656                                         div.appendChild(selNodes[i].cloneNode(true));
28657                                 }
28658                                 dragData.ddel = div;
28659                         }
28660             //console.log(dragData)
28661             //console.log(dragData.ddel.innerHTML)
28662                         return dragData;
28663                 }
28664         //console.log('nodragData')
28665                 return false;
28666     },
28667     
28668 /**     Specify to which ddGroup items in this DDView may be dragged. */
28669     setDraggable: function(ddGroup) {
28670         if (ddGroup instanceof Array) {
28671                 Roo.each(ddGroup, this.setDraggable, this);
28672                 return;
28673         }
28674         if (this.dragZone) {
28675                 this.dragZone.addToGroup(ddGroup);
28676         } else {
28677                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28678                                 containerScroll: true,
28679                                 ddGroup: ddGroup 
28680
28681                         });
28682 //                      Draggability implies selection. DragZone's mousedown selects the element.
28683                         if (!this.multiSelect) { this.singleSelect = true; }
28684
28685 //                      Wire the DragZone's handlers up to methods in *this*
28686                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28687                 }
28688     },
28689
28690 /**     Specify from which ddGroup this DDView accepts drops. */
28691     setDroppable: function(ddGroup) {
28692         if (ddGroup instanceof Array) {
28693                 Roo.each(ddGroup, this.setDroppable, this);
28694                 return;
28695         }
28696         if (this.dropZone) {
28697                 this.dropZone.addToGroup(ddGroup);
28698         } else {
28699                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28700                                 containerScroll: true,
28701                                 ddGroup: ddGroup
28702                         });
28703
28704 //                      Wire the DropZone's handlers up to methods in *this*
28705                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28706                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28707                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28708                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28709                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28710                 }
28711     },
28712
28713 /**     Decide whether to drop above or below a View node. */
28714     getDropPoint : function(e, n, dd){
28715         if (n == this.el.dom) { return "above"; }
28716                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28717                 var c = t + (b - t) / 2;
28718                 var y = Roo.lib.Event.getPageY(e);
28719                 if(y <= c) {
28720                         return "above";
28721                 }else{
28722                         return "below";
28723                 }
28724     },
28725
28726     onNodeEnter : function(n, dd, e, data){
28727                 return false;
28728     },
28729     
28730     onNodeOver : function(n, dd, e, data){
28731                 var pt = this.getDropPoint(e, n, dd);
28732                 // set the insert point style on the target node
28733                 var dragElClass = this.dropNotAllowed;
28734                 if (pt) {
28735                         var targetElClass;
28736                         if (pt == "above"){
28737                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28738                                 targetElClass = "x-view-drag-insert-above";
28739                         } else {
28740                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28741                                 targetElClass = "x-view-drag-insert-below";
28742                         }
28743                         if (this.lastInsertClass != targetElClass){
28744                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28745                                 this.lastInsertClass = targetElClass;
28746                         }
28747                 }
28748                 return dragElClass;
28749         },
28750
28751     onNodeOut : function(n, dd, e, data){
28752                 this.removeDropIndicators(n);
28753     },
28754
28755     onNodeDrop : function(n, dd, e, data){
28756         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28757                 return false;
28758         }
28759         var pt = this.getDropPoint(e, n, dd);
28760                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28761                 if (pt == "below") { insertAt++; }
28762                 for (var i = 0; i < data.records.length; i++) {
28763                         var r = data.records[i];
28764                         var dup = this.store.getById(r.id);
28765                         if (dup && (dd != this.dragZone)) {
28766                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28767                         } else {
28768                                 if (data.copy) {
28769                                         this.store.insert(insertAt++, r.copy());
28770                                 } else {
28771                                         data.source.isDirtyFlag = true;
28772                                         r.store.remove(r);
28773                                         this.store.insert(insertAt++, r);
28774                                 }
28775                                 this.isDirtyFlag = true;
28776                         }
28777                 }
28778                 this.dragZone.cachedTarget = null;
28779                 return true;
28780     },
28781
28782     removeDropIndicators : function(n){
28783                 if(n){
28784                         Roo.fly(n).removeClass([
28785                                 "x-view-drag-insert-above",
28786                                 "x-view-drag-insert-below"]);
28787                         this.lastInsertClass = "_noclass";
28788                 }
28789     },
28790
28791 /**
28792  *      Utility method. Add a delete option to the DDView's context menu.
28793  *      @param {String} imageUrl The URL of the "delete" icon image.
28794  */
28795         setDeletable: function(imageUrl) {
28796                 if (!this.singleSelect && !this.multiSelect) {
28797                         this.singleSelect = true;
28798                 }
28799                 var c = this.getContextMenu();
28800                 this.contextMenu.on("itemclick", function(item) {
28801                         switch (item.id) {
28802                                 case "delete":
28803                                         this.remove(this.getSelectedIndexes());
28804                                         break;
28805                         }
28806                 }, this);
28807                 this.contextMenu.add({
28808                         icon: imageUrl,
28809                         id: "delete",
28810                         text: 'Delete'
28811                 });
28812         },
28813         
28814 /**     Return the context menu for this DDView. */
28815         getContextMenu: function() {
28816                 if (!this.contextMenu) {
28817 //                      Create the View's context menu
28818                         this.contextMenu = new Roo.menu.Menu({
28819                                 id: this.id + "-contextmenu"
28820                         });
28821                         this.el.on("contextmenu", this.showContextMenu, this);
28822                 }
28823                 return this.contextMenu;
28824         },
28825         
28826         disableContextMenu: function() {
28827                 if (this.contextMenu) {
28828                         this.el.un("contextmenu", this.showContextMenu, this);
28829                 }
28830         },
28831
28832         showContextMenu: function(e, item) {
28833         item = this.findItemFromChild(e.getTarget());
28834                 if (item) {
28835                         e.stopEvent();
28836                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28837                         this.contextMenu.showAt(e.getXY());
28838             }
28839     },
28840
28841 /**
28842  *      Remove {@link Roo.data.Record}s at the specified indices.
28843  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28844  */
28845     remove: function(selectedIndices) {
28846                 selectedIndices = [].concat(selectedIndices);
28847                 for (var i = 0; i < selectedIndices.length; i++) {
28848                         var rec = this.store.getAt(selectedIndices[i]);
28849                         this.store.remove(rec);
28850                 }
28851     },
28852
28853 /**
28854  *      Double click fires the event, but also, if this is draggable, and there is only one other
28855  *      related DropZone, it transfers the selected node.
28856  */
28857     onDblClick : function(e){
28858         var item = this.findItemFromChild(e.getTarget());
28859         if(item){
28860             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28861                 return false;
28862             }
28863             if (this.dragGroup) {
28864                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28865                     while (targets.indexOf(this.dropZone) > -1) {
28866                             targets.remove(this.dropZone);
28867                                 }
28868                     if (targets.length == 1) {
28869                                         this.dragZone.cachedTarget = null;
28870                         var el = Roo.get(targets[0].getEl());
28871                         var box = el.getBox(true);
28872                         targets[0].onNodeDrop(el.dom, {
28873                                 target: el.dom,
28874                                 xy: [box.x, box.y + box.height - 1]
28875                         }, null, this.getDragData(e));
28876                     }
28877                 }
28878         }
28879     },
28880     
28881     handleSelection: function(e) {
28882                 this.dragZone.cachedTarget = null;
28883         var item = this.findItemFromChild(e.getTarget());
28884         if (!item) {
28885                 this.clearSelections(true);
28886                 return;
28887         }
28888                 if (item && (this.multiSelect || this.singleSelect)){
28889                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28890                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28891                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28892                                 this.unselect(item);
28893                         } else {
28894                                 this.select(item, this.multiSelect && e.ctrlKey);
28895                                 this.lastSelection = item;
28896                         }
28897                 }
28898     },
28899
28900     onItemClick : function(item, index, e){
28901                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28902                         return false;
28903                 }
28904                 return true;
28905     },
28906
28907     unselect : function(nodeInfo, suppressEvent){
28908                 var node = this.getNode(nodeInfo);
28909                 if(node && this.isSelected(node)){
28910                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28911                                 Roo.fly(node).removeClass(this.selectedClass);
28912                                 this.selections.remove(node);
28913                                 if(!suppressEvent){
28914                                         this.fireEvent("selectionchange", this, this.selections);
28915                                 }
28916                         }
28917                 }
28918     }
28919 });
28920 /*
28921  * Based on:
28922  * Ext JS Library 1.1.1
28923  * Copyright(c) 2006-2007, Ext JS, LLC.
28924  *
28925  * Originally Released Under LGPL - original licence link has changed is not relivant.
28926  *
28927  * Fork - LGPL
28928  * <script type="text/javascript">
28929  */
28930  
28931 /**
28932  * @class Roo.LayoutManager
28933  * @extends Roo.util.Observable
28934  * Base class for layout managers.
28935  */
28936 Roo.LayoutManager = function(container, config){
28937     Roo.LayoutManager.superclass.constructor.call(this);
28938     this.el = Roo.get(container);
28939     // ie scrollbar fix
28940     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28941         document.body.scroll = "no";
28942     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28943         this.el.position('relative');
28944     }
28945     this.id = this.el.id;
28946     this.el.addClass("x-layout-container");
28947     /** false to disable window resize monitoring @type Boolean */
28948     this.monitorWindowResize = true;
28949     this.regions = {};
28950     this.addEvents({
28951         /**
28952          * @event layout
28953          * Fires when a layout is performed. 
28954          * @param {Roo.LayoutManager} this
28955          */
28956         "layout" : true,
28957         /**
28958          * @event regionresized
28959          * Fires when the user resizes a region. 
28960          * @param {Roo.LayoutRegion} region The resized region
28961          * @param {Number} newSize The new size (width for east/west, height for north/south)
28962          */
28963         "regionresized" : true,
28964         /**
28965          * @event regioncollapsed
28966          * Fires when a region is collapsed. 
28967          * @param {Roo.LayoutRegion} region The collapsed region
28968          */
28969         "regioncollapsed" : true,
28970         /**
28971          * @event regionexpanded
28972          * Fires when a region is expanded.  
28973          * @param {Roo.LayoutRegion} region The expanded region
28974          */
28975         "regionexpanded" : true
28976     });
28977     this.updating = false;
28978     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28979 };
28980
28981 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28982     /**
28983      * Returns true if this layout is currently being updated
28984      * @return {Boolean}
28985      */
28986     isUpdating : function(){
28987         return this.updating; 
28988     },
28989     
28990     /**
28991      * Suspend the LayoutManager from doing auto-layouts while
28992      * making multiple add or remove calls
28993      */
28994     beginUpdate : function(){
28995         this.updating = true;    
28996     },
28997     
28998     /**
28999      * Restore auto-layouts and optionally disable the manager from performing a layout
29000      * @param {Boolean} noLayout true to disable a layout update 
29001      */
29002     endUpdate : function(noLayout){
29003         this.updating = false;
29004         if(!noLayout){
29005             this.layout();
29006         }    
29007     },
29008     
29009     layout: function(){
29010         
29011     },
29012     
29013     onRegionResized : function(region, newSize){
29014         this.fireEvent("regionresized", region, newSize);
29015         this.layout();
29016     },
29017     
29018     onRegionCollapsed : function(region){
29019         this.fireEvent("regioncollapsed", region);
29020     },
29021     
29022     onRegionExpanded : function(region){
29023         this.fireEvent("regionexpanded", region);
29024     },
29025         
29026     /**
29027      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29028      * performs box-model adjustments.
29029      * @return {Object} The size as an object {width: (the width), height: (the height)}
29030      */
29031     getViewSize : function(){
29032         var size;
29033         if(this.el.dom != document.body){
29034             size = this.el.getSize();
29035         }else{
29036             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29037         }
29038         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29039         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29040         return size;
29041     },
29042     
29043     /**
29044      * Returns the Element this layout is bound to.
29045      * @return {Roo.Element}
29046      */
29047     getEl : function(){
29048         return this.el;
29049     },
29050     
29051     /**
29052      * Returns the specified region.
29053      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29054      * @return {Roo.LayoutRegion}
29055      */
29056     getRegion : function(target){
29057         return this.regions[target.toLowerCase()];
29058     },
29059     
29060     onWindowResize : function(){
29061         if(this.monitorWindowResize){
29062             this.layout();
29063         }
29064     }
29065 });/*
29066  * Based on:
29067  * Ext JS Library 1.1.1
29068  * Copyright(c) 2006-2007, Ext JS, LLC.
29069  *
29070  * Originally Released Under LGPL - original licence link has changed is not relivant.
29071  *
29072  * Fork - LGPL
29073  * <script type="text/javascript">
29074  */
29075 /**
29076  * @class Roo.BorderLayout
29077  * @extends Roo.LayoutManager
29078  * @children Roo.ContentPanel
29079  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29080  * please see: <br><br>
29081  * <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>
29082  * <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>
29083  * Example:
29084  <pre><code>
29085  var layout = new Roo.BorderLayout(document.body, {
29086     north: {
29087         initialSize: 25,
29088         titlebar: false
29089     },
29090     west: {
29091         split:true,
29092         initialSize: 200,
29093         minSize: 175,
29094         maxSize: 400,
29095         titlebar: true,
29096         collapsible: true
29097     },
29098     east: {
29099         split:true,
29100         initialSize: 202,
29101         minSize: 175,
29102         maxSize: 400,
29103         titlebar: true,
29104         collapsible: true
29105     },
29106     south: {
29107         split:true,
29108         initialSize: 100,
29109         minSize: 100,
29110         maxSize: 200,
29111         titlebar: true,
29112         collapsible: true
29113     },
29114     center: {
29115         titlebar: true,
29116         autoScroll:true,
29117         resizeTabs: true,
29118         minTabWidth: 50,
29119         preferredTabWidth: 150
29120     }
29121 });
29122
29123 // shorthand
29124 var CP = Roo.ContentPanel;
29125
29126 layout.beginUpdate();
29127 layout.add("north", new CP("north", "North"));
29128 layout.add("south", new CP("south", {title: "South", closable: true}));
29129 layout.add("west", new CP("west", {title: "West"}));
29130 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29131 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29132 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29133 layout.getRegion("center").showPanel("center1");
29134 layout.endUpdate();
29135 </code></pre>
29136
29137 <b>The container the layout is rendered into can be either the body element or any other element.
29138 If it is not the body element, the container needs to either be an absolute positioned element,
29139 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29140 the container size if it is not the body element.</b>
29141
29142 * @constructor
29143 * Create a new BorderLayout
29144 * @param {String/HTMLElement/Element} container The container this layout is bound to
29145 * @param {Object} config Configuration options
29146  */
29147 Roo.BorderLayout = function(container, config){
29148     config = config || {};
29149     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29150     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29151     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29152         var target = this.factory.validRegions[i];
29153         if(config[target]){
29154             this.addRegion(target, config[target]);
29155         }
29156     }
29157 };
29158
29159 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29160         
29161         /**
29162          * @cfg {Roo.LayoutRegion} east
29163          */
29164         /**
29165          * @cfg {Roo.LayoutRegion} west
29166          */
29167         /**
29168          * @cfg {Roo.LayoutRegion} north
29169          */
29170         /**
29171          * @cfg {Roo.LayoutRegion} south
29172          */
29173         /**
29174          * @cfg {Roo.LayoutRegion} center
29175          */
29176     /**
29177      * Creates and adds a new region if it doesn't already exist.
29178      * @param {String} target The target region key (north, south, east, west or center).
29179      * @param {Object} config The regions config object
29180      * @return {BorderLayoutRegion} The new region
29181      */
29182     addRegion : function(target, config){
29183         if(!this.regions[target]){
29184             var r = this.factory.create(target, this, config);
29185             this.bindRegion(target, r);
29186         }
29187         return this.regions[target];
29188     },
29189
29190     // private (kinda)
29191     bindRegion : function(name, r){
29192         this.regions[name] = r;
29193         r.on("visibilitychange", this.layout, this);
29194         r.on("paneladded", this.layout, this);
29195         r.on("panelremoved", this.layout, this);
29196         r.on("invalidated", this.layout, this);
29197         r.on("resized", this.onRegionResized, this);
29198         r.on("collapsed", this.onRegionCollapsed, this);
29199         r.on("expanded", this.onRegionExpanded, this);
29200     },
29201
29202     /**
29203      * Performs a layout update.
29204      */
29205     layout : function(){
29206         if(this.updating) {
29207             return;
29208         }
29209         var size = this.getViewSize();
29210         var w = size.width;
29211         var h = size.height;
29212         var centerW = w;
29213         var centerH = h;
29214         var centerY = 0;
29215         var centerX = 0;
29216         //var x = 0, y = 0;
29217
29218         var rs = this.regions;
29219         var north = rs["north"];
29220         var south = rs["south"]; 
29221         var west = rs["west"];
29222         var east = rs["east"];
29223         var center = rs["center"];
29224         //if(this.hideOnLayout){ // not supported anymore
29225             //c.el.setStyle("display", "none");
29226         //}
29227         if(north && north.isVisible()){
29228             var b = north.getBox();
29229             var m = north.getMargins();
29230             b.width = w - (m.left+m.right);
29231             b.x = m.left;
29232             b.y = m.top;
29233             centerY = b.height + b.y + m.bottom;
29234             centerH -= centerY;
29235             north.updateBox(this.safeBox(b));
29236         }
29237         if(south && south.isVisible()){
29238             var b = south.getBox();
29239             var m = south.getMargins();
29240             b.width = w - (m.left+m.right);
29241             b.x = m.left;
29242             var totalHeight = (b.height + m.top + m.bottom);
29243             b.y = h - totalHeight + m.top;
29244             centerH -= totalHeight;
29245             south.updateBox(this.safeBox(b));
29246         }
29247         if(west && west.isVisible()){
29248             var b = west.getBox();
29249             var m = west.getMargins();
29250             b.height = centerH - (m.top+m.bottom);
29251             b.x = m.left;
29252             b.y = centerY + m.top;
29253             var totalWidth = (b.width + m.left + m.right);
29254             centerX += totalWidth;
29255             centerW -= totalWidth;
29256             west.updateBox(this.safeBox(b));
29257         }
29258         if(east && east.isVisible()){
29259             var b = east.getBox();
29260             var m = east.getMargins();
29261             b.height = centerH - (m.top+m.bottom);
29262             var totalWidth = (b.width + m.left + m.right);
29263             b.x = w - totalWidth + m.left;
29264             b.y = centerY + m.top;
29265             centerW -= totalWidth;
29266             east.updateBox(this.safeBox(b));
29267         }
29268         if(center){
29269             var m = center.getMargins();
29270             var centerBox = {
29271                 x: centerX + m.left,
29272                 y: centerY + m.top,
29273                 width: centerW - (m.left+m.right),
29274                 height: centerH - (m.top+m.bottom)
29275             };
29276             //if(this.hideOnLayout){
29277                 //center.el.setStyle("display", "block");
29278             //}
29279             center.updateBox(this.safeBox(centerBox));
29280         }
29281         this.el.repaint();
29282         this.fireEvent("layout", this);
29283     },
29284
29285     // private
29286     safeBox : function(box){
29287         box.width = Math.max(0, box.width);
29288         box.height = Math.max(0, box.height);
29289         return box;
29290     },
29291
29292     /**
29293      * Adds a ContentPanel (or subclass) to this layout.
29294      * @param {String} target The target region key (north, south, east, west or center).
29295      * @param {Roo.ContentPanel} panel The panel to add
29296      * @return {Roo.ContentPanel} The added panel
29297      */
29298     add : function(target, panel){
29299          
29300         target = target.toLowerCase();
29301         return this.regions[target].add(panel);
29302     },
29303
29304     /**
29305      * Remove a ContentPanel (or subclass) to this layout.
29306      * @param {String} target The target region key (north, south, east, west or center).
29307      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29308      * @return {Roo.ContentPanel} The removed panel
29309      */
29310     remove : function(target, panel){
29311         target = target.toLowerCase();
29312         return this.regions[target].remove(panel);
29313     },
29314
29315     /**
29316      * Searches all regions for a panel with the specified id
29317      * @param {String} panelId
29318      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29319      */
29320     findPanel : function(panelId){
29321         var rs = this.regions;
29322         for(var target in rs){
29323             if(typeof rs[target] != "function"){
29324                 var p = rs[target].getPanel(panelId);
29325                 if(p){
29326                     return p;
29327                 }
29328             }
29329         }
29330         return null;
29331     },
29332
29333     /**
29334      * Searches all regions for a panel with the specified id and activates (shows) it.
29335      * @param {String/ContentPanel} panelId The panels id or the panel itself
29336      * @return {Roo.ContentPanel} The shown panel or null
29337      */
29338     showPanel : function(panelId) {
29339       var rs = this.regions;
29340       for(var target in rs){
29341          var r = rs[target];
29342          if(typeof r != "function"){
29343             if(r.hasPanel(panelId)){
29344                return r.showPanel(panelId);
29345             }
29346          }
29347       }
29348       return null;
29349    },
29350
29351    /**
29352      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29353      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29354      */
29355     restoreState : function(provider){
29356         if(!provider){
29357             provider = Roo.state.Manager;
29358         }
29359         var sm = new Roo.LayoutStateManager();
29360         sm.init(this, provider);
29361     },
29362
29363     /**
29364      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29365      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29366      * a valid ContentPanel config object.  Example:
29367      * <pre><code>
29368 // Create the main layout
29369 var layout = new Roo.BorderLayout('main-ct', {
29370     west: {
29371         split:true,
29372         minSize: 175,
29373         titlebar: true
29374     },
29375     center: {
29376         title:'Components'
29377     }
29378 }, 'main-ct');
29379
29380 // Create and add multiple ContentPanels at once via configs
29381 layout.batchAdd({
29382    west: {
29383        id: 'source-files',
29384        autoCreate:true,
29385        title:'Ext Source Files',
29386        autoScroll:true,
29387        fitToFrame:true
29388    },
29389    center : {
29390        el: cview,
29391        autoScroll:true,
29392        fitToFrame:true,
29393        toolbar: tb,
29394        resizeEl:'cbody'
29395    }
29396 });
29397 </code></pre>
29398      * @param {Object} regions An object containing ContentPanel configs by region name
29399      */
29400     batchAdd : function(regions){
29401         this.beginUpdate();
29402         for(var rname in regions){
29403             var lr = this.regions[rname];
29404             if(lr){
29405                 this.addTypedPanels(lr, regions[rname]);
29406             }
29407         }
29408         this.endUpdate();
29409     },
29410
29411     // private
29412     addTypedPanels : function(lr, ps){
29413         if(typeof ps == 'string'){
29414             lr.add(new Roo.ContentPanel(ps));
29415         }
29416         else if(ps instanceof Array){
29417             for(var i =0, len = ps.length; i < len; i++){
29418                 this.addTypedPanels(lr, ps[i]);
29419             }
29420         }
29421         else if(!ps.events){ // raw config?
29422             var el = ps.el;
29423             delete ps.el; // prevent conflict
29424             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29425         }
29426         else {  // panel object assumed!
29427             lr.add(ps);
29428         }
29429     },
29430     /**
29431      * Adds a xtype elements to the layout.
29432      * <pre><code>
29433
29434 layout.addxtype({
29435        xtype : 'ContentPanel',
29436        region: 'west',
29437        items: [ .... ]
29438    }
29439 );
29440
29441 layout.addxtype({
29442         xtype : 'NestedLayoutPanel',
29443         region: 'west',
29444         layout: {
29445            center: { },
29446            west: { }   
29447         },
29448         items : [ ... list of content panels or nested layout panels.. ]
29449    }
29450 );
29451 </code></pre>
29452      * @param {Object} cfg Xtype definition of item to add.
29453      */
29454     addxtype : function(cfg)
29455     {
29456         // basically accepts a pannel...
29457         // can accept a layout region..!?!?
29458         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29459         
29460         if (!cfg.xtype.match(/Panel$/)) {
29461             return false;
29462         }
29463         var ret = false;
29464         
29465         if (typeof(cfg.region) == 'undefined') {
29466             Roo.log("Failed to add Panel, region was not set");
29467             Roo.log(cfg);
29468             return false;
29469         }
29470         var region = cfg.region;
29471         delete cfg.region;
29472         
29473           
29474         var xitems = [];
29475         if (cfg.items) {
29476             xitems = cfg.items;
29477             delete cfg.items;
29478         }
29479         var nb = false;
29480         
29481         switch(cfg.xtype) 
29482         {
29483             case 'ContentPanel':  // ContentPanel (el, cfg)
29484             case 'ScrollPanel':  // ContentPanel (el, cfg)
29485             case 'ViewPanel': 
29486                 if(cfg.autoCreate) {
29487                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29488                 } else {
29489                     var el = this.el.createChild();
29490                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29491                 }
29492                 
29493                 this.add(region, ret);
29494                 break;
29495             
29496             
29497             case 'TreePanel': // our new panel!
29498                 cfg.el = this.el.createChild();
29499                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29500                 this.add(region, ret);
29501                 break;
29502             
29503             case 'NestedLayoutPanel': 
29504                 // create a new Layout (which is  a Border Layout...
29505                 var el = this.el.createChild();
29506                 var clayout = cfg.layout;
29507                 delete cfg.layout;
29508                 clayout.items   = clayout.items  || [];
29509                 // replace this exitems with the clayout ones..
29510                 xitems = clayout.items;
29511                  
29512                 
29513                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29514                     cfg.background = false;
29515                 }
29516                 var layout = new Roo.BorderLayout(el, clayout);
29517                 
29518                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29519                 //console.log('adding nested layout panel '  + cfg.toSource());
29520                 this.add(region, ret);
29521                 nb = {}; /// find first...
29522                 break;
29523                 
29524             case 'GridPanel': 
29525             
29526                 // needs grid and region
29527                 
29528                 //var el = this.getRegion(region).el.createChild();
29529                 var el = this.el.createChild();
29530                 // create the grid first...
29531                 
29532                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29533                 delete cfg.grid;
29534                 if (region == 'center' && this.active ) {
29535                     cfg.background = false;
29536                 }
29537                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29538                 
29539                 this.add(region, ret);
29540                 if (cfg.background) {
29541                     ret.on('activate', function(gp) {
29542                         if (!gp.grid.rendered) {
29543                             gp.grid.render();
29544                         }
29545                     });
29546                 } else {
29547                     grid.render();
29548                 }
29549                 break;
29550            
29551            
29552            
29553                 
29554                 
29555                 
29556             default:
29557                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29558                     
29559                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29560                     this.add(region, ret);
29561                 } else {
29562                 
29563                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29564                     return null;
29565                 }
29566                 
29567              // GridPanel (grid, cfg)
29568             
29569         }
29570         this.beginUpdate();
29571         // add children..
29572         var region = '';
29573         var abn = {};
29574         Roo.each(xitems, function(i)  {
29575             region = nb && i.region ? i.region : false;
29576             
29577             var add = ret.addxtype(i);
29578            
29579             if (region) {
29580                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29581                 if (!i.background) {
29582                     abn[region] = nb[region] ;
29583                 }
29584             }
29585             
29586         });
29587         this.endUpdate();
29588
29589         // make the last non-background panel active..
29590         //if (nb) { Roo.log(abn); }
29591         if (nb) {
29592             
29593             for(var r in abn) {
29594                 region = this.getRegion(r);
29595                 if (region) {
29596                     // tried using nb[r], but it does not work..
29597                      
29598                     region.showPanel(abn[r]);
29599                    
29600                 }
29601             }
29602         }
29603         return ret;
29604         
29605     }
29606 });
29607
29608 /**
29609  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29610  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29611  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29612  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29613  * <pre><code>
29614 // shorthand
29615 var CP = Roo.ContentPanel;
29616
29617 var layout = Roo.BorderLayout.create({
29618     north: {
29619         initialSize: 25,
29620         titlebar: false,
29621         panels: [new CP("north", "North")]
29622     },
29623     west: {
29624         split:true,
29625         initialSize: 200,
29626         minSize: 175,
29627         maxSize: 400,
29628         titlebar: true,
29629         collapsible: true,
29630         panels: [new CP("west", {title: "West"})]
29631     },
29632     east: {
29633         split:true,
29634         initialSize: 202,
29635         minSize: 175,
29636         maxSize: 400,
29637         titlebar: true,
29638         collapsible: true,
29639         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29640     },
29641     south: {
29642         split:true,
29643         initialSize: 100,
29644         minSize: 100,
29645         maxSize: 200,
29646         titlebar: true,
29647         collapsible: true,
29648         panels: [new CP("south", {title: "South", closable: true})]
29649     },
29650     center: {
29651         titlebar: true,
29652         autoScroll:true,
29653         resizeTabs: true,
29654         minTabWidth: 50,
29655         preferredTabWidth: 150,
29656         panels: [
29657             new CP("center1", {title: "Close Me", closable: true}),
29658             new CP("center2", {title: "Center Panel", closable: false})
29659         ]
29660     }
29661 }, document.body);
29662
29663 layout.getRegion("center").showPanel("center1");
29664 </code></pre>
29665  * @param config
29666  * @param targetEl
29667  */
29668 Roo.BorderLayout.create = function(config, targetEl){
29669     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29670     layout.beginUpdate();
29671     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29672     for(var j = 0, jlen = regions.length; j < jlen; j++){
29673         var lr = regions[j];
29674         if(layout.regions[lr] && config[lr].panels){
29675             var r = layout.regions[lr];
29676             var ps = config[lr].panels;
29677             layout.addTypedPanels(r, ps);
29678         }
29679     }
29680     layout.endUpdate();
29681     return layout;
29682 };
29683
29684 // private
29685 Roo.BorderLayout.RegionFactory = {
29686     // private
29687     validRegions : ["north","south","east","west","center"],
29688
29689     // private
29690     create : function(target, mgr, config){
29691         target = target.toLowerCase();
29692         if(config.lightweight || config.basic){
29693             return new Roo.BasicLayoutRegion(mgr, config, target);
29694         }
29695         switch(target){
29696             case "north":
29697                 return new Roo.NorthLayoutRegion(mgr, config);
29698             case "south":
29699                 return new Roo.SouthLayoutRegion(mgr, config);
29700             case "east":
29701                 return new Roo.EastLayoutRegion(mgr, config);
29702             case "west":
29703                 return new Roo.WestLayoutRegion(mgr, config);
29704             case "center":
29705                 return new Roo.CenterLayoutRegion(mgr, config);
29706         }
29707         throw 'Layout region "'+target+'" not supported.';
29708     }
29709 };/*
29710  * Based on:
29711  * Ext JS Library 1.1.1
29712  * Copyright(c) 2006-2007, Ext JS, LLC.
29713  *
29714  * Originally Released Under LGPL - original licence link has changed is not relivant.
29715  *
29716  * Fork - LGPL
29717  * <script type="text/javascript">
29718  */
29719  
29720 /**
29721  * @class Roo.BasicLayoutRegion
29722  * @extends Roo.util.Observable
29723  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29724  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29725  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29726  */
29727 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29728     this.mgr = mgr;
29729     this.position  = pos;
29730     this.events = {
29731         /**
29732          * @scope Roo.BasicLayoutRegion
29733          */
29734         
29735         /**
29736          * @event beforeremove
29737          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29738          * @param {Roo.LayoutRegion} this
29739          * @param {Roo.ContentPanel} panel The panel
29740          * @param {Object} e The cancel event object
29741          */
29742         "beforeremove" : true,
29743         /**
29744          * @event invalidated
29745          * Fires when the layout for this region is changed.
29746          * @param {Roo.LayoutRegion} this
29747          */
29748         "invalidated" : true,
29749         /**
29750          * @event visibilitychange
29751          * Fires when this region is shown or hidden 
29752          * @param {Roo.LayoutRegion} this
29753          * @param {Boolean} visibility true or false
29754          */
29755         "visibilitychange" : true,
29756         /**
29757          * @event paneladded
29758          * Fires when a panel is added. 
29759          * @param {Roo.LayoutRegion} this
29760          * @param {Roo.ContentPanel} panel The panel
29761          */
29762         "paneladded" : true,
29763         /**
29764          * @event panelremoved
29765          * Fires when a panel is removed. 
29766          * @param {Roo.LayoutRegion} this
29767          * @param {Roo.ContentPanel} panel The panel
29768          */
29769         "panelremoved" : true,
29770         /**
29771          * @event beforecollapse
29772          * Fires when this region before collapse.
29773          * @param {Roo.LayoutRegion} this
29774          */
29775         "beforecollapse" : true,
29776         /**
29777          * @event collapsed
29778          * Fires when this region is collapsed.
29779          * @param {Roo.LayoutRegion} this
29780          */
29781         "collapsed" : true,
29782         /**
29783          * @event expanded
29784          * Fires when this region is expanded.
29785          * @param {Roo.LayoutRegion} this
29786          */
29787         "expanded" : true,
29788         /**
29789          * @event slideshow
29790          * Fires when this region is slid into view.
29791          * @param {Roo.LayoutRegion} this
29792          */
29793         "slideshow" : true,
29794         /**
29795          * @event slidehide
29796          * Fires when this region slides out of view. 
29797          * @param {Roo.LayoutRegion} this
29798          */
29799         "slidehide" : true,
29800         /**
29801          * @event panelactivated
29802          * Fires when a panel is activated. 
29803          * @param {Roo.LayoutRegion} this
29804          * @param {Roo.ContentPanel} panel The activated panel
29805          */
29806         "panelactivated" : true,
29807         /**
29808          * @event resized
29809          * Fires when the user resizes this region. 
29810          * @param {Roo.LayoutRegion} this
29811          * @param {Number} newSize The new size (width for east/west, height for north/south)
29812          */
29813         "resized" : true
29814     };
29815     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29816     this.panels = new Roo.util.MixedCollection();
29817     this.panels.getKey = this.getPanelId.createDelegate(this);
29818     this.box = null;
29819     this.activePanel = null;
29820     // ensure listeners are added...
29821     
29822     if (config.listeners || config.events) {
29823         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29824             listeners : config.listeners || {},
29825             events : config.events || {}
29826         });
29827     }
29828     
29829     if(skipConfig !== true){
29830         this.applyConfig(config);
29831     }
29832 };
29833
29834 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29835     getPanelId : function(p){
29836         return p.getId();
29837     },
29838     
29839     applyConfig : function(config){
29840         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29841         this.config = config;
29842         
29843     },
29844     
29845     /**
29846      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29847      * the width, for horizontal (north, south) the height.
29848      * @param {Number} newSize The new width or height
29849      */
29850     resizeTo : function(newSize){
29851         var el = this.el ? this.el :
29852                  (this.activePanel ? this.activePanel.getEl() : null);
29853         if(el){
29854             switch(this.position){
29855                 case "east":
29856                 case "west":
29857                     el.setWidth(newSize);
29858                     this.fireEvent("resized", this, newSize);
29859                 break;
29860                 case "north":
29861                 case "south":
29862                     el.setHeight(newSize);
29863                     this.fireEvent("resized", this, newSize);
29864                 break;                
29865             }
29866         }
29867     },
29868     
29869     getBox : function(){
29870         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29871     },
29872     
29873     getMargins : function(){
29874         return this.margins;
29875     },
29876     
29877     updateBox : function(box){
29878         this.box = box;
29879         var el = this.activePanel.getEl();
29880         el.dom.style.left = box.x + "px";
29881         el.dom.style.top = box.y + "px";
29882         this.activePanel.setSize(box.width, box.height);
29883     },
29884     
29885     /**
29886      * Returns the container element for this region.
29887      * @return {Roo.Element}
29888      */
29889     getEl : function(){
29890         return this.activePanel;
29891     },
29892     
29893     /**
29894      * Returns true if this region is currently visible.
29895      * @return {Boolean}
29896      */
29897     isVisible : function(){
29898         return this.activePanel ? true : false;
29899     },
29900     
29901     setActivePanel : function(panel){
29902         panel = this.getPanel(panel);
29903         if(this.activePanel && this.activePanel != panel){
29904             this.activePanel.setActiveState(false);
29905             this.activePanel.getEl().setLeftTop(-10000,-10000);
29906         }
29907         this.activePanel = panel;
29908         panel.setActiveState(true);
29909         if(this.box){
29910             panel.setSize(this.box.width, this.box.height);
29911         }
29912         this.fireEvent("panelactivated", this, panel);
29913         this.fireEvent("invalidated");
29914     },
29915     
29916     /**
29917      * Show the specified panel.
29918      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29919      * @return {Roo.ContentPanel} The shown panel or null
29920      */
29921     showPanel : function(panel){
29922         if(panel = this.getPanel(panel)){
29923             this.setActivePanel(panel);
29924         }
29925         return panel;
29926     },
29927     
29928     /**
29929      * Get the active panel for this region.
29930      * @return {Roo.ContentPanel} The active panel or null
29931      */
29932     getActivePanel : function(){
29933         return this.activePanel;
29934     },
29935     
29936     /**
29937      * Add the passed ContentPanel(s)
29938      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29939      * @return {Roo.ContentPanel} The panel added (if only one was added)
29940      */
29941     add : function(panel){
29942         if(arguments.length > 1){
29943             for(var i = 0, len = arguments.length; i < len; i++) {
29944                 this.add(arguments[i]);
29945             }
29946             return null;
29947         }
29948         if(this.hasPanel(panel)){
29949             this.showPanel(panel);
29950             return panel;
29951         }
29952         var el = panel.getEl();
29953         if(el.dom.parentNode != this.mgr.el.dom){
29954             this.mgr.el.dom.appendChild(el.dom);
29955         }
29956         if(panel.setRegion){
29957             panel.setRegion(this);
29958         }
29959         this.panels.add(panel);
29960         el.setStyle("position", "absolute");
29961         if(!panel.background){
29962             this.setActivePanel(panel);
29963             if(this.config.initialSize && this.panels.getCount()==1){
29964                 this.resizeTo(this.config.initialSize);
29965             }
29966         }
29967         this.fireEvent("paneladded", this, panel);
29968         return panel;
29969     },
29970     
29971     /**
29972      * Returns true if the panel is in this region.
29973      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29974      * @return {Boolean}
29975      */
29976     hasPanel : function(panel){
29977         if(typeof panel == "object"){ // must be panel obj
29978             panel = panel.getId();
29979         }
29980         return this.getPanel(panel) ? true : false;
29981     },
29982     
29983     /**
29984      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29985      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29986      * @param {Boolean} preservePanel Overrides the config preservePanel option
29987      * @return {Roo.ContentPanel} The panel that was removed
29988      */
29989     remove : function(panel, preservePanel){
29990         panel = this.getPanel(panel);
29991         if(!panel){
29992             return null;
29993         }
29994         var e = {};
29995         this.fireEvent("beforeremove", this, panel, e);
29996         if(e.cancel === true){
29997             return null;
29998         }
29999         var panelId = panel.getId();
30000         this.panels.removeKey(panelId);
30001         return panel;
30002     },
30003     
30004     /**
30005      * Returns the panel specified or null if it's not in this region.
30006      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30007      * @return {Roo.ContentPanel}
30008      */
30009     getPanel : function(id){
30010         if(typeof id == "object"){ // must be panel obj
30011             return id;
30012         }
30013         return this.panels.get(id);
30014     },
30015     
30016     /**
30017      * Returns this regions position (north/south/east/west/center).
30018      * @return {String} 
30019      */
30020     getPosition: function(){
30021         return this.position;    
30022     }
30023 });/*
30024  * Based on:
30025  * Ext JS Library 1.1.1
30026  * Copyright(c) 2006-2007, Ext JS, LLC.
30027  *
30028  * Originally Released Under LGPL - original licence link has changed is not relivant.
30029  *
30030  * Fork - LGPL
30031  * <script type="text/javascript">
30032  */
30033  
30034 /**
30035  * @class Roo.LayoutRegion
30036  * @extends Roo.BasicLayoutRegion
30037  * This class represents a region in a layout manager.
30038  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30039  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30040  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30041  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30042  * @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})
30043  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30044  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30045  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30046  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30047  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30048  * @cfg {String}    title           The title for the region (overrides panel titles)
30049  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30050  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30051  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30052  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30053  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30054  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30055  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30056  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30057  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30058  * @cfg {Boolean}   showPin         True to show a pin button
30059  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30060  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30061  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30062  * @cfg {Number}    width           For East/West panels
30063  * @cfg {Number}    height          For North/South panels
30064  * @cfg {Boolean}   split           To show the splitter
30065  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30066  */
30067 Roo.LayoutRegion = function(mgr, config, pos){
30068     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30069     var dh = Roo.DomHelper;
30070     /** This region's container element 
30071     * @type Roo.Element */
30072     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30073     /** This region's title element 
30074     * @type Roo.Element */
30075
30076     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30077         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30078         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30079     ]}, true);
30080     this.titleEl.enableDisplayMode();
30081     /** This region's title text element 
30082     * @type HTMLElement */
30083     this.titleTextEl = this.titleEl.dom.firstChild;
30084     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30085     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30086     this.closeBtn.enableDisplayMode();
30087     this.closeBtn.on("click", this.closeClicked, this);
30088     this.closeBtn.hide();
30089
30090     this.createBody(config);
30091     this.visible = true;
30092     this.collapsed = false;
30093
30094     if(config.hideWhenEmpty){
30095         this.hide();
30096         this.on("paneladded", this.validateVisibility, this);
30097         this.on("panelremoved", this.validateVisibility, this);
30098     }
30099     this.applyConfig(config);
30100 };
30101
30102 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30103
30104     createBody : function(){
30105         /** This region's body element 
30106         * @type Roo.Element */
30107         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30108     },
30109
30110     applyConfig : function(c){
30111         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30112             var dh = Roo.DomHelper;
30113             if(c.titlebar !== false){
30114                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30115                 this.collapseBtn.on("click", this.collapse, this);
30116                 this.collapseBtn.enableDisplayMode();
30117
30118                 if(c.showPin === true || this.showPin){
30119                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30120                     this.stickBtn.enableDisplayMode();
30121                     this.stickBtn.on("click", this.expand, this);
30122                     this.stickBtn.hide();
30123                 }
30124             }
30125             /** This region's collapsed element
30126             * @type Roo.Element */
30127             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30128                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30129             ]}, true);
30130             if(c.floatable !== false){
30131                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30132                this.collapsedEl.on("click", this.collapseClick, this);
30133             }
30134
30135             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30136                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30137                    id: "message", unselectable: "on", style:{"float":"left"}});
30138                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30139              }
30140             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30141             this.expandBtn.on("click", this.expand, this);
30142         }
30143         if(this.collapseBtn){
30144             this.collapseBtn.setVisible(c.collapsible == true);
30145         }
30146         this.cmargins = c.cmargins || this.cmargins ||
30147                          (this.position == "west" || this.position == "east" ?
30148                              {top: 0, left: 2, right:2, bottom: 0} :
30149                              {top: 2, left: 0, right:0, bottom: 2});
30150         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30151         this.bottomTabs = c.tabPosition != "top";
30152         this.autoScroll = c.autoScroll || false;
30153         if(this.autoScroll){
30154             this.bodyEl.setStyle("overflow", "auto");
30155         }else{
30156             this.bodyEl.setStyle("overflow", "hidden");
30157         }
30158         //if(c.titlebar !== false){
30159             if((!c.titlebar && !c.title) || c.titlebar === false){
30160                 this.titleEl.hide();
30161             }else{
30162                 this.titleEl.show();
30163                 if(c.title){
30164                     this.titleTextEl.innerHTML = c.title;
30165                 }
30166             }
30167         //}
30168         this.duration = c.duration || .30;
30169         this.slideDuration = c.slideDuration || .45;
30170         this.config = c;
30171         if(c.collapsed){
30172             this.collapse(true);
30173         }
30174         if(c.hidden){
30175             this.hide();
30176         }
30177     },
30178     /**
30179      * Returns true if this region is currently visible.
30180      * @return {Boolean}
30181      */
30182     isVisible : function(){
30183         return this.visible;
30184     },
30185
30186     /**
30187      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30188      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30189      */
30190     setCollapsedTitle : function(title){
30191         title = title || "&#160;";
30192         if(this.collapsedTitleTextEl){
30193             this.collapsedTitleTextEl.innerHTML = title;
30194         }
30195     },
30196
30197     getBox : function(){
30198         var b;
30199         if(!this.collapsed){
30200             b = this.el.getBox(false, true);
30201         }else{
30202             b = this.collapsedEl.getBox(false, true);
30203         }
30204         return b;
30205     },
30206
30207     getMargins : function(){
30208         return this.collapsed ? this.cmargins : this.margins;
30209     },
30210
30211     highlight : function(){
30212         this.el.addClass("x-layout-panel-dragover");
30213     },
30214
30215     unhighlight : function(){
30216         this.el.removeClass("x-layout-panel-dragover");
30217     },
30218
30219     updateBox : function(box){
30220         this.box = box;
30221         if(!this.collapsed){
30222             this.el.dom.style.left = box.x + "px";
30223             this.el.dom.style.top = box.y + "px";
30224             this.updateBody(box.width, box.height);
30225         }else{
30226             this.collapsedEl.dom.style.left = box.x + "px";
30227             this.collapsedEl.dom.style.top = box.y + "px";
30228             this.collapsedEl.setSize(box.width, box.height);
30229         }
30230         if(this.tabs){
30231             this.tabs.autoSizeTabs();
30232         }
30233     },
30234
30235     updateBody : function(w, h){
30236         if(w !== null){
30237             this.el.setWidth(w);
30238             w -= this.el.getBorderWidth("rl");
30239             if(this.config.adjustments){
30240                 w += this.config.adjustments[0];
30241             }
30242         }
30243         if(h !== null){
30244             this.el.setHeight(h);
30245             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30246             h -= this.el.getBorderWidth("tb");
30247             if(this.config.adjustments){
30248                 h += this.config.adjustments[1];
30249             }
30250             this.bodyEl.setHeight(h);
30251             if(this.tabs){
30252                 h = this.tabs.syncHeight(h);
30253             }
30254         }
30255         if(this.panelSize){
30256             w = w !== null ? w : this.panelSize.width;
30257             h = h !== null ? h : this.panelSize.height;
30258         }
30259         if(this.activePanel){
30260             var el = this.activePanel.getEl();
30261             w = w !== null ? w : el.getWidth();
30262             h = h !== null ? h : el.getHeight();
30263             this.panelSize = {width: w, height: h};
30264             this.activePanel.setSize(w, h);
30265         }
30266         if(Roo.isIE && this.tabs){
30267             this.tabs.el.repaint();
30268         }
30269     },
30270
30271     /**
30272      * Returns the container element for this region.
30273      * @return {Roo.Element}
30274      */
30275     getEl : function(){
30276         return this.el;
30277     },
30278
30279     /**
30280      * Hides this region.
30281      */
30282     hide : function(){
30283         if(!this.collapsed){
30284             this.el.dom.style.left = "-2000px";
30285             this.el.hide();
30286         }else{
30287             this.collapsedEl.dom.style.left = "-2000px";
30288             this.collapsedEl.hide();
30289         }
30290         this.visible = false;
30291         this.fireEvent("visibilitychange", this, false);
30292     },
30293
30294     /**
30295      * Shows this region if it was previously hidden.
30296      */
30297     show : function(){
30298         if(!this.collapsed){
30299             this.el.show();
30300         }else{
30301             this.collapsedEl.show();
30302         }
30303         this.visible = true;
30304         this.fireEvent("visibilitychange", this, true);
30305     },
30306
30307     closeClicked : function(){
30308         if(this.activePanel){
30309             this.remove(this.activePanel);
30310         }
30311     },
30312
30313     collapseClick : function(e){
30314         if(this.isSlid){
30315            e.stopPropagation();
30316            this.slideIn();
30317         }else{
30318            e.stopPropagation();
30319            this.slideOut();
30320         }
30321     },
30322
30323     /**
30324      * Collapses this region.
30325      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30326      */
30327     collapse : function(skipAnim, skipCheck){
30328         if(this.collapsed) {
30329             return;
30330         }
30331         
30332         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30333             
30334             this.collapsed = true;
30335             if(this.split){
30336                 this.split.el.hide();
30337             }
30338             if(this.config.animate && skipAnim !== true){
30339                 this.fireEvent("invalidated", this);
30340                 this.animateCollapse();
30341             }else{
30342                 this.el.setLocation(-20000,-20000);
30343                 this.el.hide();
30344                 this.collapsedEl.show();
30345                 this.fireEvent("collapsed", this);
30346                 this.fireEvent("invalidated", this);
30347             }
30348         }
30349         
30350     },
30351
30352     animateCollapse : function(){
30353         // overridden
30354     },
30355
30356     /**
30357      * Expands this region if it was previously collapsed.
30358      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30359      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30360      */
30361     expand : function(e, skipAnim){
30362         if(e) {
30363             e.stopPropagation();
30364         }
30365         if(!this.collapsed || this.el.hasActiveFx()) {
30366             return;
30367         }
30368         if(this.isSlid){
30369             this.afterSlideIn();
30370             skipAnim = true;
30371         }
30372         this.collapsed = false;
30373         if(this.config.animate && skipAnim !== true){
30374             this.animateExpand();
30375         }else{
30376             this.el.show();
30377             if(this.split){
30378                 this.split.el.show();
30379             }
30380             this.collapsedEl.setLocation(-2000,-2000);
30381             this.collapsedEl.hide();
30382             this.fireEvent("invalidated", this);
30383             this.fireEvent("expanded", this);
30384         }
30385     },
30386
30387     animateExpand : function(){
30388         // overridden
30389     },
30390
30391     initTabs : function()
30392     {
30393         this.bodyEl.setStyle("overflow", "hidden");
30394         var ts = new Roo.TabPanel(
30395                 this.bodyEl.dom,
30396                 {
30397                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30398                     disableTooltips: this.config.disableTabTips,
30399                     toolbar : this.config.toolbar
30400                 }
30401         );
30402         if(this.config.hideTabs){
30403             ts.stripWrap.setDisplayed(false);
30404         }
30405         this.tabs = ts;
30406         ts.resizeTabs = this.config.resizeTabs === true;
30407         ts.minTabWidth = this.config.minTabWidth || 40;
30408         ts.maxTabWidth = this.config.maxTabWidth || 250;
30409         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30410         ts.monitorResize = false;
30411         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30412         ts.bodyEl.addClass('x-layout-tabs-body');
30413         this.panels.each(this.initPanelAsTab, this);
30414     },
30415
30416     initPanelAsTab : function(panel){
30417         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30418                     this.config.closeOnTab && panel.isClosable());
30419         if(panel.tabTip !== undefined){
30420             ti.setTooltip(panel.tabTip);
30421         }
30422         ti.on("activate", function(){
30423               this.setActivePanel(panel);
30424         }, this);
30425         if(this.config.closeOnTab){
30426             ti.on("beforeclose", function(t, e){
30427                 e.cancel = true;
30428                 this.remove(panel);
30429             }, this);
30430         }
30431         return ti;
30432     },
30433
30434     updatePanelTitle : function(panel, title){
30435         if(this.activePanel == panel){
30436             this.updateTitle(title);
30437         }
30438         if(this.tabs){
30439             var ti = this.tabs.getTab(panel.getEl().id);
30440             ti.setText(title);
30441             if(panel.tabTip !== undefined){
30442                 ti.setTooltip(panel.tabTip);
30443             }
30444         }
30445     },
30446
30447     updateTitle : function(title){
30448         if(this.titleTextEl && !this.config.title){
30449             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30450         }
30451     },
30452
30453     setActivePanel : function(panel){
30454         panel = this.getPanel(panel);
30455         if(this.activePanel && this.activePanel != panel){
30456             this.activePanel.setActiveState(false);
30457         }
30458         this.activePanel = panel;
30459         panel.setActiveState(true);
30460         if(this.panelSize){
30461             panel.setSize(this.panelSize.width, this.panelSize.height);
30462         }
30463         if(this.closeBtn){
30464             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30465         }
30466         this.updateTitle(panel.getTitle());
30467         if(this.tabs){
30468             this.fireEvent("invalidated", this);
30469         }
30470         this.fireEvent("panelactivated", this, panel);
30471     },
30472
30473     /**
30474      * Shows the specified panel.
30475      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30476      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30477      */
30478     showPanel : function(panel)
30479     {
30480         panel = this.getPanel(panel);
30481         if(panel){
30482             if(this.tabs){
30483                 var tab = this.tabs.getTab(panel.getEl().id);
30484                 if(tab.isHidden()){
30485                     this.tabs.unhideTab(tab.id);
30486                 }
30487                 tab.activate();
30488             }else{
30489                 this.setActivePanel(panel);
30490             }
30491         }
30492         return panel;
30493     },
30494
30495     /**
30496      * Get the active panel for this region.
30497      * @return {Roo.ContentPanel} The active panel or null
30498      */
30499     getActivePanel : function(){
30500         return this.activePanel;
30501     },
30502
30503     validateVisibility : function(){
30504         if(this.panels.getCount() < 1){
30505             this.updateTitle("&#160;");
30506             this.closeBtn.hide();
30507             this.hide();
30508         }else{
30509             if(!this.isVisible()){
30510                 this.show();
30511             }
30512         }
30513     },
30514
30515     /**
30516      * Adds the passed ContentPanel(s) to this region.
30517      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30518      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30519      */
30520     add : function(panel){
30521         if(arguments.length > 1){
30522             for(var i = 0, len = arguments.length; i < len; i++) {
30523                 this.add(arguments[i]);
30524             }
30525             return null;
30526         }
30527         if(this.hasPanel(panel)){
30528             this.showPanel(panel);
30529             return panel;
30530         }
30531         panel.setRegion(this);
30532         this.panels.add(panel);
30533         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30534             this.bodyEl.dom.appendChild(panel.getEl().dom);
30535             if(panel.background !== true){
30536                 this.setActivePanel(panel);
30537             }
30538             this.fireEvent("paneladded", this, panel);
30539             return panel;
30540         }
30541         if(!this.tabs){
30542             this.initTabs();
30543         }else{
30544             this.initPanelAsTab(panel);
30545         }
30546         if(panel.background !== true){
30547             this.tabs.activate(panel.getEl().id);
30548         }
30549         this.fireEvent("paneladded", this, panel);
30550         return panel;
30551     },
30552
30553     /**
30554      * Hides the tab for the specified panel.
30555      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30556      */
30557     hidePanel : function(panel){
30558         if(this.tabs && (panel = this.getPanel(panel))){
30559             this.tabs.hideTab(panel.getEl().id);
30560         }
30561     },
30562
30563     /**
30564      * Unhides the tab for a previously hidden panel.
30565      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30566      */
30567     unhidePanel : function(panel){
30568         if(this.tabs && (panel = this.getPanel(panel))){
30569             this.tabs.unhideTab(panel.getEl().id);
30570         }
30571     },
30572
30573     clearPanels : function(){
30574         while(this.panels.getCount() > 0){
30575              this.remove(this.panels.first());
30576         }
30577     },
30578
30579     /**
30580      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30581      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30582      * @param {Boolean} preservePanel Overrides the config preservePanel option
30583      * @return {Roo.ContentPanel} The panel that was removed
30584      */
30585     remove : function(panel, preservePanel){
30586         panel = this.getPanel(panel);
30587         if(!panel){
30588             return null;
30589         }
30590         var e = {};
30591         this.fireEvent("beforeremove", this, panel, e);
30592         if(e.cancel === true){
30593             return null;
30594         }
30595         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30596         var panelId = panel.getId();
30597         this.panels.removeKey(panelId);
30598         if(preservePanel){
30599             document.body.appendChild(panel.getEl().dom);
30600         }
30601         if(this.tabs){
30602             this.tabs.removeTab(panel.getEl().id);
30603         }else if (!preservePanel){
30604             this.bodyEl.dom.removeChild(panel.getEl().dom);
30605         }
30606         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30607             var p = this.panels.first();
30608             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30609             tempEl.appendChild(p.getEl().dom);
30610             this.bodyEl.update("");
30611             this.bodyEl.dom.appendChild(p.getEl().dom);
30612             tempEl = null;
30613             this.updateTitle(p.getTitle());
30614             this.tabs = null;
30615             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30616             this.setActivePanel(p);
30617         }
30618         panel.setRegion(null);
30619         if(this.activePanel == panel){
30620             this.activePanel = null;
30621         }
30622         if(this.config.autoDestroy !== false && preservePanel !== true){
30623             try{panel.destroy();}catch(e){}
30624         }
30625         this.fireEvent("panelremoved", this, panel);
30626         return panel;
30627     },
30628
30629     /**
30630      * Returns the TabPanel component used by this region
30631      * @return {Roo.TabPanel}
30632      */
30633     getTabs : function(){
30634         return this.tabs;
30635     },
30636
30637     createTool : function(parentEl, className){
30638         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30639             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30640         btn.addClassOnOver("x-layout-tools-button-over");
30641         return btn;
30642     }
30643 });/*
30644  * Based on:
30645  * Ext JS Library 1.1.1
30646  * Copyright(c) 2006-2007, Ext JS, LLC.
30647  *
30648  * Originally Released Under LGPL - original licence link has changed is not relivant.
30649  *
30650  * Fork - LGPL
30651  * <script type="text/javascript">
30652  */
30653  
30654
30655
30656 /**
30657  * @class Roo.SplitLayoutRegion
30658  * @extends Roo.LayoutRegion
30659  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30660  */
30661 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30662     this.cursor = cursor;
30663     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30664 };
30665
30666 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30667     splitTip : "Drag to resize.",
30668     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30669     useSplitTips : false,
30670
30671     applyConfig : function(config){
30672         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30673         if(config.split){
30674             if(!this.split){
30675                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30676                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30677                 /** The SplitBar for this region 
30678                 * @type Roo.SplitBar */
30679                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30680                 this.split.on("moved", this.onSplitMove, this);
30681                 this.split.useShim = config.useShim === true;
30682                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30683                 if(this.useSplitTips){
30684                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30685                 }
30686                 if(config.collapsible){
30687                     this.split.el.on("dblclick", this.collapse,  this);
30688                 }
30689             }
30690             if(typeof config.minSize != "undefined"){
30691                 this.split.minSize = config.minSize;
30692             }
30693             if(typeof config.maxSize != "undefined"){
30694                 this.split.maxSize = config.maxSize;
30695             }
30696             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30697                 this.hideSplitter();
30698             }
30699         }
30700     },
30701
30702     getHMaxSize : function(){
30703          var cmax = this.config.maxSize || 10000;
30704          var center = this.mgr.getRegion("center");
30705          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30706     },
30707
30708     getVMaxSize : function(){
30709          var cmax = this.config.maxSize || 10000;
30710          var center = this.mgr.getRegion("center");
30711          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30712     },
30713
30714     onSplitMove : function(split, newSize){
30715         this.fireEvent("resized", this, newSize);
30716     },
30717     
30718     /** 
30719      * Returns the {@link Roo.SplitBar} for this region.
30720      * @return {Roo.SplitBar}
30721      */
30722     getSplitBar : function(){
30723         return this.split;
30724     },
30725     
30726     hide : function(){
30727         this.hideSplitter();
30728         Roo.SplitLayoutRegion.superclass.hide.call(this);
30729     },
30730
30731     hideSplitter : function(){
30732         if(this.split){
30733             this.split.el.setLocation(-2000,-2000);
30734             this.split.el.hide();
30735         }
30736     },
30737
30738     show : function(){
30739         if(this.split){
30740             this.split.el.show();
30741         }
30742         Roo.SplitLayoutRegion.superclass.show.call(this);
30743     },
30744     
30745     beforeSlide: function(){
30746         if(Roo.isGecko){// firefox overflow auto bug workaround
30747             this.bodyEl.clip();
30748             if(this.tabs) {
30749                 this.tabs.bodyEl.clip();
30750             }
30751             if(this.activePanel){
30752                 this.activePanel.getEl().clip();
30753                 
30754                 if(this.activePanel.beforeSlide){
30755                     this.activePanel.beforeSlide();
30756                 }
30757             }
30758         }
30759     },
30760     
30761     afterSlide : function(){
30762         if(Roo.isGecko){// firefox overflow auto bug workaround
30763             this.bodyEl.unclip();
30764             if(this.tabs) {
30765                 this.tabs.bodyEl.unclip();
30766             }
30767             if(this.activePanel){
30768                 this.activePanel.getEl().unclip();
30769                 if(this.activePanel.afterSlide){
30770                     this.activePanel.afterSlide();
30771                 }
30772             }
30773         }
30774     },
30775
30776     initAutoHide : function(){
30777         if(this.autoHide !== false){
30778             if(!this.autoHideHd){
30779                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30780                 this.autoHideHd = {
30781                     "mouseout": function(e){
30782                         if(!e.within(this.el, true)){
30783                             st.delay(500);
30784                         }
30785                     },
30786                     "mouseover" : function(e){
30787                         st.cancel();
30788                     },
30789                     scope : this
30790                 };
30791             }
30792             this.el.on(this.autoHideHd);
30793         }
30794     },
30795
30796     clearAutoHide : function(){
30797         if(this.autoHide !== false){
30798             this.el.un("mouseout", this.autoHideHd.mouseout);
30799             this.el.un("mouseover", this.autoHideHd.mouseover);
30800         }
30801     },
30802
30803     clearMonitor : function(){
30804         Roo.get(document).un("click", this.slideInIf, this);
30805     },
30806
30807     // these names are backwards but not changed for compat
30808     slideOut : function(){
30809         if(this.isSlid || this.el.hasActiveFx()){
30810             return;
30811         }
30812         this.isSlid = true;
30813         if(this.collapseBtn){
30814             this.collapseBtn.hide();
30815         }
30816         this.closeBtnState = this.closeBtn.getStyle('display');
30817         this.closeBtn.hide();
30818         if(this.stickBtn){
30819             this.stickBtn.show();
30820         }
30821         this.el.show();
30822         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30823         this.beforeSlide();
30824         this.el.setStyle("z-index", 10001);
30825         this.el.slideIn(this.getSlideAnchor(), {
30826             callback: function(){
30827                 this.afterSlide();
30828                 this.initAutoHide();
30829                 Roo.get(document).on("click", this.slideInIf, this);
30830                 this.fireEvent("slideshow", this);
30831             },
30832             scope: this,
30833             block: true
30834         });
30835     },
30836
30837     afterSlideIn : function(){
30838         this.clearAutoHide();
30839         this.isSlid = false;
30840         this.clearMonitor();
30841         this.el.setStyle("z-index", "");
30842         if(this.collapseBtn){
30843             this.collapseBtn.show();
30844         }
30845         this.closeBtn.setStyle('display', this.closeBtnState);
30846         if(this.stickBtn){
30847             this.stickBtn.hide();
30848         }
30849         this.fireEvent("slidehide", this);
30850     },
30851
30852     slideIn : function(cb){
30853         if(!this.isSlid || this.el.hasActiveFx()){
30854             Roo.callback(cb);
30855             return;
30856         }
30857         this.isSlid = false;
30858         this.beforeSlide();
30859         this.el.slideOut(this.getSlideAnchor(), {
30860             callback: function(){
30861                 this.el.setLeftTop(-10000, -10000);
30862                 this.afterSlide();
30863                 this.afterSlideIn();
30864                 Roo.callback(cb);
30865             },
30866             scope: this,
30867             block: true
30868         });
30869     },
30870     
30871     slideInIf : function(e){
30872         if(!e.within(this.el)){
30873             this.slideIn();
30874         }
30875     },
30876
30877     animateCollapse : function(){
30878         this.beforeSlide();
30879         this.el.setStyle("z-index", 20000);
30880         var anchor = this.getSlideAnchor();
30881         this.el.slideOut(anchor, {
30882             callback : function(){
30883                 this.el.setStyle("z-index", "");
30884                 this.collapsedEl.slideIn(anchor, {duration:.3});
30885                 this.afterSlide();
30886                 this.el.setLocation(-10000,-10000);
30887                 this.el.hide();
30888                 this.fireEvent("collapsed", this);
30889             },
30890             scope: this,
30891             block: true
30892         });
30893     },
30894
30895     animateExpand : function(){
30896         this.beforeSlide();
30897         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30898         this.el.setStyle("z-index", 20000);
30899         this.collapsedEl.hide({
30900             duration:.1
30901         });
30902         this.el.slideIn(this.getSlideAnchor(), {
30903             callback : function(){
30904                 this.el.setStyle("z-index", "");
30905                 this.afterSlide();
30906                 if(this.split){
30907                     this.split.el.show();
30908                 }
30909                 this.fireEvent("invalidated", this);
30910                 this.fireEvent("expanded", this);
30911             },
30912             scope: this,
30913             block: true
30914         });
30915     },
30916
30917     anchors : {
30918         "west" : "left",
30919         "east" : "right",
30920         "north" : "top",
30921         "south" : "bottom"
30922     },
30923
30924     sanchors : {
30925         "west" : "l",
30926         "east" : "r",
30927         "north" : "t",
30928         "south" : "b"
30929     },
30930
30931     canchors : {
30932         "west" : "tl-tr",
30933         "east" : "tr-tl",
30934         "north" : "tl-bl",
30935         "south" : "bl-tl"
30936     },
30937
30938     getAnchor : function(){
30939         return this.anchors[this.position];
30940     },
30941
30942     getCollapseAnchor : function(){
30943         return this.canchors[this.position];
30944     },
30945
30946     getSlideAnchor : function(){
30947         return this.sanchors[this.position];
30948     },
30949
30950     getAlignAdj : function(){
30951         var cm = this.cmargins;
30952         switch(this.position){
30953             case "west":
30954                 return [0, 0];
30955             break;
30956             case "east":
30957                 return [0, 0];
30958             break;
30959             case "north":
30960                 return [0, 0];
30961             break;
30962             case "south":
30963                 return [0, 0];
30964             break;
30965         }
30966     },
30967
30968     getExpandAdj : function(){
30969         var c = this.collapsedEl, cm = this.cmargins;
30970         switch(this.position){
30971             case "west":
30972                 return [-(cm.right+c.getWidth()+cm.left), 0];
30973             break;
30974             case "east":
30975                 return [cm.right+c.getWidth()+cm.left, 0];
30976             break;
30977             case "north":
30978                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30979             break;
30980             case "south":
30981                 return [0, cm.top+cm.bottom+c.getHeight()];
30982             break;
30983         }
30984     }
30985 });/*
30986  * Based on:
30987  * Ext JS Library 1.1.1
30988  * Copyright(c) 2006-2007, Ext JS, LLC.
30989  *
30990  * Originally Released Under LGPL - original licence link has changed is not relivant.
30991  *
30992  * Fork - LGPL
30993  * <script type="text/javascript">
30994  */
30995 /*
30996  * These classes are private internal classes
30997  */
30998 Roo.CenterLayoutRegion = function(mgr, config){
30999     Roo.LayoutRegion.call(this, mgr, config, "center");
31000     this.visible = true;
31001     this.minWidth = config.minWidth || 20;
31002     this.minHeight = config.minHeight || 20;
31003 };
31004
31005 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31006     hide : function(){
31007         // center panel can't be hidden
31008     },
31009     
31010     show : function(){
31011         // center panel can't be hidden
31012     },
31013     
31014     getMinWidth: function(){
31015         return this.minWidth;
31016     },
31017     
31018     getMinHeight: function(){
31019         return this.minHeight;
31020     }
31021 });
31022
31023
31024 Roo.NorthLayoutRegion = function(mgr, config){
31025     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31026     if(this.split){
31027         this.split.placement = Roo.SplitBar.TOP;
31028         this.split.orientation = Roo.SplitBar.VERTICAL;
31029         this.split.el.addClass("x-layout-split-v");
31030     }
31031     var size = config.initialSize || config.height;
31032     if(typeof size != "undefined"){
31033         this.el.setHeight(size);
31034     }
31035 };
31036 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31037     orientation: Roo.SplitBar.VERTICAL,
31038     getBox : function(){
31039         if(this.collapsed){
31040             return this.collapsedEl.getBox();
31041         }
31042         var box = this.el.getBox();
31043         if(this.split){
31044             box.height += this.split.el.getHeight();
31045         }
31046         return box;
31047     },
31048     
31049     updateBox : function(box){
31050         if(this.split && !this.collapsed){
31051             box.height -= this.split.el.getHeight();
31052             this.split.el.setLeft(box.x);
31053             this.split.el.setTop(box.y+box.height);
31054             this.split.el.setWidth(box.width);
31055         }
31056         if(this.collapsed){
31057             this.updateBody(box.width, null);
31058         }
31059         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31060     }
31061 });
31062
31063 Roo.SouthLayoutRegion = function(mgr, config){
31064     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31065     if(this.split){
31066         this.split.placement = Roo.SplitBar.BOTTOM;
31067         this.split.orientation = Roo.SplitBar.VERTICAL;
31068         this.split.el.addClass("x-layout-split-v");
31069     }
31070     var size = config.initialSize || config.height;
31071     if(typeof size != "undefined"){
31072         this.el.setHeight(size);
31073     }
31074 };
31075 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31076     orientation: Roo.SplitBar.VERTICAL,
31077     getBox : function(){
31078         if(this.collapsed){
31079             return this.collapsedEl.getBox();
31080         }
31081         var box = this.el.getBox();
31082         if(this.split){
31083             var sh = this.split.el.getHeight();
31084             box.height += sh;
31085             box.y -= sh;
31086         }
31087         return box;
31088     },
31089     
31090     updateBox : function(box){
31091         if(this.split && !this.collapsed){
31092             var sh = this.split.el.getHeight();
31093             box.height -= sh;
31094             box.y += sh;
31095             this.split.el.setLeft(box.x);
31096             this.split.el.setTop(box.y-sh);
31097             this.split.el.setWidth(box.width);
31098         }
31099         if(this.collapsed){
31100             this.updateBody(box.width, null);
31101         }
31102         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31103     }
31104 });
31105
31106 Roo.EastLayoutRegion = function(mgr, config){
31107     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31108     if(this.split){
31109         this.split.placement = Roo.SplitBar.RIGHT;
31110         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31111         this.split.el.addClass("x-layout-split-h");
31112     }
31113     var size = config.initialSize || config.width;
31114     if(typeof size != "undefined"){
31115         this.el.setWidth(size);
31116     }
31117 };
31118 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31119     orientation: Roo.SplitBar.HORIZONTAL,
31120     getBox : function(){
31121         if(this.collapsed){
31122             return this.collapsedEl.getBox();
31123         }
31124         var box = this.el.getBox();
31125         if(this.split){
31126             var sw = this.split.el.getWidth();
31127             box.width += sw;
31128             box.x -= sw;
31129         }
31130         return box;
31131     },
31132
31133     updateBox : function(box){
31134         if(this.split && !this.collapsed){
31135             var sw = this.split.el.getWidth();
31136             box.width -= sw;
31137             this.split.el.setLeft(box.x);
31138             this.split.el.setTop(box.y);
31139             this.split.el.setHeight(box.height);
31140             box.x += sw;
31141         }
31142         if(this.collapsed){
31143             this.updateBody(null, box.height);
31144         }
31145         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31146     }
31147 });
31148
31149 Roo.WestLayoutRegion = function(mgr, config){
31150     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31151     if(this.split){
31152         this.split.placement = Roo.SplitBar.LEFT;
31153         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31154         this.split.el.addClass("x-layout-split-h");
31155     }
31156     var size = config.initialSize || config.width;
31157     if(typeof size != "undefined"){
31158         this.el.setWidth(size);
31159     }
31160 };
31161 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31162     orientation: Roo.SplitBar.HORIZONTAL,
31163     getBox : function(){
31164         if(this.collapsed){
31165             return this.collapsedEl.getBox();
31166         }
31167         var box = this.el.getBox();
31168         if(this.split){
31169             box.width += this.split.el.getWidth();
31170         }
31171         return box;
31172     },
31173     
31174     updateBox : function(box){
31175         if(this.split && !this.collapsed){
31176             var sw = this.split.el.getWidth();
31177             box.width -= sw;
31178             this.split.el.setLeft(box.x+box.width);
31179             this.split.el.setTop(box.y);
31180             this.split.el.setHeight(box.height);
31181         }
31182         if(this.collapsed){
31183             this.updateBody(null, box.height);
31184         }
31185         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31186     }
31187 });
31188 /*
31189  * Based on:
31190  * Ext JS Library 1.1.1
31191  * Copyright(c) 2006-2007, Ext JS, LLC.
31192  *
31193  * Originally Released Under LGPL - original licence link has changed is not relivant.
31194  *
31195  * Fork - LGPL
31196  * <script type="text/javascript">
31197  */
31198  
31199  
31200 /*
31201  * Private internal class for reading and applying state
31202  */
31203 Roo.LayoutStateManager = function(layout){
31204      // default empty state
31205      this.state = {
31206         north: {},
31207         south: {},
31208         east: {},
31209         west: {}       
31210     };
31211 };
31212
31213 Roo.LayoutStateManager.prototype = {
31214     init : function(layout, provider){
31215         this.provider = provider;
31216         var state = provider.get(layout.id+"-layout-state");
31217         if(state){
31218             var wasUpdating = layout.isUpdating();
31219             if(!wasUpdating){
31220                 layout.beginUpdate();
31221             }
31222             for(var key in state){
31223                 if(typeof state[key] != "function"){
31224                     var rstate = state[key];
31225                     var r = layout.getRegion(key);
31226                     if(r && rstate){
31227                         if(rstate.size){
31228                             r.resizeTo(rstate.size);
31229                         }
31230                         if(rstate.collapsed == true){
31231                             r.collapse(true);
31232                         }else{
31233                             r.expand(null, true);
31234                         }
31235                     }
31236                 }
31237             }
31238             if(!wasUpdating){
31239                 layout.endUpdate();
31240             }
31241             this.state = state; 
31242         }
31243         this.layout = layout;
31244         layout.on("regionresized", this.onRegionResized, this);
31245         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31246         layout.on("regionexpanded", this.onRegionExpanded, this);
31247     },
31248     
31249     storeState : function(){
31250         this.provider.set(this.layout.id+"-layout-state", this.state);
31251     },
31252     
31253     onRegionResized : function(region, newSize){
31254         this.state[region.getPosition()].size = newSize;
31255         this.storeState();
31256     },
31257     
31258     onRegionCollapsed : function(region){
31259         this.state[region.getPosition()].collapsed = true;
31260         this.storeState();
31261     },
31262     
31263     onRegionExpanded : function(region){
31264         this.state[region.getPosition()].collapsed = false;
31265         this.storeState();
31266     }
31267 };/*
31268  * Based on:
31269  * Ext JS Library 1.1.1
31270  * Copyright(c) 2006-2007, Ext JS, LLC.
31271  *
31272  * Originally Released Under LGPL - original licence link has changed is not relivant.
31273  *
31274  * Fork - LGPL
31275  * <script type="text/javascript">
31276  */
31277 /**
31278  * @class Roo.ContentPanel
31279  * @extends Roo.util.Observable
31280  * @children Roo.form.Form Roo.JsonView Roo.View
31281  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
31282  * A basic ContentPanel element.
31283  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31284  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31285  * @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
31286  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31287  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31288  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31289  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
31290  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31291  * @cfg {String} title          The title for this panel
31292  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31293  * @cfg {String} url            Calls {@link #setUrl} with this value
31294  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31295  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31296  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31297  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31298  * @cfg {String}    style  Extra style to add to the content panel
31299  * @cfg {Roo.menu.Menu} menu  popup menu
31300
31301  * @constructor
31302  * Create a new ContentPanel.
31303  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31304  * @param {String/Object} config A string to set only the title or a config object
31305  * @param {String} content (optional) Set the HTML content for this panel
31306  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31307  */
31308 Roo.ContentPanel = function(el, config, content){
31309     
31310      
31311     /*
31312     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31313         config = el;
31314         el = Roo.id();
31315     }
31316     if (config && config.parentLayout) { 
31317         el = config.parentLayout.el.createChild(); 
31318     }
31319     */
31320     if(el.autoCreate){ // xtype is available if this is called from factory
31321         config = el;
31322         el = Roo.id();
31323     }
31324     this.el = Roo.get(el);
31325     if(!this.el && config && config.autoCreate){
31326         if(typeof config.autoCreate == "object"){
31327             if(!config.autoCreate.id){
31328                 config.autoCreate.id = config.id||el;
31329             }
31330             this.el = Roo.DomHelper.append(document.body,
31331                         config.autoCreate, true);
31332         }else{
31333             this.el = Roo.DomHelper.append(document.body,
31334                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31335         }
31336     }
31337     
31338     
31339     this.closable = false;
31340     this.loaded = false;
31341     this.active = false;
31342     if(typeof config == "string"){
31343         this.title = config;
31344     }else{
31345         Roo.apply(this, config);
31346     }
31347     
31348     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31349         this.wrapEl = this.el.wrap();
31350         this.toolbar.container = this.el.insertSibling(false, 'before');
31351         this.toolbar = new Roo.Toolbar(this.toolbar);
31352     }
31353     
31354     // xtype created footer. - not sure if will work as we normally have to render first..
31355     if (this.footer && !this.footer.el && this.footer.xtype) {
31356         if (!this.wrapEl) {
31357             this.wrapEl = this.el.wrap();
31358         }
31359     
31360         this.footer.container = this.wrapEl.createChild();
31361          
31362         this.footer = Roo.factory(this.footer, Roo);
31363         
31364     }
31365     
31366     if(this.resizeEl){
31367         this.resizeEl = Roo.get(this.resizeEl, true);
31368     }else{
31369         this.resizeEl = this.el;
31370     }
31371     // handle view.xtype
31372     
31373  
31374     
31375     
31376     this.addEvents({
31377         /**
31378          * @event activate
31379          * Fires when this panel is activated. 
31380          * @param {Roo.ContentPanel} this
31381          */
31382         "activate" : true,
31383         /**
31384          * @event deactivate
31385          * Fires when this panel is activated. 
31386          * @param {Roo.ContentPanel} this
31387          */
31388         "deactivate" : true,
31389
31390         /**
31391          * @event resize
31392          * Fires when this panel is resized if fitToFrame is true.
31393          * @param {Roo.ContentPanel} this
31394          * @param {Number} width The width after any component adjustments
31395          * @param {Number} height The height after any component adjustments
31396          */
31397         "resize" : true,
31398         
31399          /**
31400          * @event render
31401          * Fires when this tab is created
31402          * @param {Roo.ContentPanel} this
31403          */
31404         "render" : true
31405          
31406         
31407     });
31408     
31409
31410     
31411     
31412     if(this.autoScroll){
31413         this.resizeEl.setStyle("overflow", "auto");
31414     } else {
31415         // fix randome scrolling
31416         this.el.on('scroll', function() {
31417             Roo.log('fix random scolling');
31418             this.scrollTo('top',0); 
31419         });
31420     }
31421     content = content || this.content;
31422     if(content){
31423         this.setContent(content);
31424     }
31425     if(config && config.url){
31426         this.setUrl(this.url, this.params, this.loadOnce);
31427     }
31428     
31429     
31430     
31431     Roo.ContentPanel.superclass.constructor.call(this);
31432     
31433     if (this.view && typeof(this.view.xtype) != 'undefined') {
31434         this.view.el = this.el.appendChild(document.createElement("div"));
31435         this.view = Roo.factory(this.view); 
31436         this.view.render  &&  this.view.render(false, '');  
31437     }
31438     
31439     
31440     this.fireEvent('render', this);
31441 };
31442
31443 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31444     tabTip:'',
31445     setRegion : function(region){
31446         this.region = region;
31447         if(region){
31448            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31449         }else{
31450            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31451         } 
31452     },
31453     
31454     /**
31455      * Returns the toolbar for this Panel if one was configured. 
31456      * @return {Roo.Toolbar} 
31457      */
31458     getToolbar : function(){
31459         return this.toolbar;
31460     },
31461     
31462     setActiveState : function(active){
31463         this.active = active;
31464         if(!active){
31465             this.fireEvent("deactivate", this);
31466         }else{
31467             this.fireEvent("activate", this);
31468         }
31469     },
31470     /**
31471      * Updates this panel's element
31472      * @param {String} content The new content
31473      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31474     */
31475     setContent : function(content, loadScripts){
31476         this.el.update(content, loadScripts);
31477     },
31478
31479     ignoreResize : function(w, h){
31480         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31481             return true;
31482         }else{
31483             this.lastSize = {width: w, height: h};
31484             return false;
31485         }
31486     },
31487     /**
31488      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31489      * @return {Roo.UpdateManager} The UpdateManager
31490      */
31491     getUpdateManager : function(){
31492         return this.el.getUpdateManager();
31493     },
31494      /**
31495      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31496      * @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:
31497 <pre><code>
31498 panel.load({
31499     url: "your-url.php",
31500     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31501     callback: yourFunction,
31502     scope: yourObject, //(optional scope)
31503     discardUrl: false,
31504     nocache: false,
31505     text: "Loading...",
31506     timeout: 30,
31507     scripts: false
31508 });
31509 </code></pre>
31510      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31511      * 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.
31512      * @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}
31513      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31514      * @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.
31515      * @return {Roo.ContentPanel} this
31516      */
31517     load : function(){
31518         var um = this.el.getUpdateManager();
31519         um.update.apply(um, arguments);
31520         return this;
31521     },
31522
31523
31524     /**
31525      * 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.
31526      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31527      * @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)
31528      * @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)
31529      * @return {Roo.UpdateManager} The UpdateManager
31530      */
31531     setUrl : function(url, params, loadOnce){
31532         if(this.refreshDelegate){
31533             this.removeListener("activate", this.refreshDelegate);
31534         }
31535         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31536         this.on("activate", this.refreshDelegate);
31537         return this.el.getUpdateManager();
31538     },
31539     
31540     _handleRefresh : function(url, params, loadOnce){
31541         if(!loadOnce || !this.loaded){
31542             var updater = this.el.getUpdateManager();
31543             updater.update(url, params, this._setLoaded.createDelegate(this));
31544         }
31545     },
31546     
31547     _setLoaded : function(){
31548         this.loaded = true;
31549     }, 
31550     
31551     /**
31552      * Returns this panel's id
31553      * @return {String} 
31554      */
31555     getId : function(){
31556         return this.el.id;
31557     },
31558     
31559     /** 
31560      * Returns this panel's element - used by regiosn to add.
31561      * @return {Roo.Element} 
31562      */
31563     getEl : function(){
31564         return this.wrapEl || this.el;
31565     },
31566     
31567     adjustForComponents : function(width, height)
31568     {
31569         //Roo.log('adjustForComponents ');
31570         if(this.resizeEl != this.el){
31571             width -= this.el.getFrameWidth('lr');
31572             height -= this.el.getFrameWidth('tb');
31573         }
31574         if(this.toolbar){
31575             var te = this.toolbar.getEl();
31576             height -= te.getHeight();
31577             te.setWidth(width);
31578         }
31579         if(this.footer){
31580             var te = this.footer.getEl();
31581             //Roo.log("footer:" + te.getHeight());
31582             
31583             height -= te.getHeight();
31584             te.setWidth(width);
31585         }
31586         
31587         
31588         if(this.adjustments){
31589             width += this.adjustments[0];
31590             height += this.adjustments[1];
31591         }
31592         return {"width": width, "height": height};
31593     },
31594     
31595     setSize : function(width, height){
31596         if(this.fitToFrame && !this.ignoreResize(width, height)){
31597             if(this.fitContainer && this.resizeEl != this.el){
31598                 this.el.setSize(width, height);
31599             }
31600             var size = this.adjustForComponents(width, height);
31601             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31602             this.fireEvent('resize', this, size.width, size.height);
31603         }
31604     },
31605     
31606     /**
31607      * Returns this panel's title
31608      * @return {String} 
31609      */
31610     getTitle : function(){
31611         return this.title;
31612     },
31613     
31614     /**
31615      * Set this panel's title
31616      * @param {String} title
31617      */
31618     setTitle : function(title){
31619         this.title = title;
31620         if(this.region){
31621             this.region.updatePanelTitle(this, title);
31622         }
31623     },
31624     
31625     /**
31626      * Returns true is this panel was configured to be closable
31627      * @return {Boolean} 
31628      */
31629     isClosable : function(){
31630         return this.closable;
31631     },
31632     
31633     beforeSlide : function(){
31634         this.el.clip();
31635         this.resizeEl.clip();
31636     },
31637     
31638     afterSlide : function(){
31639         this.el.unclip();
31640         this.resizeEl.unclip();
31641     },
31642     
31643     /**
31644      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31645      *   Will fail silently if the {@link #setUrl} method has not been called.
31646      *   This does not activate the panel, just updates its content.
31647      */
31648     refresh : function(){
31649         if(this.refreshDelegate){
31650            this.loaded = false;
31651            this.refreshDelegate();
31652         }
31653     },
31654     
31655     /**
31656      * Destroys this panel
31657      */
31658     destroy : function(){
31659         this.el.removeAllListeners();
31660         var tempEl = document.createElement("span");
31661         tempEl.appendChild(this.el.dom);
31662         tempEl.innerHTML = "";
31663         this.el.remove();
31664         this.el = null;
31665     },
31666     
31667     /**
31668      * form - if the content panel contains a form - this is a reference to it.
31669      * @type {Roo.form.Form}
31670      */
31671     form : false,
31672     /**
31673      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31674      *    This contains a reference to it.
31675      * @type {Roo.View}
31676      */
31677     view : false,
31678     
31679       /**
31680      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31681      * <pre><code>
31682
31683 layout.addxtype({
31684        xtype : 'Form',
31685        items: [ .... ]
31686    }
31687 );
31688
31689 </code></pre>
31690      * @param {Object} cfg Xtype definition of item to add.
31691      */
31692     
31693     addxtype : function(cfg) {
31694         // add form..
31695         if (cfg.xtype.match(/^Form$/)) {
31696             
31697             var el;
31698             //if (this.footer) {
31699             //    el = this.footer.container.insertSibling(false, 'before');
31700             //} else {
31701                 el = this.el.createChild();
31702             //}
31703
31704             this.form = new  Roo.form.Form(cfg);
31705             
31706             
31707             if ( this.form.allItems.length) {
31708                 this.form.render(el.dom);
31709             }
31710             return this.form;
31711         }
31712         // should only have one of theses..
31713         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31714             // views.. should not be just added - used named prop 'view''
31715             
31716             cfg.el = this.el.appendChild(document.createElement("div"));
31717             // factory?
31718             
31719             var ret = new Roo.factory(cfg);
31720              
31721              ret.render && ret.render(false, ''); // render blank..
31722             this.view = ret;
31723             return ret;
31724         }
31725         return false;
31726     }
31727 });
31728
31729 /**
31730  * @class Roo.GridPanel
31731  * @extends Roo.ContentPanel
31732  * @constructor
31733  * Create a new GridPanel.
31734  * @param {Roo.grid.Grid} grid The grid for this panel
31735  * @param {String/Object} config A string to set only the panel's title, or a config object
31736  */
31737 Roo.GridPanel = function(grid, config){
31738     
31739   
31740     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31741         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31742         
31743     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31744     
31745     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31746     
31747     if(this.toolbar){
31748         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31749     }
31750     // xtype created footer. - not sure if will work as we normally have to render first..
31751     if (this.footer && !this.footer.el && this.footer.xtype) {
31752         
31753         this.footer.container = this.grid.getView().getFooterPanel(true);
31754         this.footer.dataSource = this.grid.dataSource;
31755         this.footer = Roo.factory(this.footer, Roo);
31756         
31757     }
31758     
31759     grid.monitorWindowResize = false; // turn off autosizing
31760     grid.autoHeight = false;
31761     grid.autoWidth = false;
31762     this.grid = grid;
31763     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31764 };
31765
31766 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31767     getId : function(){
31768         return this.grid.id;
31769     },
31770     
31771     /**
31772      * Returns the grid for this panel
31773      * @return {Roo.grid.Grid} 
31774      */
31775     getGrid : function(){
31776         return this.grid;    
31777     },
31778     
31779     setSize : function(width, height){
31780         if(!this.ignoreResize(width, height)){
31781             var grid = this.grid;
31782             var size = this.adjustForComponents(width, height);
31783             grid.getGridEl().setSize(size.width, size.height);
31784             grid.autoSize();
31785         }
31786     },
31787     
31788     beforeSlide : function(){
31789         this.grid.getView().scroller.clip();
31790     },
31791     
31792     afterSlide : function(){
31793         this.grid.getView().scroller.unclip();
31794     },
31795     
31796     destroy : function(){
31797         this.grid.destroy();
31798         delete this.grid;
31799         Roo.GridPanel.superclass.destroy.call(this); 
31800     }
31801 });
31802
31803
31804 /**
31805  * @class Roo.NestedLayoutPanel
31806  * @extends Roo.ContentPanel
31807  * @constructor
31808  * Create a new NestedLayoutPanel.
31809  * 
31810  * 
31811  * @param {Roo.BorderLayout} layout [required] The layout for this panel
31812  * @param {String/Object} config A string to set only the title or a config object
31813  */
31814 Roo.NestedLayoutPanel = function(layout, config)
31815 {
31816     // construct with only one argument..
31817     /* FIXME - implement nicer consturctors
31818     if (layout.layout) {
31819         config = layout;
31820         layout = config.layout;
31821         delete config.layout;
31822     }
31823     if (layout.xtype && !layout.getEl) {
31824         // then layout needs constructing..
31825         layout = Roo.factory(layout, Roo);
31826     }
31827     */
31828     
31829     
31830     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31831     
31832     layout.monitorWindowResize = false; // turn off autosizing
31833     this.layout = layout;
31834     this.layout.getEl().addClass("x-layout-nested-layout");
31835     
31836     
31837     
31838     
31839 };
31840
31841 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31842
31843     setSize : function(width, height){
31844         if(!this.ignoreResize(width, height)){
31845             var size = this.adjustForComponents(width, height);
31846             var el = this.layout.getEl();
31847             el.setSize(size.width, size.height);
31848             var touch = el.dom.offsetWidth;
31849             this.layout.layout();
31850             // ie requires a double layout on the first pass
31851             if(Roo.isIE && !this.initialized){
31852                 this.initialized = true;
31853                 this.layout.layout();
31854             }
31855         }
31856     },
31857     
31858     // activate all subpanels if not currently active..
31859     
31860     setActiveState : function(active){
31861         this.active = active;
31862         if(!active){
31863             this.fireEvent("deactivate", this);
31864             return;
31865         }
31866         
31867         this.fireEvent("activate", this);
31868         // not sure if this should happen before or after..
31869         if (!this.layout) {
31870             return; // should not happen..
31871         }
31872         var reg = false;
31873         for (var r in this.layout.regions) {
31874             reg = this.layout.getRegion(r);
31875             if (reg.getActivePanel()) {
31876                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31877                 reg.setActivePanel(reg.getActivePanel());
31878                 continue;
31879             }
31880             if (!reg.panels.length) {
31881                 continue;
31882             }
31883             reg.showPanel(reg.getPanel(0));
31884         }
31885         
31886         
31887         
31888         
31889     },
31890     
31891     /**
31892      * Returns the nested BorderLayout for this panel
31893      * @return {Roo.BorderLayout} 
31894      */
31895     getLayout : function(){
31896         return this.layout;
31897     },
31898     
31899      /**
31900      * Adds a xtype elements to the layout of the nested panel
31901      * <pre><code>
31902
31903 panel.addxtype({
31904        xtype : 'ContentPanel',
31905        region: 'west',
31906        items: [ .... ]
31907    }
31908 );
31909
31910 panel.addxtype({
31911         xtype : 'NestedLayoutPanel',
31912         region: 'west',
31913         layout: {
31914            center: { },
31915            west: { }   
31916         },
31917         items : [ ... list of content panels or nested layout panels.. ]
31918    }
31919 );
31920 </code></pre>
31921      * @param {Object} cfg Xtype definition of item to add.
31922      */
31923     addxtype : function(cfg) {
31924         return this.layout.addxtype(cfg);
31925     
31926     }
31927 });
31928
31929 Roo.ScrollPanel = function(el, config, content){
31930     config = config || {};
31931     config.fitToFrame = true;
31932     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31933     
31934     this.el.dom.style.overflow = "hidden";
31935     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31936     this.el.removeClass("x-layout-inactive-content");
31937     this.el.on("mousewheel", this.onWheel, this);
31938
31939     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31940     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31941     up.unselectable(); down.unselectable();
31942     up.on("click", this.scrollUp, this);
31943     down.on("click", this.scrollDown, this);
31944     up.addClassOnOver("x-scroller-btn-over");
31945     down.addClassOnOver("x-scroller-btn-over");
31946     up.addClassOnClick("x-scroller-btn-click");
31947     down.addClassOnClick("x-scroller-btn-click");
31948     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31949
31950     this.resizeEl = this.el;
31951     this.el = wrap; this.up = up; this.down = down;
31952 };
31953
31954 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31955     increment : 100,
31956     wheelIncrement : 5,
31957     scrollUp : function(){
31958         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31959     },
31960
31961     scrollDown : function(){
31962         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31963     },
31964
31965     afterScroll : function(){
31966         var el = this.resizeEl;
31967         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31968         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31969         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31970     },
31971
31972     setSize : function(){
31973         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31974         this.afterScroll();
31975     },
31976
31977     onWheel : function(e){
31978         var d = e.getWheelDelta();
31979         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31980         this.afterScroll();
31981         e.stopEvent();
31982     },
31983
31984     setContent : function(content, loadScripts){
31985         this.resizeEl.update(content, loadScripts);
31986     }
31987
31988 });
31989
31990
31991
31992 /**
31993  * @class Roo.TreePanel
31994  * @extends Roo.ContentPanel
31995  * Treepanel component
31996  * 
31997  * @constructor
31998  * Create a new TreePanel. - defaults to fit/scoll contents.
31999  * @param {String/Object} config A string to set only the panel's title, or a config object
32000  */
32001 Roo.TreePanel = function(config){
32002     var el = config.el;
32003     var tree = config.tree;
32004     delete config.tree; 
32005     delete config.el; // hopefull!
32006     
32007     // wrapper for IE7 strict & safari scroll issue
32008     
32009     var treeEl = el.createChild();
32010     config.resizeEl = treeEl;
32011     
32012     
32013     
32014     Roo.TreePanel.superclass.constructor.call(this, el, config);
32015  
32016  
32017     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32018     //console.log(tree);
32019     this.on('activate', function()
32020     {
32021         if (this.tree.rendered) {
32022             return;
32023         }
32024         //console.log('render tree');
32025         this.tree.render();
32026     });
32027     // this should not be needed.. - it's actually the 'el' that resizes?
32028     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32029     
32030     //this.on('resize',  function (cp, w, h) {
32031     //        this.tree.innerCt.setWidth(w);
32032     //        this.tree.innerCt.setHeight(h);
32033     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32034     //});
32035
32036         
32037     
32038 };
32039
32040 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32041     fitToFrame : true,
32042     autoScroll : true,
32043     /*
32044      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
32045      */
32046     tree : false
32047
32048 });
32049
32050
32051
32052
32053
32054
32055
32056
32057
32058
32059
32060 /*
32061  * Based on:
32062  * Ext JS Library 1.1.1
32063  * Copyright(c) 2006-2007, Ext JS, LLC.
32064  *
32065  * Originally Released Under LGPL - original licence link has changed is not relivant.
32066  *
32067  * Fork - LGPL
32068  * <script type="text/javascript">
32069  */
32070  
32071
32072 /**
32073  * @class Roo.ReaderLayout
32074  * @extends Roo.BorderLayout
32075  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32076  * center region containing two nested regions (a top one for a list view and one for item preview below),
32077  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32078  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32079  * expedites the setup of the overall layout and regions for this common application style.
32080  * Example:
32081  <pre><code>
32082 var reader = new Roo.ReaderLayout();
32083 var CP = Roo.ContentPanel;  // shortcut for adding
32084
32085 reader.beginUpdate();
32086 reader.add("north", new CP("north", "North"));
32087 reader.add("west", new CP("west", {title: "West"}));
32088 reader.add("east", new CP("east", {title: "East"}));
32089
32090 reader.regions.listView.add(new CP("listView", "List"));
32091 reader.regions.preview.add(new CP("preview", "Preview"));
32092 reader.endUpdate();
32093 </code></pre>
32094 * @constructor
32095 * Create a new ReaderLayout
32096 * @param {Object} config Configuration options
32097 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32098 * document.body if omitted)
32099 */
32100 Roo.ReaderLayout = function(config, renderTo){
32101     var c = config || {size:{}};
32102     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32103         north: c.north !== false ? Roo.apply({
32104             split:false,
32105             initialSize: 32,
32106             titlebar: false
32107         }, c.north) : false,
32108         west: c.west !== false ? Roo.apply({
32109             split:true,
32110             initialSize: 200,
32111             minSize: 175,
32112             maxSize: 400,
32113             titlebar: true,
32114             collapsible: true,
32115             animate: true,
32116             margins:{left:5,right:0,bottom:5,top:5},
32117             cmargins:{left:5,right:5,bottom:5,top:5}
32118         }, c.west) : false,
32119         east: c.east !== false ? Roo.apply({
32120             split:true,
32121             initialSize: 200,
32122             minSize: 175,
32123             maxSize: 400,
32124             titlebar: true,
32125             collapsible: true,
32126             animate: true,
32127             margins:{left:0,right:5,bottom:5,top:5},
32128             cmargins:{left:5,right:5,bottom:5,top:5}
32129         }, c.east) : false,
32130         center: Roo.apply({
32131             tabPosition: 'top',
32132             autoScroll:false,
32133             closeOnTab: true,
32134             titlebar:false,
32135             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32136         }, c.center)
32137     });
32138
32139     this.el.addClass('x-reader');
32140
32141     this.beginUpdate();
32142
32143     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32144         south: c.preview !== false ? Roo.apply({
32145             split:true,
32146             initialSize: 200,
32147             minSize: 100,
32148             autoScroll:true,
32149             collapsible:true,
32150             titlebar: true,
32151             cmargins:{top:5,left:0, right:0, bottom:0}
32152         }, c.preview) : false,
32153         center: Roo.apply({
32154             autoScroll:false,
32155             titlebar:false,
32156             minHeight:200
32157         }, c.listView)
32158     });
32159     this.add('center', new Roo.NestedLayoutPanel(inner,
32160             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32161
32162     this.endUpdate();
32163
32164     this.regions.preview = inner.getRegion('south');
32165     this.regions.listView = inner.getRegion('center');
32166 };
32167
32168 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32169  * Based on:
32170  * Ext JS Library 1.1.1
32171  * Copyright(c) 2006-2007, Ext JS, LLC.
32172  *
32173  * Originally Released Under LGPL - original licence link has changed is not relivant.
32174  *
32175  * Fork - LGPL
32176  * <script type="text/javascript">
32177  */
32178  
32179 /**
32180  * @class Roo.grid.Grid
32181  * @extends Roo.util.Observable
32182  * This class represents the primary interface of a component based grid control.
32183  * <br><br>Usage:<pre><code>
32184  var grid = new Roo.grid.Grid("my-container-id", {
32185      ds: myDataStore,
32186      cm: myColModel,
32187      selModel: mySelectionModel,
32188      autoSizeColumns: true,
32189      monitorWindowResize: false,
32190      trackMouseOver: true
32191  });
32192  // set any options
32193  grid.render();
32194  * </code></pre>
32195  * <b>Common Problems:</b><br/>
32196  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32197  * element will correct this<br/>
32198  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32199  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32200  * are unpredictable.<br/>
32201  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32202  * grid to calculate dimensions/offsets.<br/>
32203   * @constructor
32204  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32205  * The container MUST have some type of size defined for the grid to fill. The container will be
32206  * automatically set to position relative if it isn't already.
32207  * @param {Object} config A config object that sets properties on this grid.
32208  */
32209 Roo.grid.Grid = function(container, config){
32210         // initialize the container
32211         this.container = Roo.get(container);
32212         this.container.update("");
32213         this.container.setStyle("overflow", "hidden");
32214     this.container.addClass('x-grid-container');
32215
32216     this.id = this.container.id;
32217
32218     Roo.apply(this, config);
32219     // check and correct shorthanded configs
32220     if(this.ds){
32221         this.dataSource = this.ds;
32222         delete this.ds;
32223     }
32224     if(this.cm){
32225         this.colModel = this.cm;
32226         delete this.cm;
32227     }
32228     if(this.sm){
32229         this.selModel = this.sm;
32230         delete this.sm;
32231     }
32232
32233     if (this.selModel) {
32234         this.selModel = Roo.factory(this.selModel, Roo.grid);
32235         this.sm = this.selModel;
32236         this.sm.xmodule = this.xmodule || false;
32237     }
32238     if (typeof(this.colModel.config) == 'undefined') {
32239         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32240         this.cm = this.colModel;
32241         this.cm.xmodule = this.xmodule || false;
32242     }
32243     if (this.dataSource) {
32244         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32245         this.ds = this.dataSource;
32246         this.ds.xmodule = this.xmodule || false;
32247          
32248     }
32249     
32250     
32251     
32252     if(this.width){
32253         this.container.setWidth(this.width);
32254     }
32255
32256     if(this.height){
32257         this.container.setHeight(this.height);
32258     }
32259     /** @private */
32260         this.addEvents({
32261         // raw events
32262         /**
32263          * @event click
32264          * The raw click event for the entire grid.
32265          * @param {Roo.EventObject} e
32266          */
32267         "click" : true,
32268         /**
32269          * @event dblclick
32270          * The raw dblclick event for the entire grid.
32271          * @param {Roo.EventObject} e
32272          */
32273         "dblclick" : true,
32274         /**
32275          * @event contextmenu
32276          * The raw contextmenu event for the entire grid.
32277          * @param {Roo.EventObject} e
32278          */
32279         "contextmenu" : true,
32280         /**
32281          * @event mousedown
32282          * The raw mousedown event for the entire grid.
32283          * @param {Roo.EventObject} e
32284          */
32285         "mousedown" : true,
32286         /**
32287          * @event mouseup
32288          * The raw mouseup event for the entire grid.
32289          * @param {Roo.EventObject} e
32290          */
32291         "mouseup" : true,
32292         /**
32293          * @event mouseover
32294          * The raw mouseover event for the entire grid.
32295          * @param {Roo.EventObject} e
32296          */
32297         "mouseover" : true,
32298         /**
32299          * @event mouseout
32300          * The raw mouseout event for the entire grid.
32301          * @param {Roo.EventObject} e
32302          */
32303         "mouseout" : true,
32304         /**
32305          * @event keypress
32306          * The raw keypress event for the entire grid.
32307          * @param {Roo.EventObject} e
32308          */
32309         "keypress" : true,
32310         /**
32311          * @event keydown
32312          * The raw keydown event for the entire grid.
32313          * @param {Roo.EventObject} e
32314          */
32315         "keydown" : true,
32316
32317         // custom events
32318
32319         /**
32320          * @event cellclick
32321          * Fires when a cell is clicked
32322          * @param {Grid} this
32323          * @param {Number} rowIndex
32324          * @param {Number} columnIndex
32325          * @param {Roo.EventObject} e
32326          */
32327         "cellclick" : true,
32328         /**
32329          * @event celldblclick
32330          * Fires when a cell is double clicked
32331          * @param {Grid} this
32332          * @param {Number} rowIndex
32333          * @param {Number} columnIndex
32334          * @param {Roo.EventObject} e
32335          */
32336         "celldblclick" : true,
32337         /**
32338          * @event rowclick
32339          * Fires when a row is clicked
32340          * @param {Grid} this
32341          * @param {Number} rowIndex
32342          * @param {Roo.EventObject} e
32343          */
32344         "rowclick" : true,
32345         /**
32346          * @event rowdblclick
32347          * Fires when a row is double clicked
32348          * @param {Grid} this
32349          * @param {Number} rowIndex
32350          * @param {Roo.EventObject} e
32351          */
32352         "rowdblclick" : true,
32353         /**
32354          * @event headerclick
32355          * Fires when a header is clicked
32356          * @param {Grid} this
32357          * @param {Number} columnIndex
32358          * @param {Roo.EventObject} e
32359          */
32360         "headerclick" : true,
32361         /**
32362          * @event headerdblclick
32363          * Fires when a header cell is double clicked
32364          * @param {Grid} this
32365          * @param {Number} columnIndex
32366          * @param {Roo.EventObject} e
32367          */
32368         "headerdblclick" : true,
32369         /**
32370          * @event rowcontextmenu
32371          * Fires when a row is right clicked
32372          * @param {Grid} this
32373          * @param {Number} rowIndex
32374          * @param {Roo.EventObject} e
32375          */
32376         "rowcontextmenu" : true,
32377         /**
32378          * @event cellcontextmenu
32379          * Fires when a cell is right clicked
32380          * @param {Grid} this
32381          * @param {Number} rowIndex
32382          * @param {Number} cellIndex
32383          * @param {Roo.EventObject} e
32384          */
32385          "cellcontextmenu" : true,
32386         /**
32387          * @event headercontextmenu
32388          * Fires when a header is right clicked
32389          * @param {Grid} this
32390          * @param {Number} columnIndex
32391          * @param {Roo.EventObject} e
32392          */
32393         "headercontextmenu" : true,
32394         /**
32395          * @event bodyscroll
32396          * Fires when the body element is scrolled
32397          * @param {Number} scrollLeft
32398          * @param {Number} scrollTop
32399          */
32400         "bodyscroll" : true,
32401         /**
32402          * @event columnresize
32403          * Fires when the user resizes a column
32404          * @param {Number} columnIndex
32405          * @param {Number} newSize
32406          */
32407         "columnresize" : true,
32408         /**
32409          * @event columnmove
32410          * Fires when the user moves a column
32411          * @param {Number} oldIndex
32412          * @param {Number} newIndex
32413          */
32414         "columnmove" : true,
32415         /**
32416          * @event startdrag
32417          * Fires when row(s) start being dragged
32418          * @param {Grid} this
32419          * @param {Roo.GridDD} dd The drag drop object
32420          * @param {event} e The raw browser event
32421          */
32422         "startdrag" : true,
32423         /**
32424          * @event enddrag
32425          * Fires when a drag operation is complete
32426          * @param {Grid} this
32427          * @param {Roo.GridDD} dd The drag drop object
32428          * @param {event} e The raw browser event
32429          */
32430         "enddrag" : true,
32431         /**
32432          * @event dragdrop
32433          * Fires when dragged row(s) are dropped on a valid DD target
32434          * @param {Grid} this
32435          * @param {Roo.GridDD} dd The drag drop object
32436          * @param {String} targetId The target drag drop object
32437          * @param {event} e The raw browser event
32438          */
32439         "dragdrop" : true,
32440         /**
32441          * @event dragover
32442          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32443          * @param {Grid} this
32444          * @param {Roo.GridDD} dd The drag drop object
32445          * @param {String} targetId The target drag drop object
32446          * @param {event} e The raw browser event
32447          */
32448         "dragover" : true,
32449         /**
32450          * @event dragenter
32451          *  Fires when the dragged row(s) first cross another DD target while being dragged
32452          * @param {Grid} this
32453          * @param {Roo.GridDD} dd The drag drop object
32454          * @param {String} targetId The target drag drop object
32455          * @param {event} e The raw browser event
32456          */
32457         "dragenter" : true,
32458         /**
32459          * @event dragout
32460          * Fires when the dragged row(s) leave another DD target while being dragged
32461          * @param {Grid} this
32462          * @param {Roo.GridDD} dd The drag drop object
32463          * @param {String} targetId The target drag drop object
32464          * @param {event} e The raw browser event
32465          */
32466         "dragout" : true,
32467         /**
32468          * @event rowclass
32469          * Fires when a row is rendered, so you can change add a style to it.
32470          * @param {GridView} gridview   The grid view
32471          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32472          */
32473         'rowclass' : true,
32474
32475         /**
32476          * @event render
32477          * Fires when the grid is rendered
32478          * @param {Grid} grid
32479          */
32480         'render' : true
32481     });
32482
32483     Roo.grid.Grid.superclass.constructor.call(this);
32484 };
32485 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32486     
32487     /**
32488          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
32489          */
32490         /**
32491          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
32492          */
32493         /**
32494          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
32495          */
32496         /**
32497          * @cfg {Roo.grid.Store} ds The data store for the grid
32498          */
32499         /**
32500          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
32501          */
32502         /**
32503      * @cfg {String} ddGroup - drag drop group.
32504      */
32505       /**
32506      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32507      */
32508
32509     /**
32510      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32511      */
32512     minColumnWidth : 25,
32513
32514     /**
32515      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32516      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32517      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32518      */
32519     autoSizeColumns : false,
32520
32521     /**
32522      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32523      */
32524     autoSizeHeaders : true,
32525
32526     /**
32527      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32528      */
32529     monitorWindowResize : true,
32530
32531     /**
32532      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32533      * rows measured to get a columns size. Default is 0 (all rows).
32534      */
32535     maxRowsToMeasure : 0,
32536
32537     /**
32538      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32539      */
32540     trackMouseOver : true,
32541
32542     /**
32543     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32544     */
32545       /**
32546     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32547     */
32548     
32549     /**
32550     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32551     */
32552     enableDragDrop : false,
32553     
32554     /**
32555     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32556     */
32557     enableColumnMove : true,
32558     
32559     /**
32560     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32561     */
32562     enableColumnHide : true,
32563     
32564     /**
32565     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32566     */
32567     enableRowHeightSync : false,
32568     
32569     /**
32570     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32571     */
32572     stripeRows : true,
32573     
32574     /**
32575     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32576     */
32577     autoHeight : false,
32578
32579     /**
32580      * @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.
32581      */
32582     autoExpandColumn : false,
32583
32584     /**
32585     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32586     * Default is 50.
32587     */
32588     autoExpandMin : 50,
32589
32590     /**
32591     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32592     */
32593     autoExpandMax : 1000,
32594
32595     /**
32596     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32597     */
32598     view : null,
32599
32600     /**
32601     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32602     */
32603     loadMask : false,
32604     /**
32605     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32606     */
32607     dropTarget: false,
32608     
32609    
32610     
32611     // private
32612     rendered : false,
32613
32614     /**
32615     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32616     * of a fixed width. Default is false.
32617     */
32618     /**
32619     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32620     */
32621     
32622     
32623     /**
32624     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32625     * %0 is replaced with the number of selected rows.
32626     */
32627     ddText : "{0} selected row{1}",
32628     
32629     
32630     /**
32631      * Called once after all setup has been completed and the grid is ready to be rendered.
32632      * @return {Roo.grid.Grid} this
32633      */
32634     render : function()
32635     {
32636         var c = this.container;
32637         // try to detect autoHeight/width mode
32638         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32639             this.autoHeight = true;
32640         }
32641         var view = this.getView();
32642         view.init(this);
32643
32644         c.on("click", this.onClick, this);
32645         c.on("dblclick", this.onDblClick, this);
32646         c.on("contextmenu", this.onContextMenu, this);
32647         c.on("keydown", this.onKeyDown, this);
32648         if (Roo.isTouch) {
32649             c.on("touchstart", this.onTouchStart, this);
32650         }
32651
32652         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32653
32654         this.getSelectionModel().init(this);
32655
32656         view.render();
32657
32658         if(this.loadMask){
32659             this.loadMask = new Roo.LoadMask(this.container,
32660                     Roo.apply({store:this.dataSource}, this.loadMask));
32661         }
32662         
32663         
32664         if (this.toolbar && this.toolbar.xtype) {
32665             this.toolbar.container = this.getView().getHeaderPanel(true);
32666             this.toolbar = new Roo.Toolbar(this.toolbar);
32667         }
32668         if (this.footer && this.footer.xtype) {
32669             this.footer.dataSource = this.getDataSource();
32670             this.footer.container = this.getView().getFooterPanel(true);
32671             this.footer = Roo.factory(this.footer, Roo);
32672         }
32673         if (this.dropTarget && this.dropTarget.xtype) {
32674             delete this.dropTarget.xtype;
32675             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32676         }
32677         
32678         
32679         this.rendered = true;
32680         this.fireEvent('render', this);
32681         return this;
32682     },
32683
32684     /**
32685      * Reconfigures the grid to use a different Store and Column Model.
32686      * The View will be bound to the new objects and refreshed.
32687      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32688      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32689      */
32690     reconfigure : function(dataSource, colModel){
32691         if(this.loadMask){
32692             this.loadMask.destroy();
32693             this.loadMask = new Roo.LoadMask(this.container,
32694                     Roo.apply({store:dataSource}, this.loadMask));
32695         }
32696         this.view.bind(dataSource, colModel);
32697         this.dataSource = dataSource;
32698         this.colModel = colModel;
32699         this.view.refresh(true);
32700     },
32701     /**
32702      * addColumns
32703      * Add's a column, default at the end..
32704      
32705      * @param {int} position to add (default end)
32706      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32707      */
32708     addColumns : function(pos, ar)
32709     {
32710         
32711         for (var i =0;i< ar.length;i++) {
32712             var cfg = ar[i];
32713             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32714             this.cm.lookup[cfg.id] = cfg;
32715         }
32716         
32717         
32718         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32719             pos = this.cm.config.length; //this.cm.config.push(cfg);
32720         } 
32721         pos = Math.max(0,pos);
32722         ar.unshift(0);
32723         ar.unshift(pos);
32724         this.cm.config.splice.apply(this.cm.config, ar);
32725         
32726         
32727         
32728         this.view.generateRules(this.cm);
32729         this.view.refresh(true);
32730         
32731     },
32732     
32733     
32734     
32735     
32736     // private
32737     onKeyDown : function(e){
32738         this.fireEvent("keydown", e);
32739     },
32740
32741     /**
32742      * Destroy this grid.
32743      * @param {Boolean} removeEl True to remove the element
32744      */
32745     destroy : function(removeEl, keepListeners){
32746         if(this.loadMask){
32747             this.loadMask.destroy();
32748         }
32749         var c = this.container;
32750         c.removeAllListeners();
32751         this.view.destroy();
32752         this.colModel.purgeListeners();
32753         if(!keepListeners){
32754             this.purgeListeners();
32755         }
32756         c.update("");
32757         if(removeEl === true){
32758             c.remove();
32759         }
32760     },
32761
32762     // private
32763     processEvent : function(name, e){
32764         // does this fire select???
32765         //Roo.log('grid:processEvent '  + name);
32766         
32767         if (name != 'touchstart' ) {
32768             this.fireEvent(name, e);    
32769         }
32770         
32771         var t = e.getTarget();
32772         var v = this.view;
32773         var header = v.findHeaderIndex(t);
32774         if(header !== false){
32775             var ename = name == 'touchstart' ? 'click' : name;
32776              
32777             this.fireEvent("header" + ename, this, header, e);
32778         }else{
32779             var row = v.findRowIndex(t);
32780             var cell = v.findCellIndex(t);
32781             if (name == 'touchstart') {
32782                 // first touch is always a click.
32783                 // hopefull this happens after selection is updated.?
32784                 name = false;
32785                 
32786                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32787                     var cs = this.selModel.getSelectedCell();
32788                     if (row == cs[0] && cell == cs[1]){
32789                         name = 'dblclick';
32790                     }
32791                 }
32792                 if (typeof(this.selModel.getSelections) != 'undefined') {
32793                     var cs = this.selModel.getSelections();
32794                     var ds = this.dataSource;
32795                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32796                         name = 'dblclick';
32797                     }
32798                 }
32799                 if (!name) {
32800                     return;
32801                 }
32802             }
32803             
32804             
32805             if(row !== false){
32806                 this.fireEvent("row" + name, this, row, e);
32807                 if(cell !== false){
32808                     this.fireEvent("cell" + name, this, row, cell, e);
32809                 }
32810             }
32811         }
32812     },
32813
32814     // private
32815     onClick : function(e){
32816         this.processEvent("click", e);
32817     },
32818    // private
32819     onTouchStart : function(e){
32820         this.processEvent("touchstart", e);
32821     },
32822
32823     // private
32824     onContextMenu : function(e, t){
32825         this.processEvent("contextmenu", e);
32826     },
32827
32828     // private
32829     onDblClick : function(e){
32830         this.processEvent("dblclick", e);
32831     },
32832
32833     // private
32834     walkCells : function(row, col, step, fn, scope){
32835         var cm = this.colModel, clen = cm.getColumnCount();
32836         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32837         if(step < 0){
32838             if(col < 0){
32839                 row--;
32840                 first = false;
32841             }
32842             while(row >= 0){
32843                 if(!first){
32844                     col = clen-1;
32845                 }
32846                 first = false;
32847                 while(col >= 0){
32848                     if(fn.call(scope || this, row, col, cm) === true){
32849                         return [row, col];
32850                     }
32851                     col--;
32852                 }
32853                 row--;
32854             }
32855         } else {
32856             if(col >= clen){
32857                 row++;
32858                 first = false;
32859             }
32860             while(row < rlen){
32861                 if(!first){
32862                     col = 0;
32863                 }
32864                 first = false;
32865                 while(col < clen){
32866                     if(fn.call(scope || this, row, col, cm) === true){
32867                         return [row, col];
32868                     }
32869                     col++;
32870                 }
32871                 row++;
32872             }
32873         }
32874         return null;
32875     },
32876
32877     // private
32878     getSelections : function(){
32879         return this.selModel.getSelections();
32880     },
32881
32882     /**
32883      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32884      * but if manual update is required this method will initiate it.
32885      */
32886     autoSize : function(){
32887         if(this.rendered){
32888             this.view.layout();
32889             if(this.view.adjustForScroll){
32890                 this.view.adjustForScroll();
32891             }
32892         }
32893     },
32894
32895     /**
32896      * Returns the grid's underlying element.
32897      * @return {Element} The element
32898      */
32899     getGridEl : function(){
32900         return this.container;
32901     },
32902
32903     // private for compatibility, overridden by editor grid
32904     stopEditing : function(){},
32905
32906     /**
32907      * Returns the grid's SelectionModel.
32908      * @return {SelectionModel}
32909      */
32910     getSelectionModel : function(){
32911         if(!this.selModel){
32912             this.selModel = new Roo.grid.RowSelectionModel();
32913         }
32914         return this.selModel;
32915     },
32916
32917     /**
32918      * Returns the grid's DataSource.
32919      * @return {DataSource}
32920      */
32921     getDataSource : function(){
32922         return this.dataSource;
32923     },
32924
32925     /**
32926      * Returns the grid's ColumnModel.
32927      * @return {ColumnModel}
32928      */
32929     getColumnModel : function(){
32930         return this.colModel;
32931     },
32932
32933     /**
32934      * Returns the grid's GridView object.
32935      * @return {GridView}
32936      */
32937     getView : function(){
32938         if(!this.view){
32939             this.view = new Roo.grid.GridView(this.viewConfig);
32940             this.relayEvents(this.view, [
32941                 "beforerowremoved", "beforerowsinserted",
32942                 "beforerefresh", "rowremoved",
32943                 "rowsinserted", "rowupdated" ,"refresh"
32944             ]);
32945         }
32946         return this.view;
32947     },
32948     /**
32949      * Called to get grid's drag proxy text, by default returns this.ddText.
32950      * Override this to put something different in the dragged text.
32951      * @return {String}
32952      */
32953     getDragDropText : function(){
32954         var count = this.selModel.getCount();
32955         return String.format(this.ddText, count, count == 1 ? '' : 's');
32956     }
32957 });
32958 /*
32959  * Based on:
32960  * Ext JS Library 1.1.1
32961  * Copyright(c) 2006-2007, Ext JS, LLC.
32962  *
32963  * Originally Released Under LGPL - original licence link has changed is not relivant.
32964  *
32965  * Fork - LGPL
32966  * <script type="text/javascript">
32967  */
32968  /**
32969  * @class Roo.grid.AbstractGridView
32970  * @extends Roo.util.Observable
32971  * @abstract
32972  * Abstract base class for grid Views
32973  * @constructor
32974  */
32975 Roo.grid.AbstractGridView = function(){
32976         this.grid = null;
32977         
32978         this.events = {
32979             "beforerowremoved" : true,
32980             "beforerowsinserted" : true,
32981             "beforerefresh" : true,
32982             "rowremoved" : true,
32983             "rowsinserted" : true,
32984             "rowupdated" : true,
32985             "refresh" : true
32986         };
32987     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32988 };
32989
32990 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32991     rowClass : "x-grid-row",
32992     cellClass : "x-grid-cell",
32993     tdClass : "x-grid-td",
32994     hdClass : "x-grid-hd",
32995     splitClass : "x-grid-hd-split",
32996     
32997     init: function(grid){
32998         this.grid = grid;
32999                 var cid = this.grid.getGridEl().id;
33000         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33001         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33002         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33003         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33004         },
33005         
33006     getColumnRenderers : function(){
33007         var renderers = [];
33008         var cm = this.grid.colModel;
33009         var colCount = cm.getColumnCount();
33010         for(var i = 0; i < colCount; i++){
33011             renderers[i] = cm.getRenderer(i);
33012         }
33013         return renderers;
33014     },
33015     
33016     getColumnIds : function(){
33017         var ids = [];
33018         var cm = this.grid.colModel;
33019         var colCount = cm.getColumnCount();
33020         for(var i = 0; i < colCount; i++){
33021             ids[i] = cm.getColumnId(i);
33022         }
33023         return ids;
33024     },
33025     
33026     getDataIndexes : function(){
33027         if(!this.indexMap){
33028             this.indexMap = this.buildIndexMap();
33029         }
33030         return this.indexMap.colToData;
33031     },
33032     
33033     getColumnIndexByDataIndex : function(dataIndex){
33034         if(!this.indexMap){
33035             this.indexMap = this.buildIndexMap();
33036         }
33037         return this.indexMap.dataToCol[dataIndex];
33038     },
33039     
33040     /**
33041      * Set a css style for a column dynamically. 
33042      * @param {Number} colIndex The index of the column
33043      * @param {String} name The css property name
33044      * @param {String} value The css value
33045      */
33046     setCSSStyle : function(colIndex, name, value){
33047         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33048         Roo.util.CSS.updateRule(selector, name, value);
33049     },
33050     
33051     generateRules : function(cm){
33052         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33053         Roo.util.CSS.removeStyleSheet(rulesId);
33054         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33055             var cid = cm.getColumnId(i);
33056             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33057                          this.tdSelector, cid, " {\n}\n",
33058                          this.hdSelector, cid, " {\n}\n",
33059                          this.splitSelector, cid, " {\n}\n");
33060         }
33061         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33062     }
33063 });/*
33064  * Based on:
33065  * Ext JS Library 1.1.1
33066  * Copyright(c) 2006-2007, Ext JS, LLC.
33067  *
33068  * Originally Released Under LGPL - original licence link has changed is not relivant.
33069  *
33070  * Fork - LGPL
33071  * <script type="text/javascript">
33072  */
33073
33074 // private
33075 // This is a support class used internally by the Grid components
33076 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33077     this.grid = grid;
33078     this.view = grid.getView();
33079     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33080     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33081     if(hd2){
33082         this.setHandleElId(Roo.id(hd));
33083         this.setOuterHandleElId(Roo.id(hd2));
33084     }
33085     this.scroll = false;
33086 };
33087 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33088     maxDragWidth: 120,
33089     getDragData : function(e){
33090         var t = Roo.lib.Event.getTarget(e);
33091         var h = this.view.findHeaderCell(t);
33092         if(h){
33093             return {ddel: h.firstChild, header:h};
33094         }
33095         return false;
33096     },
33097
33098     onInitDrag : function(e){
33099         this.view.headersDisabled = true;
33100         var clone = this.dragData.ddel.cloneNode(true);
33101         clone.id = Roo.id();
33102         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33103         this.proxy.update(clone);
33104         return true;
33105     },
33106
33107     afterValidDrop : function(){
33108         var v = this.view;
33109         setTimeout(function(){
33110             v.headersDisabled = false;
33111         }, 50);
33112     },
33113
33114     afterInvalidDrop : function(){
33115         var v = this.view;
33116         setTimeout(function(){
33117             v.headersDisabled = false;
33118         }, 50);
33119     }
33120 });
33121 /*
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 // private
33132 // This is a support class used internally by the Grid components
33133 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33134     this.grid = grid;
33135     this.view = grid.getView();
33136     // split the proxies so they don't interfere with mouse events
33137     this.proxyTop = Roo.DomHelper.append(document.body, {
33138         cls:"col-move-top", html:"&#160;"
33139     }, true);
33140     this.proxyBottom = Roo.DomHelper.append(document.body, {
33141         cls:"col-move-bottom", html:"&#160;"
33142     }, true);
33143     this.proxyTop.hide = this.proxyBottom.hide = function(){
33144         this.setLeftTop(-100,-100);
33145         this.setStyle("visibility", "hidden");
33146     };
33147     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33148     // temporarily disabled
33149     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33150     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33151 };
33152 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33153     proxyOffsets : [-4, -9],
33154     fly: Roo.Element.fly,
33155
33156     getTargetFromEvent : function(e){
33157         var t = Roo.lib.Event.getTarget(e);
33158         var cindex = this.view.findCellIndex(t);
33159         if(cindex !== false){
33160             return this.view.getHeaderCell(cindex);
33161         }
33162         return null;
33163     },
33164
33165     nextVisible : function(h){
33166         var v = this.view, cm = this.grid.colModel;
33167         h = h.nextSibling;
33168         while(h){
33169             if(!cm.isHidden(v.getCellIndex(h))){
33170                 return h;
33171             }
33172             h = h.nextSibling;
33173         }
33174         return null;
33175     },
33176
33177     prevVisible : function(h){
33178         var v = this.view, cm = this.grid.colModel;
33179         h = h.prevSibling;
33180         while(h){
33181             if(!cm.isHidden(v.getCellIndex(h))){
33182                 return h;
33183             }
33184             h = h.prevSibling;
33185         }
33186         return null;
33187     },
33188
33189     positionIndicator : function(h, n, e){
33190         var x = Roo.lib.Event.getPageX(e);
33191         var r = Roo.lib.Dom.getRegion(n.firstChild);
33192         var px, pt, py = r.top + this.proxyOffsets[1];
33193         if((r.right - x) <= (r.right-r.left)/2){
33194             px = r.right+this.view.borderWidth;
33195             pt = "after";
33196         }else{
33197             px = r.left;
33198             pt = "before";
33199         }
33200         var oldIndex = this.view.getCellIndex(h);
33201         var newIndex = this.view.getCellIndex(n);
33202
33203         if(this.grid.colModel.isFixed(newIndex)){
33204             return false;
33205         }
33206
33207         var locked = this.grid.colModel.isLocked(newIndex);
33208
33209         if(pt == "after"){
33210             newIndex++;
33211         }
33212         if(oldIndex < newIndex){
33213             newIndex--;
33214         }
33215         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33216             return false;
33217         }
33218         px +=  this.proxyOffsets[0];
33219         this.proxyTop.setLeftTop(px, py);
33220         this.proxyTop.show();
33221         if(!this.bottomOffset){
33222             this.bottomOffset = this.view.mainHd.getHeight();
33223         }
33224         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33225         this.proxyBottom.show();
33226         return pt;
33227     },
33228
33229     onNodeEnter : function(n, dd, e, data){
33230         if(data.header != n){
33231             this.positionIndicator(data.header, n, e);
33232         }
33233     },
33234
33235     onNodeOver : function(n, dd, e, data){
33236         var result = false;
33237         if(data.header != n){
33238             result = this.positionIndicator(data.header, n, e);
33239         }
33240         if(!result){
33241             this.proxyTop.hide();
33242             this.proxyBottom.hide();
33243         }
33244         return result ? this.dropAllowed : this.dropNotAllowed;
33245     },
33246
33247     onNodeOut : function(n, dd, e, data){
33248         this.proxyTop.hide();
33249         this.proxyBottom.hide();
33250     },
33251
33252     onNodeDrop : function(n, dd, e, data){
33253         var h = data.header;
33254         if(h != n){
33255             var cm = this.grid.colModel;
33256             var x = Roo.lib.Event.getPageX(e);
33257             var r = Roo.lib.Dom.getRegion(n.firstChild);
33258             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33259             var oldIndex = this.view.getCellIndex(h);
33260             var newIndex = this.view.getCellIndex(n);
33261             var locked = cm.isLocked(newIndex);
33262             if(pt == "after"){
33263                 newIndex++;
33264             }
33265             if(oldIndex < newIndex){
33266                 newIndex--;
33267             }
33268             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33269                 return false;
33270             }
33271             cm.setLocked(oldIndex, locked, true);
33272             cm.moveColumn(oldIndex, newIndex);
33273             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33274             return true;
33275         }
33276         return false;
33277     }
33278 });
33279 /*
33280  * Based on:
33281  * Ext JS Library 1.1.1
33282  * Copyright(c) 2006-2007, Ext JS, LLC.
33283  *
33284  * Originally Released Under LGPL - original licence link has changed is not relivant.
33285  *
33286  * Fork - LGPL
33287  * <script type="text/javascript">
33288  */
33289   
33290 /**
33291  * @class Roo.grid.GridView
33292  * @extends Roo.util.Observable
33293  *
33294  * @constructor
33295  * @param {Object} config
33296  */
33297 Roo.grid.GridView = function(config){
33298     Roo.grid.GridView.superclass.constructor.call(this);
33299     this.el = null;
33300
33301     Roo.apply(this, config);
33302 };
33303
33304 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33305
33306     unselectable :  'unselectable="on"',
33307     unselectableCls :  'x-unselectable',
33308     
33309     
33310     rowClass : "x-grid-row",
33311
33312     cellClass : "x-grid-col",
33313
33314     tdClass : "x-grid-td",
33315
33316     hdClass : "x-grid-hd",
33317
33318     splitClass : "x-grid-split",
33319
33320     sortClasses : ["sort-asc", "sort-desc"],
33321
33322     enableMoveAnim : false,
33323
33324     hlColor: "C3DAF9",
33325
33326     dh : Roo.DomHelper,
33327
33328     fly : Roo.Element.fly,
33329
33330     css : Roo.util.CSS,
33331
33332     borderWidth: 1,
33333
33334     splitOffset: 3,
33335
33336     scrollIncrement : 22,
33337
33338     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33339
33340     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33341
33342     bind : function(ds, cm){
33343         if(this.ds){
33344             this.ds.un("load", this.onLoad, this);
33345             this.ds.un("datachanged", this.onDataChange, this);
33346             this.ds.un("add", this.onAdd, this);
33347             this.ds.un("remove", this.onRemove, this);
33348             this.ds.un("update", this.onUpdate, this);
33349             this.ds.un("clear", this.onClear, this);
33350         }
33351         if(ds){
33352             ds.on("load", this.onLoad, this);
33353             ds.on("datachanged", this.onDataChange, this);
33354             ds.on("add", this.onAdd, this);
33355             ds.on("remove", this.onRemove, this);
33356             ds.on("update", this.onUpdate, this);
33357             ds.on("clear", this.onClear, this);
33358         }
33359         this.ds = ds;
33360
33361         if(this.cm){
33362             this.cm.un("widthchange", this.onColWidthChange, this);
33363             this.cm.un("headerchange", this.onHeaderChange, this);
33364             this.cm.un("hiddenchange", this.onHiddenChange, this);
33365             this.cm.un("columnmoved", this.onColumnMove, this);
33366             this.cm.un("columnlockchange", this.onColumnLock, this);
33367         }
33368         if(cm){
33369             this.generateRules(cm);
33370             cm.on("widthchange", this.onColWidthChange, this);
33371             cm.on("headerchange", this.onHeaderChange, this);
33372             cm.on("hiddenchange", this.onHiddenChange, this);
33373             cm.on("columnmoved", this.onColumnMove, this);
33374             cm.on("columnlockchange", this.onColumnLock, this);
33375         }
33376         this.cm = cm;
33377     },
33378
33379     init: function(grid){
33380         Roo.grid.GridView.superclass.init.call(this, grid);
33381
33382         this.bind(grid.dataSource, grid.colModel);
33383
33384         grid.on("headerclick", this.handleHeaderClick, this);
33385
33386         if(grid.trackMouseOver){
33387             grid.on("mouseover", this.onRowOver, this);
33388             grid.on("mouseout", this.onRowOut, this);
33389         }
33390         grid.cancelTextSelection = function(){};
33391         this.gridId = grid.id;
33392
33393         var tpls = this.templates || {};
33394
33395         if(!tpls.master){
33396             tpls.master = new Roo.Template(
33397                '<div class="x-grid" hidefocus="true">',
33398                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33399                   '<div class="x-grid-topbar"></div>',
33400                   '<div class="x-grid-scroller"><div></div></div>',
33401                   '<div class="x-grid-locked">',
33402                       '<div class="x-grid-header">{lockedHeader}</div>',
33403                       '<div class="x-grid-body">{lockedBody}</div>',
33404                   "</div>",
33405                   '<div class="x-grid-viewport">',
33406                       '<div class="x-grid-header">{header}</div>',
33407                       '<div class="x-grid-body">{body}</div>',
33408                   "</div>",
33409                   '<div class="x-grid-bottombar"></div>',
33410                  
33411                   '<div class="x-grid-resize-proxy">&#160;</div>',
33412                "</div>"
33413             );
33414             tpls.master.disableformats = true;
33415         }
33416
33417         if(!tpls.header){
33418             tpls.header = new Roo.Template(
33419                '<table border="0" cellspacing="0" cellpadding="0">',
33420                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33421                "</table>{splits}"
33422             );
33423             tpls.header.disableformats = true;
33424         }
33425         tpls.header.compile();
33426
33427         if(!tpls.hcell){
33428             tpls.hcell = new Roo.Template(
33429                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33430                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33431                 "</div></td>"
33432              );
33433              tpls.hcell.disableFormats = true;
33434         }
33435         tpls.hcell.compile();
33436
33437         if(!tpls.hsplit){
33438             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33439                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33440             tpls.hsplit.disableFormats = true;
33441         }
33442         tpls.hsplit.compile();
33443
33444         if(!tpls.body){
33445             tpls.body = new Roo.Template(
33446                '<table border="0" cellspacing="0" cellpadding="0">',
33447                "<tbody>{rows}</tbody>",
33448                "</table>"
33449             );
33450             tpls.body.disableFormats = true;
33451         }
33452         tpls.body.compile();
33453
33454         if(!tpls.row){
33455             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33456             tpls.row.disableFormats = true;
33457         }
33458         tpls.row.compile();
33459
33460         if(!tpls.cell){
33461             tpls.cell = new Roo.Template(
33462                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33463                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33464                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33465                 "</td>"
33466             );
33467             tpls.cell.disableFormats = true;
33468         }
33469         tpls.cell.compile();
33470
33471         this.templates = tpls;
33472     },
33473
33474     // remap these for backwards compat
33475     onColWidthChange : function(){
33476         this.updateColumns.apply(this, arguments);
33477     },
33478     onHeaderChange : function(){
33479         this.updateHeaders.apply(this, arguments);
33480     }, 
33481     onHiddenChange : function(){
33482         this.handleHiddenChange.apply(this, arguments);
33483     },
33484     onColumnMove : function(){
33485         this.handleColumnMove.apply(this, arguments);
33486     },
33487     onColumnLock : function(){
33488         this.handleLockChange.apply(this, arguments);
33489     },
33490
33491     onDataChange : function(){
33492         this.refresh();
33493         this.updateHeaderSortState();
33494     },
33495
33496     onClear : function(){
33497         this.refresh();
33498     },
33499
33500     onUpdate : function(ds, record){
33501         this.refreshRow(record);
33502     },
33503
33504     refreshRow : function(record){
33505         var ds = this.ds, index;
33506         if(typeof record == 'number'){
33507             index = record;
33508             record = ds.getAt(index);
33509         }else{
33510             index = ds.indexOf(record);
33511         }
33512         this.insertRows(ds, index, index, true);
33513         this.onRemove(ds, record, index+1, true);
33514         this.syncRowHeights(index, index);
33515         this.layout();
33516         this.fireEvent("rowupdated", this, index, record);
33517     },
33518
33519     onAdd : function(ds, records, index){
33520         this.insertRows(ds, index, index + (records.length-1));
33521     },
33522
33523     onRemove : function(ds, record, index, isUpdate){
33524         if(isUpdate !== true){
33525             this.fireEvent("beforerowremoved", this, index, record);
33526         }
33527         var bt = this.getBodyTable(), lt = this.getLockedTable();
33528         if(bt.rows[index]){
33529             bt.firstChild.removeChild(bt.rows[index]);
33530         }
33531         if(lt.rows[index]){
33532             lt.firstChild.removeChild(lt.rows[index]);
33533         }
33534         if(isUpdate !== true){
33535             this.stripeRows(index);
33536             this.syncRowHeights(index, index);
33537             this.layout();
33538             this.fireEvent("rowremoved", this, index, record);
33539         }
33540     },
33541
33542     onLoad : function(){
33543         this.scrollToTop();
33544     },
33545
33546     /**
33547      * Scrolls the grid to the top
33548      */
33549     scrollToTop : function(){
33550         if(this.scroller){
33551             this.scroller.dom.scrollTop = 0;
33552             this.syncScroll();
33553         }
33554     },
33555
33556     /**
33557      * Gets a panel in the header of the grid that can be used for toolbars etc.
33558      * After modifying the contents of this panel a call to grid.autoSize() may be
33559      * required to register any changes in size.
33560      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33561      * @return Roo.Element
33562      */
33563     getHeaderPanel : function(doShow){
33564         if(doShow){
33565             this.headerPanel.show();
33566         }
33567         return this.headerPanel;
33568     },
33569
33570     /**
33571      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33572      * After modifying the contents of this panel a call to grid.autoSize() may be
33573      * required to register any changes in size.
33574      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33575      * @return Roo.Element
33576      */
33577     getFooterPanel : function(doShow){
33578         if(doShow){
33579             this.footerPanel.show();
33580         }
33581         return this.footerPanel;
33582     },
33583
33584     initElements : function(){
33585         var E = Roo.Element;
33586         var el = this.grid.getGridEl().dom.firstChild;
33587         var cs = el.childNodes;
33588
33589         this.el = new E(el);
33590         
33591          this.focusEl = new E(el.firstChild);
33592         this.focusEl.swallowEvent("click", true);
33593         
33594         this.headerPanel = new E(cs[1]);
33595         this.headerPanel.enableDisplayMode("block");
33596
33597         this.scroller = new E(cs[2]);
33598         this.scrollSizer = new E(this.scroller.dom.firstChild);
33599
33600         this.lockedWrap = new E(cs[3]);
33601         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33602         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33603
33604         this.mainWrap = new E(cs[4]);
33605         this.mainHd = new E(this.mainWrap.dom.firstChild);
33606         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33607
33608         this.footerPanel = new E(cs[5]);
33609         this.footerPanel.enableDisplayMode("block");
33610
33611         this.resizeProxy = new E(cs[6]);
33612
33613         this.headerSelector = String.format(
33614            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33615            this.lockedHd.id, this.mainHd.id
33616         );
33617
33618         this.splitterSelector = String.format(
33619            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33620            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33621         );
33622     },
33623     idToCssName : function(s)
33624     {
33625         return s.replace(/[^a-z0-9]+/ig, '-');
33626     },
33627
33628     getHeaderCell : function(index){
33629         return Roo.DomQuery.select(this.headerSelector)[index];
33630     },
33631
33632     getHeaderCellMeasure : function(index){
33633         return this.getHeaderCell(index).firstChild;
33634     },
33635
33636     getHeaderCellText : function(index){
33637         return this.getHeaderCell(index).firstChild.firstChild;
33638     },
33639
33640     getLockedTable : function(){
33641         return this.lockedBody.dom.firstChild;
33642     },
33643
33644     getBodyTable : function(){
33645         return this.mainBody.dom.firstChild;
33646     },
33647
33648     getLockedRow : function(index){
33649         return this.getLockedTable().rows[index];
33650     },
33651
33652     getRow : function(index){
33653         return this.getBodyTable().rows[index];
33654     },
33655
33656     getRowComposite : function(index){
33657         if(!this.rowEl){
33658             this.rowEl = new Roo.CompositeElementLite();
33659         }
33660         var els = [], lrow, mrow;
33661         if(lrow = this.getLockedRow(index)){
33662             els.push(lrow);
33663         }
33664         if(mrow = this.getRow(index)){
33665             els.push(mrow);
33666         }
33667         this.rowEl.elements = els;
33668         return this.rowEl;
33669     },
33670     /**
33671      * Gets the 'td' of the cell
33672      * 
33673      * @param {Integer} rowIndex row to select
33674      * @param {Integer} colIndex column to select
33675      * 
33676      * @return {Object} 
33677      */
33678     getCell : function(rowIndex, colIndex){
33679         var locked = this.cm.getLockedCount();
33680         var source;
33681         if(colIndex < locked){
33682             source = this.lockedBody.dom.firstChild;
33683         }else{
33684             source = this.mainBody.dom.firstChild;
33685             colIndex -= locked;
33686         }
33687         return source.rows[rowIndex].childNodes[colIndex];
33688     },
33689
33690     getCellText : function(rowIndex, colIndex){
33691         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33692     },
33693
33694     getCellBox : function(cell){
33695         var b = this.fly(cell).getBox();
33696         if(Roo.isOpera){ // opera fails to report the Y
33697             b.y = cell.offsetTop + this.mainBody.getY();
33698         }
33699         return b;
33700     },
33701
33702     getCellIndex : function(cell){
33703         var id = String(cell.className).match(this.cellRE);
33704         if(id){
33705             return parseInt(id[1], 10);
33706         }
33707         return 0;
33708     },
33709
33710     findHeaderIndex : function(n){
33711         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33712         return r ? this.getCellIndex(r) : false;
33713     },
33714
33715     findHeaderCell : function(n){
33716         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33717         return r ? r : false;
33718     },
33719
33720     findRowIndex : function(n){
33721         if(!n){
33722             return false;
33723         }
33724         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33725         return r ? r.rowIndex : false;
33726     },
33727
33728     findCellIndex : function(node){
33729         var stop = this.el.dom;
33730         while(node && node != stop){
33731             if(this.findRE.test(node.className)){
33732                 return this.getCellIndex(node);
33733             }
33734             node = node.parentNode;
33735         }
33736         return false;
33737     },
33738
33739     getColumnId : function(index){
33740         return this.cm.getColumnId(index);
33741     },
33742
33743     getSplitters : function()
33744     {
33745         if(this.splitterSelector){
33746            return Roo.DomQuery.select(this.splitterSelector);
33747         }else{
33748             return null;
33749       }
33750     },
33751
33752     getSplitter : function(index){
33753         return this.getSplitters()[index];
33754     },
33755
33756     onRowOver : function(e, t){
33757         var row;
33758         if((row = this.findRowIndex(t)) !== false){
33759             this.getRowComposite(row).addClass("x-grid-row-over");
33760         }
33761     },
33762
33763     onRowOut : function(e, t){
33764         var row;
33765         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33766             this.getRowComposite(row).removeClass("x-grid-row-over");
33767         }
33768     },
33769
33770     renderHeaders : function(){
33771         var cm = this.cm;
33772         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33773         var cb = [], lb = [], sb = [], lsb = [], p = {};
33774         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33775             p.cellId = "x-grid-hd-0-" + i;
33776             p.splitId = "x-grid-csplit-0-" + i;
33777             p.id = cm.getColumnId(i);
33778             p.value = cm.getColumnHeader(i) || "";
33779             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33780             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33781             if(!cm.isLocked(i)){
33782                 cb[cb.length] = ct.apply(p);
33783                 sb[sb.length] = st.apply(p);
33784             }else{
33785                 lb[lb.length] = ct.apply(p);
33786                 lsb[lsb.length] = st.apply(p);
33787             }
33788         }
33789         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33790                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33791     },
33792
33793     updateHeaders : function(){
33794         var html = this.renderHeaders();
33795         this.lockedHd.update(html[0]);
33796         this.mainHd.update(html[1]);
33797     },
33798
33799     /**
33800      * Focuses the specified row.
33801      * @param {Number} row The row index
33802      */
33803     focusRow : function(row)
33804     {
33805         //Roo.log('GridView.focusRow');
33806         var x = this.scroller.dom.scrollLeft;
33807         this.focusCell(row, 0, false);
33808         this.scroller.dom.scrollLeft = x;
33809     },
33810
33811     /**
33812      * Focuses the specified cell.
33813      * @param {Number} row The row index
33814      * @param {Number} col The column index
33815      * @param {Boolean} hscroll false to disable horizontal scrolling
33816      */
33817     focusCell : function(row, col, hscroll)
33818     {
33819         //Roo.log('GridView.focusCell');
33820         var el = this.ensureVisible(row, col, hscroll);
33821         this.focusEl.alignTo(el, "tl-tl");
33822         if(Roo.isGecko){
33823             this.focusEl.focus();
33824         }else{
33825             this.focusEl.focus.defer(1, this.focusEl);
33826         }
33827     },
33828
33829     /**
33830      * Scrolls the specified cell into view
33831      * @param {Number} row The row index
33832      * @param {Number} col The column index
33833      * @param {Boolean} hscroll false to disable horizontal scrolling
33834      */
33835     ensureVisible : function(row, col, hscroll)
33836     {
33837         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33838         //return null; //disable for testing.
33839         if(typeof row != "number"){
33840             row = row.rowIndex;
33841         }
33842         if(row < 0 && row >= this.ds.getCount()){
33843             return  null;
33844         }
33845         col = (col !== undefined ? col : 0);
33846         var cm = this.grid.colModel;
33847         while(cm.isHidden(col)){
33848             col++;
33849         }
33850
33851         var el = this.getCell(row, col);
33852         if(!el){
33853             return null;
33854         }
33855         var c = this.scroller.dom;
33856
33857         var ctop = parseInt(el.offsetTop, 10);
33858         var cleft = parseInt(el.offsetLeft, 10);
33859         var cbot = ctop + el.offsetHeight;
33860         var cright = cleft + el.offsetWidth;
33861         
33862         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33863         var stop = parseInt(c.scrollTop, 10);
33864         var sleft = parseInt(c.scrollLeft, 10);
33865         var sbot = stop + ch;
33866         var sright = sleft + c.clientWidth;
33867         /*
33868         Roo.log('GridView.ensureVisible:' +
33869                 ' ctop:' + ctop +
33870                 ' c.clientHeight:' + c.clientHeight +
33871                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33872                 ' stop:' + stop +
33873                 ' cbot:' + cbot +
33874                 ' sbot:' + sbot +
33875                 ' ch:' + ch  
33876                 );
33877         */
33878         if(ctop < stop){
33879             c.scrollTop = ctop;
33880             //Roo.log("set scrolltop to ctop DISABLE?");
33881         }else if(cbot > sbot){
33882             //Roo.log("set scrolltop to cbot-ch");
33883             c.scrollTop = cbot-ch;
33884         }
33885         
33886         if(hscroll !== false){
33887             if(cleft < sleft){
33888                 c.scrollLeft = cleft;
33889             }else if(cright > sright){
33890                 c.scrollLeft = cright-c.clientWidth;
33891             }
33892         }
33893          
33894         return el;
33895     },
33896
33897     updateColumns : function(){
33898         this.grid.stopEditing();
33899         var cm = this.grid.colModel, colIds = this.getColumnIds();
33900         //var totalWidth = cm.getTotalWidth();
33901         var pos = 0;
33902         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33903             //if(cm.isHidden(i)) continue;
33904             var w = cm.getColumnWidth(i);
33905             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33906             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33907         }
33908         this.updateSplitters();
33909     },
33910
33911     generateRules : function(cm){
33912         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33913         Roo.util.CSS.removeStyleSheet(rulesId);
33914         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33915             var cid = cm.getColumnId(i);
33916             var align = '';
33917             if(cm.config[i].align){
33918                 align = 'text-align:'+cm.config[i].align+';';
33919             }
33920             var hidden = '';
33921             if(cm.isHidden(i)){
33922                 hidden = 'display:none;';
33923             }
33924             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33925             ruleBuf.push(
33926                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33927                     this.hdSelector, cid, " {\n", align, width, "}\n",
33928                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33929                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33930         }
33931         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33932     },
33933
33934     updateSplitters : function(){
33935         var cm = this.cm, s = this.getSplitters();
33936         if(s){ // splitters not created yet
33937             var pos = 0, locked = true;
33938             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33939                 if(cm.isHidden(i)) {
33940                     continue;
33941                 }
33942                 var w = cm.getColumnWidth(i); // make sure it's a number
33943                 if(!cm.isLocked(i) && locked){
33944                     pos = 0;
33945                     locked = false;
33946                 }
33947                 pos += w;
33948                 s[i].style.left = (pos-this.splitOffset) + "px";
33949             }
33950         }
33951     },
33952
33953     handleHiddenChange : function(colModel, colIndex, hidden){
33954         if(hidden){
33955             this.hideColumn(colIndex);
33956         }else{
33957             this.unhideColumn(colIndex);
33958         }
33959     },
33960
33961     hideColumn : function(colIndex){
33962         var cid = this.getColumnId(colIndex);
33963         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33964         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33965         if(Roo.isSafari){
33966             this.updateHeaders();
33967         }
33968         this.updateSplitters();
33969         this.layout();
33970     },
33971
33972     unhideColumn : function(colIndex){
33973         var cid = this.getColumnId(colIndex);
33974         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33975         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33976
33977         if(Roo.isSafari){
33978             this.updateHeaders();
33979         }
33980         this.updateSplitters();
33981         this.layout();
33982     },
33983
33984     insertRows : function(dm, firstRow, lastRow, isUpdate){
33985         if(firstRow == 0 && lastRow == dm.getCount()-1){
33986             this.refresh();
33987         }else{
33988             if(!isUpdate){
33989                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33990             }
33991             var s = this.getScrollState();
33992             var markup = this.renderRows(firstRow, lastRow);
33993             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33994             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33995             this.restoreScroll(s);
33996             if(!isUpdate){
33997                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33998                 this.syncRowHeights(firstRow, lastRow);
33999                 this.stripeRows(firstRow);
34000                 this.layout();
34001             }
34002         }
34003     },
34004
34005     bufferRows : function(markup, target, index){
34006         var before = null, trows = target.rows, tbody = target.tBodies[0];
34007         if(index < trows.length){
34008             before = trows[index];
34009         }
34010         var b = document.createElement("div");
34011         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34012         var rows = b.firstChild.rows;
34013         for(var i = 0, len = rows.length; i < len; i++){
34014             if(before){
34015                 tbody.insertBefore(rows[0], before);
34016             }else{
34017                 tbody.appendChild(rows[0]);
34018             }
34019         }
34020         b.innerHTML = "";
34021         b = null;
34022     },
34023
34024     deleteRows : function(dm, firstRow, lastRow){
34025         if(dm.getRowCount()<1){
34026             this.fireEvent("beforerefresh", this);
34027             this.mainBody.update("");
34028             this.lockedBody.update("");
34029             this.fireEvent("refresh", this);
34030         }else{
34031             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34032             var bt = this.getBodyTable();
34033             var tbody = bt.firstChild;
34034             var rows = bt.rows;
34035             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34036                 tbody.removeChild(rows[firstRow]);
34037             }
34038             this.stripeRows(firstRow);
34039             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34040         }
34041     },
34042
34043     updateRows : function(dataSource, firstRow, lastRow){
34044         var s = this.getScrollState();
34045         this.refresh();
34046         this.restoreScroll(s);
34047     },
34048
34049     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34050         if(!noRefresh){
34051            this.refresh();
34052         }
34053         this.updateHeaderSortState();
34054     },
34055
34056     getScrollState : function(){
34057         
34058         var sb = this.scroller.dom;
34059         return {left: sb.scrollLeft, top: sb.scrollTop};
34060     },
34061
34062     stripeRows : function(startRow){
34063         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34064             return;
34065         }
34066         startRow = startRow || 0;
34067         var rows = this.getBodyTable().rows;
34068         var lrows = this.getLockedTable().rows;
34069         var cls = ' x-grid-row-alt ';
34070         for(var i = startRow, len = rows.length; i < len; i++){
34071             var row = rows[i], lrow = lrows[i];
34072             var isAlt = ((i+1) % 2 == 0);
34073             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34074             if(isAlt == hasAlt){
34075                 continue;
34076             }
34077             if(isAlt){
34078                 row.className += " x-grid-row-alt";
34079             }else{
34080                 row.className = row.className.replace("x-grid-row-alt", "");
34081             }
34082             if(lrow){
34083                 lrow.className = row.className;
34084             }
34085         }
34086     },
34087
34088     restoreScroll : function(state){
34089         //Roo.log('GridView.restoreScroll');
34090         var sb = this.scroller.dom;
34091         sb.scrollLeft = state.left;
34092         sb.scrollTop = state.top;
34093         this.syncScroll();
34094     },
34095
34096     syncScroll : function(){
34097         //Roo.log('GridView.syncScroll');
34098         var sb = this.scroller.dom;
34099         var sh = this.mainHd.dom;
34100         var bs = this.mainBody.dom;
34101         var lv = this.lockedBody.dom;
34102         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34103         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34104     },
34105
34106     handleScroll : function(e){
34107         this.syncScroll();
34108         var sb = this.scroller.dom;
34109         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34110         e.stopEvent();
34111     },
34112
34113     handleWheel : function(e){
34114         var d = e.getWheelDelta();
34115         this.scroller.dom.scrollTop -= d*22;
34116         // set this here to prevent jumpy scrolling on large tables
34117         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34118         e.stopEvent();
34119     },
34120
34121     renderRows : function(startRow, endRow){
34122         // pull in all the crap needed to render rows
34123         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34124         var colCount = cm.getColumnCount();
34125
34126         if(ds.getCount() < 1){
34127             return ["", ""];
34128         }
34129
34130         // build a map for all the columns
34131         var cs = [];
34132         for(var i = 0; i < colCount; i++){
34133             var name = cm.getDataIndex(i);
34134             cs[i] = {
34135                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34136                 renderer : cm.getRenderer(i),
34137                 id : cm.getColumnId(i),
34138                 locked : cm.isLocked(i),
34139                 has_editor : cm.isCellEditable(i)
34140             };
34141         }
34142
34143         startRow = startRow || 0;
34144         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34145
34146         // records to render
34147         var rs = ds.getRange(startRow, endRow);
34148
34149         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34150     },
34151
34152     // As much as I hate to duplicate code, this was branched because FireFox really hates
34153     // [].join("") on strings. The performance difference was substantial enough to
34154     // branch this function
34155     doRender : Roo.isGecko ?
34156             function(cs, rs, ds, startRow, colCount, stripe){
34157                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34158                 // buffers
34159                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34160                 
34161                 var hasListener = this.grid.hasListener('rowclass');
34162                 var rowcfg = {};
34163                 for(var j = 0, len = rs.length; j < len; j++){
34164                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34165                     for(var i = 0; i < colCount; i++){
34166                         c = cs[i];
34167                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34168                         p.id = c.id;
34169                         p.css = p.attr = "";
34170                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34171                         if(p.value == undefined || p.value === "") {
34172                             p.value = "&#160;";
34173                         }
34174                         if(c.has_editor){
34175                             p.css += ' x-grid-editable-cell';
34176                         }
34177                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34178                             p.css +=  ' x-grid-dirty-cell';
34179                         }
34180                         var markup = ct.apply(p);
34181                         if(!c.locked){
34182                             cb+= markup;
34183                         }else{
34184                             lcb+= markup;
34185                         }
34186                     }
34187                     var alt = [];
34188                     if(stripe && ((rowIndex+1) % 2 == 0)){
34189                         alt.push("x-grid-row-alt")
34190                     }
34191                     if(r.dirty){
34192                         alt.push(  " x-grid-dirty-row");
34193                     }
34194                     rp.cells = lcb;
34195                     if(this.getRowClass){
34196                         alt.push(this.getRowClass(r, rowIndex));
34197                     }
34198                     if (hasListener) {
34199                         rowcfg = {
34200                              
34201                             record: r,
34202                             rowIndex : rowIndex,
34203                             rowClass : ''
34204                         };
34205                         this.grid.fireEvent('rowclass', this, rowcfg);
34206                         alt.push(rowcfg.rowClass);
34207                     }
34208                     rp.alt = alt.join(" ");
34209                     lbuf+= rt.apply(rp);
34210                     rp.cells = cb;
34211                     buf+=  rt.apply(rp);
34212                 }
34213                 return [lbuf, buf];
34214             } :
34215             function(cs, rs, ds, startRow, colCount, stripe){
34216                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34217                 // buffers
34218                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34219                 var hasListener = this.grid.hasListener('rowclass');
34220  
34221                 var rowcfg = {};
34222                 for(var j = 0, len = rs.length; j < len; j++){
34223                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34224                     for(var i = 0; i < colCount; i++){
34225                         c = cs[i];
34226                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34227                         p.id = c.id;
34228                         p.css = p.attr = "";
34229                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34230                         if(p.value == undefined || p.value === "") {
34231                             p.value = "&#160;";
34232                         }
34233                         //Roo.log(c);
34234                          if(c.has_editor){
34235                             p.css += ' x-grid-editable-cell';
34236                         }
34237                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34238                             p.css += ' x-grid-dirty-cell' 
34239                         }
34240                         
34241                         var markup = ct.apply(p);
34242                         if(!c.locked){
34243                             cb[cb.length] = markup;
34244                         }else{
34245                             lcb[lcb.length] = markup;
34246                         }
34247                     }
34248                     var alt = [];
34249                     if(stripe && ((rowIndex+1) % 2 == 0)){
34250                         alt.push( "x-grid-row-alt");
34251                     }
34252                     if(r.dirty){
34253                         alt.push(" x-grid-dirty-row");
34254                     }
34255                     rp.cells = lcb;
34256                     if(this.getRowClass){
34257                         alt.push( this.getRowClass(r, rowIndex));
34258                     }
34259                     if (hasListener) {
34260                         rowcfg = {
34261                              
34262                             record: r,
34263                             rowIndex : rowIndex,
34264                             rowClass : ''
34265                         };
34266                         this.grid.fireEvent('rowclass', this, rowcfg);
34267                         alt.push(rowcfg.rowClass);
34268                     }
34269                     
34270                     rp.alt = alt.join(" ");
34271                     rp.cells = lcb.join("");
34272                     lbuf[lbuf.length] = rt.apply(rp);
34273                     rp.cells = cb.join("");
34274                     buf[buf.length] =  rt.apply(rp);
34275                 }
34276                 return [lbuf.join(""), buf.join("")];
34277             },
34278
34279     renderBody : function(){
34280         var markup = this.renderRows();
34281         var bt = this.templates.body;
34282         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34283     },
34284
34285     /**
34286      * Refreshes the grid
34287      * @param {Boolean} headersToo
34288      */
34289     refresh : function(headersToo){
34290         this.fireEvent("beforerefresh", this);
34291         this.grid.stopEditing();
34292         var result = this.renderBody();
34293         this.lockedBody.update(result[0]);
34294         this.mainBody.update(result[1]);
34295         if(headersToo === true){
34296             this.updateHeaders();
34297             this.updateColumns();
34298             this.updateSplitters();
34299             this.updateHeaderSortState();
34300         }
34301         this.syncRowHeights();
34302         this.layout();
34303         this.fireEvent("refresh", this);
34304     },
34305
34306     handleColumnMove : function(cm, oldIndex, newIndex){
34307         this.indexMap = null;
34308         var s = this.getScrollState();
34309         this.refresh(true);
34310         this.restoreScroll(s);
34311         this.afterMove(newIndex);
34312     },
34313
34314     afterMove : function(colIndex){
34315         if(this.enableMoveAnim && Roo.enableFx){
34316             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34317         }
34318         // if multisort - fix sortOrder, and reload..
34319         if (this.grid.dataSource.multiSort) {
34320             // the we can call sort again..
34321             var dm = this.grid.dataSource;
34322             var cm = this.grid.colModel;
34323             var so = [];
34324             for(var i = 0; i < cm.config.length; i++ ) {
34325                 
34326                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34327                     continue; // dont' bother, it's not in sort list or being set.
34328                 }
34329                 
34330                 so.push(cm.config[i].dataIndex);
34331             };
34332             dm.sortOrder = so;
34333             dm.load(dm.lastOptions);
34334             
34335             
34336         }
34337         
34338     },
34339
34340     updateCell : function(dm, rowIndex, dataIndex){
34341         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34342         if(typeof colIndex == "undefined"){ // not present in grid
34343             return;
34344         }
34345         var cm = this.grid.colModel;
34346         var cell = this.getCell(rowIndex, colIndex);
34347         var cellText = this.getCellText(rowIndex, colIndex);
34348
34349         var p = {
34350             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34351             id : cm.getColumnId(colIndex),
34352             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34353         };
34354         var renderer = cm.getRenderer(colIndex);
34355         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34356         if(typeof val == "undefined" || val === "") {
34357             val = "&#160;";
34358         }
34359         cellText.innerHTML = val;
34360         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34361         this.syncRowHeights(rowIndex, rowIndex);
34362     },
34363
34364     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34365         var maxWidth = 0;
34366         if(this.grid.autoSizeHeaders){
34367             var h = this.getHeaderCellMeasure(colIndex);
34368             maxWidth = Math.max(maxWidth, h.scrollWidth);
34369         }
34370         var tb, index;
34371         if(this.cm.isLocked(colIndex)){
34372             tb = this.getLockedTable();
34373             index = colIndex;
34374         }else{
34375             tb = this.getBodyTable();
34376             index = colIndex - this.cm.getLockedCount();
34377         }
34378         if(tb && tb.rows){
34379             var rows = tb.rows;
34380             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34381             for(var i = 0; i < stopIndex; i++){
34382                 var cell = rows[i].childNodes[index].firstChild;
34383                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34384             }
34385         }
34386         return maxWidth + /*margin for error in IE*/ 5;
34387     },
34388     /**
34389      * Autofit a column to its content.
34390      * @param {Number} colIndex
34391      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34392      */
34393      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34394          if(this.cm.isHidden(colIndex)){
34395              return; // can't calc a hidden column
34396          }
34397         if(forceMinSize){
34398             var cid = this.cm.getColumnId(colIndex);
34399             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34400            if(this.grid.autoSizeHeaders){
34401                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34402            }
34403         }
34404         var newWidth = this.calcColumnWidth(colIndex);
34405         this.cm.setColumnWidth(colIndex,
34406             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34407         if(!suppressEvent){
34408             this.grid.fireEvent("columnresize", colIndex, newWidth);
34409         }
34410     },
34411
34412     /**
34413      * Autofits all columns to their content and then expands to fit any extra space in the grid
34414      */
34415      autoSizeColumns : function(){
34416         var cm = this.grid.colModel;
34417         var colCount = cm.getColumnCount();
34418         for(var i = 0; i < colCount; i++){
34419             this.autoSizeColumn(i, true, true);
34420         }
34421         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34422             this.fitColumns();
34423         }else{
34424             this.updateColumns();
34425             this.layout();
34426         }
34427     },
34428
34429     /**
34430      * Autofits all columns to the grid's width proportionate with their current size
34431      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34432      */
34433     fitColumns : function(reserveScrollSpace){
34434         var cm = this.grid.colModel;
34435         var colCount = cm.getColumnCount();
34436         var cols = [];
34437         var width = 0;
34438         var i, w;
34439         for (i = 0; i < colCount; i++){
34440             if(!cm.isHidden(i) && !cm.isFixed(i)){
34441                 w = cm.getColumnWidth(i);
34442                 cols.push(i);
34443                 cols.push(w);
34444                 width += w;
34445             }
34446         }
34447         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34448         if(reserveScrollSpace){
34449             avail -= 17;
34450         }
34451         var frac = (avail - cm.getTotalWidth())/width;
34452         while (cols.length){
34453             w = cols.pop();
34454             i = cols.pop();
34455             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34456         }
34457         this.updateColumns();
34458         this.layout();
34459     },
34460
34461     onRowSelect : function(rowIndex){
34462         var row = this.getRowComposite(rowIndex);
34463         row.addClass("x-grid-row-selected");
34464     },
34465
34466     onRowDeselect : function(rowIndex){
34467         var row = this.getRowComposite(rowIndex);
34468         row.removeClass("x-grid-row-selected");
34469     },
34470
34471     onCellSelect : function(row, col){
34472         var cell = this.getCell(row, col);
34473         if(cell){
34474             Roo.fly(cell).addClass("x-grid-cell-selected");
34475         }
34476     },
34477
34478     onCellDeselect : function(row, col){
34479         var cell = this.getCell(row, col);
34480         if(cell){
34481             Roo.fly(cell).removeClass("x-grid-cell-selected");
34482         }
34483     },
34484
34485     updateHeaderSortState : function(){
34486         
34487         // sort state can be single { field: xxx, direction : yyy}
34488         // or   { xxx=>ASC , yyy : DESC ..... }
34489         
34490         var mstate = {};
34491         if (!this.ds.multiSort) { 
34492             var state = this.ds.getSortState();
34493             if(!state){
34494                 return;
34495             }
34496             mstate[state.field] = state.direction;
34497             // FIXME... - this is not used here.. but might be elsewhere..
34498             this.sortState = state;
34499             
34500         } else {
34501             mstate = this.ds.sortToggle;
34502         }
34503         //remove existing sort classes..
34504         
34505         var sc = this.sortClasses;
34506         var hds = this.el.select(this.headerSelector).removeClass(sc);
34507         
34508         for(var f in mstate) {
34509         
34510             var sortColumn = this.cm.findColumnIndex(f);
34511             
34512             if(sortColumn != -1){
34513                 var sortDir = mstate[f];        
34514                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34515             }
34516         }
34517         
34518          
34519         
34520     },
34521
34522
34523     handleHeaderClick : function(g, index,e){
34524         
34525         Roo.log("header click");
34526         
34527         if (Roo.isTouch) {
34528             // touch events on header are handled by context
34529             this.handleHdCtx(g,index,e);
34530             return;
34531         }
34532         
34533         
34534         if(this.headersDisabled){
34535             return;
34536         }
34537         var dm = g.dataSource, cm = g.colModel;
34538         if(!cm.isSortable(index)){
34539             return;
34540         }
34541         g.stopEditing();
34542         
34543         if (dm.multiSort) {
34544             // update the sortOrder
34545             var so = [];
34546             for(var i = 0; i < cm.config.length; i++ ) {
34547                 
34548                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34549                     continue; // dont' bother, it's not in sort list or being set.
34550                 }
34551                 
34552                 so.push(cm.config[i].dataIndex);
34553             };
34554             dm.sortOrder = so;
34555         }
34556         
34557         
34558         dm.sort(cm.getDataIndex(index));
34559     },
34560
34561
34562     destroy : function(){
34563         if(this.colMenu){
34564             this.colMenu.removeAll();
34565             Roo.menu.MenuMgr.unregister(this.colMenu);
34566             this.colMenu.getEl().remove();
34567             delete this.colMenu;
34568         }
34569         if(this.hmenu){
34570             this.hmenu.removeAll();
34571             Roo.menu.MenuMgr.unregister(this.hmenu);
34572             this.hmenu.getEl().remove();
34573             delete this.hmenu;
34574         }
34575         if(this.grid.enableColumnMove){
34576             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34577             if(dds){
34578                 for(var dd in dds){
34579                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34580                         var elid = dds[dd].dragElId;
34581                         dds[dd].unreg();
34582                         Roo.get(elid).remove();
34583                     } else if(dds[dd].config.isTarget){
34584                         dds[dd].proxyTop.remove();
34585                         dds[dd].proxyBottom.remove();
34586                         dds[dd].unreg();
34587                     }
34588                     if(Roo.dd.DDM.locationCache[dd]){
34589                         delete Roo.dd.DDM.locationCache[dd];
34590                     }
34591                 }
34592                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34593             }
34594         }
34595         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34596         this.bind(null, null);
34597         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34598     },
34599
34600     handleLockChange : function(){
34601         this.refresh(true);
34602     },
34603
34604     onDenyColumnLock : function(){
34605
34606     },
34607
34608     onDenyColumnHide : function(){
34609
34610     },
34611
34612     handleHdMenuClick : function(item){
34613         var index = this.hdCtxIndex;
34614         var cm = this.cm, ds = this.ds;
34615         switch(item.id){
34616             case "asc":
34617                 ds.sort(cm.getDataIndex(index), "ASC");
34618                 break;
34619             case "desc":
34620                 ds.sort(cm.getDataIndex(index), "DESC");
34621                 break;
34622             case "lock":
34623                 var lc = cm.getLockedCount();
34624                 if(cm.getColumnCount(true) <= lc+1){
34625                     this.onDenyColumnLock();
34626                     return;
34627                 }
34628                 if(lc != index){
34629                     cm.setLocked(index, true, true);
34630                     cm.moveColumn(index, lc);
34631                     this.grid.fireEvent("columnmove", index, lc);
34632                 }else{
34633                     cm.setLocked(index, true);
34634                 }
34635             break;
34636             case "unlock":
34637                 var lc = cm.getLockedCount();
34638                 if((lc-1) != index){
34639                     cm.setLocked(index, false, true);
34640                     cm.moveColumn(index, lc-1);
34641                     this.grid.fireEvent("columnmove", index, lc-1);
34642                 }else{
34643                     cm.setLocked(index, false);
34644                 }
34645             break;
34646             case 'wider': // used to expand cols on touch..
34647             case 'narrow':
34648                 var cw = cm.getColumnWidth(index);
34649                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34650                 cw = Math.max(0, cw);
34651                 cw = Math.min(cw,4000);
34652                 cm.setColumnWidth(index, cw);
34653                 break;
34654                 
34655             default:
34656                 index = cm.getIndexById(item.id.substr(4));
34657                 if(index != -1){
34658                     if(item.checked && cm.getColumnCount(true) <= 1){
34659                         this.onDenyColumnHide();
34660                         return false;
34661                     }
34662                     cm.setHidden(index, item.checked);
34663                 }
34664         }
34665         return true;
34666     },
34667
34668     beforeColMenuShow : function(){
34669         var cm = this.cm,  colCount = cm.getColumnCount();
34670         this.colMenu.removeAll();
34671         for(var i = 0; i < colCount; i++){
34672             this.colMenu.add(new Roo.menu.CheckItem({
34673                 id: "col-"+cm.getColumnId(i),
34674                 text: cm.getColumnHeader(i),
34675                 checked: !cm.isHidden(i),
34676                 hideOnClick:false
34677             }));
34678         }
34679     },
34680
34681     handleHdCtx : function(g, index, e){
34682         e.stopEvent();
34683         var hd = this.getHeaderCell(index);
34684         this.hdCtxIndex = index;
34685         var ms = this.hmenu.items, cm = this.cm;
34686         ms.get("asc").setDisabled(!cm.isSortable(index));
34687         ms.get("desc").setDisabled(!cm.isSortable(index));
34688         if(this.grid.enableColLock !== false){
34689             ms.get("lock").setDisabled(cm.isLocked(index));
34690             ms.get("unlock").setDisabled(!cm.isLocked(index));
34691         }
34692         this.hmenu.show(hd, "tl-bl");
34693     },
34694
34695     handleHdOver : function(e){
34696         var hd = this.findHeaderCell(e.getTarget());
34697         if(hd && !this.headersDisabled){
34698             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34699                this.fly(hd).addClass("x-grid-hd-over");
34700             }
34701         }
34702     },
34703
34704     handleHdOut : function(e){
34705         var hd = this.findHeaderCell(e.getTarget());
34706         if(hd){
34707             this.fly(hd).removeClass("x-grid-hd-over");
34708         }
34709     },
34710
34711     handleSplitDblClick : function(e, t){
34712         var i = this.getCellIndex(t);
34713         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34714             this.autoSizeColumn(i, true);
34715             this.layout();
34716         }
34717     },
34718
34719     render : function(){
34720
34721         var cm = this.cm;
34722         var colCount = cm.getColumnCount();
34723
34724         if(this.grid.monitorWindowResize === true){
34725             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34726         }
34727         var header = this.renderHeaders();
34728         var body = this.templates.body.apply({rows:""});
34729         var html = this.templates.master.apply({
34730             lockedBody: body,
34731             body: body,
34732             lockedHeader: header[0],
34733             header: header[1]
34734         });
34735
34736         //this.updateColumns();
34737
34738         this.grid.getGridEl().dom.innerHTML = html;
34739
34740         this.initElements();
34741         
34742         // a kludge to fix the random scolling effect in webkit
34743         this.el.on("scroll", function() {
34744             this.el.dom.scrollTop=0; // hopefully not recursive..
34745         },this);
34746
34747         this.scroller.on("scroll", this.handleScroll, this);
34748         this.lockedBody.on("mousewheel", this.handleWheel, this);
34749         this.mainBody.on("mousewheel", this.handleWheel, this);
34750
34751         this.mainHd.on("mouseover", this.handleHdOver, this);
34752         this.mainHd.on("mouseout", this.handleHdOut, this);
34753         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34754                 {delegate: "."+this.splitClass});
34755
34756         this.lockedHd.on("mouseover", this.handleHdOver, this);
34757         this.lockedHd.on("mouseout", this.handleHdOut, this);
34758         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34759                 {delegate: "."+this.splitClass});
34760
34761         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34762             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34763         }
34764
34765         this.updateSplitters();
34766
34767         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34768             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34769             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34770         }
34771
34772         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34773             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34774             this.hmenu.add(
34775                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34776                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34777             );
34778             if(this.grid.enableColLock !== false){
34779                 this.hmenu.add('-',
34780                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34781                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34782                 );
34783             }
34784             if (Roo.isTouch) {
34785                  this.hmenu.add('-',
34786                     {id:"wider", text: this.columnsWiderText},
34787                     {id:"narrow", text: this.columnsNarrowText }
34788                 );
34789                 
34790                  
34791             }
34792             
34793             if(this.grid.enableColumnHide !== false){
34794
34795                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34796                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34797                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34798
34799                 this.hmenu.add('-',
34800                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34801                 );
34802             }
34803             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34804
34805             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34806         }
34807
34808         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34809             this.dd = new Roo.grid.GridDragZone(this.grid, {
34810                 ddGroup : this.grid.ddGroup || 'GridDD'
34811             });
34812             
34813         }
34814
34815         /*
34816         for(var i = 0; i < colCount; i++){
34817             if(cm.isHidden(i)){
34818                 this.hideColumn(i);
34819             }
34820             if(cm.config[i].align){
34821                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34822                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34823             }
34824         }*/
34825         
34826         this.updateHeaderSortState();
34827
34828         this.beforeInitialResize();
34829         this.layout(true);
34830
34831         // two part rendering gives faster view to the user
34832         this.renderPhase2.defer(1, this);
34833     },
34834
34835     renderPhase2 : function(){
34836         // render the rows now
34837         this.refresh();
34838         if(this.grid.autoSizeColumns){
34839             this.autoSizeColumns();
34840         }
34841     },
34842
34843     beforeInitialResize : function(){
34844
34845     },
34846
34847     onColumnSplitterMoved : function(i, w){
34848         this.userResized = true;
34849         var cm = this.grid.colModel;
34850         cm.setColumnWidth(i, w, true);
34851         var cid = cm.getColumnId(i);
34852         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34853         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34854         this.updateSplitters();
34855         this.layout();
34856         this.grid.fireEvent("columnresize", i, w);
34857     },
34858
34859     syncRowHeights : function(startIndex, endIndex){
34860         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34861             startIndex = startIndex || 0;
34862             var mrows = this.getBodyTable().rows;
34863             var lrows = this.getLockedTable().rows;
34864             var len = mrows.length-1;
34865             endIndex = Math.min(endIndex || len, len);
34866             for(var i = startIndex; i <= endIndex; i++){
34867                 var m = mrows[i], l = lrows[i];
34868                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34869                 m.style.height = l.style.height = h + "px";
34870             }
34871         }
34872     },
34873
34874     layout : function(initialRender, is2ndPass)
34875     {
34876         var g = this.grid;
34877         var auto = g.autoHeight;
34878         var scrollOffset = 16;
34879         var c = g.getGridEl(), cm = this.cm,
34880                 expandCol = g.autoExpandColumn,
34881                 gv = this;
34882         //c.beginMeasure();
34883
34884         if(!c.dom.offsetWidth){ // display:none?
34885             if(initialRender){
34886                 this.lockedWrap.show();
34887                 this.mainWrap.show();
34888             }
34889             return;
34890         }
34891
34892         var hasLock = this.cm.isLocked(0);
34893
34894         var tbh = this.headerPanel.getHeight();
34895         var bbh = this.footerPanel.getHeight();
34896
34897         if(auto){
34898             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34899             var newHeight = ch + c.getBorderWidth("tb");
34900             if(g.maxHeight){
34901                 newHeight = Math.min(g.maxHeight, newHeight);
34902             }
34903             c.setHeight(newHeight);
34904         }
34905
34906         if(g.autoWidth){
34907             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34908         }
34909
34910         var s = this.scroller;
34911
34912         var csize = c.getSize(true);
34913
34914         this.el.setSize(csize.width, csize.height);
34915
34916         this.headerPanel.setWidth(csize.width);
34917         this.footerPanel.setWidth(csize.width);
34918
34919         var hdHeight = this.mainHd.getHeight();
34920         var vw = csize.width;
34921         var vh = csize.height - (tbh + bbh);
34922
34923         s.setSize(vw, vh);
34924
34925         var bt = this.getBodyTable();
34926         
34927         if(cm.getLockedCount() == cm.config.length){
34928             bt = this.getLockedTable();
34929         }
34930         
34931         var ltWidth = hasLock ?
34932                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34933
34934         var scrollHeight = bt.offsetHeight;
34935         var scrollWidth = ltWidth + bt.offsetWidth;
34936         var vscroll = false, hscroll = false;
34937
34938         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34939
34940         var lw = this.lockedWrap, mw = this.mainWrap;
34941         var lb = this.lockedBody, mb = this.mainBody;
34942
34943         setTimeout(function(){
34944             var t = s.dom.offsetTop;
34945             var w = s.dom.clientWidth,
34946                 h = s.dom.clientHeight;
34947
34948             lw.setTop(t);
34949             lw.setSize(ltWidth, h);
34950
34951             mw.setLeftTop(ltWidth, t);
34952             mw.setSize(w-ltWidth, h);
34953
34954             lb.setHeight(h-hdHeight);
34955             mb.setHeight(h-hdHeight);
34956
34957             if(is2ndPass !== true && !gv.userResized && expandCol){
34958                 // high speed resize without full column calculation
34959                 
34960                 var ci = cm.getIndexById(expandCol);
34961                 if (ci < 0) {
34962                     ci = cm.findColumnIndex(expandCol);
34963                 }
34964                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34965                 var expandId = cm.getColumnId(ci);
34966                 var  tw = cm.getTotalWidth(false);
34967                 var currentWidth = cm.getColumnWidth(ci);
34968                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34969                 if(currentWidth != cw){
34970                     cm.setColumnWidth(ci, cw, true);
34971                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34972                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34973                     gv.updateSplitters();
34974                     gv.layout(false, true);
34975                 }
34976             }
34977
34978             if(initialRender){
34979                 lw.show();
34980                 mw.show();
34981             }
34982             //c.endMeasure();
34983         }, 10);
34984     },
34985
34986     onWindowResize : function(){
34987         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34988             return;
34989         }
34990         this.layout();
34991     },
34992
34993     appendFooter : function(parentEl){
34994         return null;
34995     },
34996
34997     sortAscText : "Sort Ascending",
34998     sortDescText : "Sort Descending",
34999     lockText : "Lock Column",
35000     unlockText : "Unlock Column",
35001     columnsText : "Columns",
35002  
35003     columnsWiderText : "Wider",
35004     columnsNarrowText : "Thinner"
35005 });
35006
35007
35008 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35009     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35010     this.proxy.el.addClass('x-grid3-col-dd');
35011 };
35012
35013 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35014     handleMouseDown : function(e){
35015
35016     },
35017
35018     callHandleMouseDown : function(e){
35019         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35020     }
35021 });
35022 /*
35023  * Based on:
35024  * Ext JS Library 1.1.1
35025  * Copyright(c) 2006-2007, Ext JS, LLC.
35026  *
35027  * Originally Released Under LGPL - original licence link has changed is not relivant.
35028  *
35029  * Fork - LGPL
35030  * <script type="text/javascript">
35031  */
35032  /**
35033  * @extends Roo.dd.DDProxy
35034  * @class Roo.grid.SplitDragZone
35035  * Support for Column Header resizing
35036  * @constructor
35037  * @param {Object} config
35038  */
35039 // private
35040 // This is a support class used internally by the Grid components
35041 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35042     this.grid = grid;
35043     this.view = grid.getView();
35044     this.proxy = this.view.resizeProxy;
35045     Roo.grid.SplitDragZone.superclass.constructor.call(
35046         this,
35047         hd, // ID
35048         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35049         {  // CONFIG
35050             dragElId : Roo.id(this.proxy.dom),
35051             resizeFrame:false
35052         }
35053     );
35054     
35055     this.setHandleElId(Roo.id(hd));
35056     if (hd2 !== false) {
35057         this.setOuterHandleElId(Roo.id(hd2));
35058     }
35059     
35060     this.scroll = false;
35061 };
35062 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35063     fly: Roo.Element.fly,
35064
35065     b4StartDrag : function(x, y){
35066         this.view.headersDisabled = true;
35067         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35068                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35069         );
35070         this.proxy.setHeight(h);
35071         
35072         // for old system colWidth really stored the actual width?
35073         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35074         // which in reality did not work.. - it worked only for fixed sizes
35075         // for resizable we need to use actual sizes.
35076         var w = this.cm.getColumnWidth(this.cellIndex);
35077         if (!this.view.mainWrap) {
35078             // bootstrap.
35079             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35080         }
35081         
35082         
35083         
35084         // this was w-this.grid.minColumnWidth;
35085         // doesnt really make sense? - w = thie curren width or the rendered one?
35086         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35087         this.resetConstraints();
35088         this.setXConstraint(minw, 1000);
35089         this.setYConstraint(0, 0);
35090         this.minX = x - minw;
35091         this.maxX = x + 1000;
35092         this.startPos = x;
35093         if (!this.view.mainWrap) { // this is Bootstrap code..
35094             this.getDragEl().style.display='block';
35095         }
35096         
35097         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35098     },
35099
35100
35101     handleMouseDown : function(e){
35102         ev = Roo.EventObject.setEvent(e);
35103         var t = this.fly(ev.getTarget());
35104         if(t.hasClass("x-grid-split")){
35105             this.cellIndex = this.view.getCellIndex(t.dom);
35106             this.split = t.dom;
35107             this.cm = this.grid.colModel;
35108             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35109                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35110             }
35111         }
35112     },
35113
35114     endDrag : function(e){
35115         this.view.headersDisabled = false;
35116         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35117         var diff = endX - this.startPos;
35118         // 
35119         var w = this.cm.getColumnWidth(this.cellIndex);
35120         if (!this.view.mainWrap) {
35121             w = 0;
35122         }
35123         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35124     },
35125
35126     autoOffset : function(){
35127         this.setDelta(0,0);
35128     }
35129 });/*
35130  * Based on:
35131  * Ext JS Library 1.1.1
35132  * Copyright(c) 2006-2007, Ext JS, LLC.
35133  *
35134  * Originally Released Under LGPL - original licence link has changed is not relivant.
35135  *
35136  * Fork - LGPL
35137  * <script type="text/javascript">
35138  */
35139  
35140 // private
35141 // This is a support class used internally by the Grid components
35142 Roo.grid.GridDragZone = function(grid, config){
35143     this.view = grid.getView();
35144     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35145     if(this.view.lockedBody){
35146         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35147         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35148     }
35149     this.scroll = false;
35150     this.grid = grid;
35151     this.ddel = document.createElement('div');
35152     this.ddel.className = 'x-grid-dd-wrap';
35153 };
35154
35155 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35156     ddGroup : "GridDD",
35157
35158     getDragData : function(e){
35159         var t = Roo.lib.Event.getTarget(e);
35160         var rowIndex = this.view.findRowIndex(t);
35161         var sm = this.grid.selModel;
35162             
35163         //Roo.log(rowIndex);
35164         
35165         if (sm.getSelectedCell) {
35166             // cell selection..
35167             if (!sm.getSelectedCell()) {
35168                 return false;
35169             }
35170             if (rowIndex != sm.getSelectedCell()[0]) {
35171                 return false;
35172             }
35173         
35174         }
35175         if (sm.getSelections && sm.getSelections().length < 1) {
35176             return false;
35177         }
35178         
35179         
35180         // before it used to all dragging of unseleted... - now we dont do that.
35181         if(rowIndex !== false){
35182             
35183             // if editorgrid.. 
35184             
35185             
35186             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35187                
35188             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35189               //  
35190             //}
35191             if (e.hasModifier()){
35192                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35193             }
35194             
35195             Roo.log("getDragData");
35196             
35197             return {
35198                 grid: this.grid,
35199                 ddel: this.ddel,
35200                 rowIndex: rowIndex,
35201                 selections: sm.getSelections ? sm.getSelections() : (
35202                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35203             };
35204         }
35205         return false;
35206     },
35207     
35208     
35209     onInitDrag : function(e){
35210         var data = this.dragData;
35211         this.ddel.innerHTML = this.grid.getDragDropText();
35212         this.proxy.update(this.ddel);
35213         // fire start drag?
35214     },
35215
35216     afterRepair : function(){
35217         this.dragging = false;
35218     },
35219
35220     getRepairXY : function(e, data){
35221         return false;
35222     },
35223
35224     onEndDrag : function(data, e){
35225         // fire end drag?
35226     },
35227
35228     onValidDrop : function(dd, e, id){
35229         // fire drag drop?
35230         this.hideProxy();
35231     },
35232
35233     beforeInvalidDrop : function(e, id){
35234
35235     }
35236 });/*
35237  * Based on:
35238  * Ext JS Library 1.1.1
35239  * Copyright(c) 2006-2007, Ext JS, LLC.
35240  *
35241  * Originally Released Under LGPL - original licence link has changed is not relivant.
35242  *
35243  * Fork - LGPL
35244  * <script type="text/javascript">
35245  */
35246  
35247
35248 /**
35249  * @class Roo.grid.ColumnModel
35250  * @extends Roo.util.Observable
35251  * This is the default implementation of a ColumnModel used by the Grid. It defines
35252  * the columns in the grid.
35253  * <br>Usage:<br>
35254  <pre><code>
35255  var colModel = new Roo.grid.ColumnModel([
35256         {header: "Ticker", width: 60, sortable: true, locked: true},
35257         {header: "Company Name", width: 150, sortable: true},
35258         {header: "Market Cap.", width: 100, sortable: true},
35259         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35260         {header: "Employees", width: 100, sortable: true, resizable: false}
35261  ]);
35262  </code></pre>
35263  * <p>
35264  
35265  * The config options listed for this class are options which may appear in each
35266  * individual column definition.
35267  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35268  * @constructor
35269  * @param {Object} config An Array of column config objects. See this class's
35270  * config objects for details.
35271 */
35272 Roo.grid.ColumnModel = function(config){
35273         /**
35274      * The config passed into the constructor
35275      */
35276     this.config = []; //config;
35277     this.lookup = {};
35278
35279     // if no id, create one
35280     // if the column does not have a dataIndex mapping,
35281     // map it to the order it is in the config
35282     for(var i = 0, len = config.length; i < len; i++){
35283         this.addColumn(config[i]);
35284         
35285     }
35286
35287     /**
35288      * The width of columns which have no width specified (defaults to 100)
35289      * @type Number
35290      */
35291     this.defaultWidth = 100;
35292
35293     /**
35294      * Default sortable of columns which have no sortable specified (defaults to false)
35295      * @type Boolean
35296      */
35297     this.defaultSortable = false;
35298
35299     this.addEvents({
35300         /**
35301              * @event widthchange
35302              * Fires when the width of a column changes.
35303              * @param {ColumnModel} this
35304              * @param {Number} columnIndex The column index
35305              * @param {Number} newWidth The new width
35306              */
35307             "widthchange": true,
35308         /**
35309              * @event headerchange
35310              * Fires when the text of a header changes.
35311              * @param {ColumnModel} this
35312              * @param {Number} columnIndex The column index
35313              * @param {Number} newText The new header text
35314              */
35315             "headerchange": true,
35316         /**
35317              * @event hiddenchange
35318              * Fires when a column is hidden or "unhidden".
35319              * @param {ColumnModel} this
35320              * @param {Number} columnIndex The column index
35321              * @param {Boolean} hidden true if hidden, false otherwise
35322              */
35323             "hiddenchange": true,
35324             /**
35325          * @event columnmoved
35326          * Fires when a column is moved.
35327          * @param {ColumnModel} this
35328          * @param {Number} oldIndex
35329          * @param {Number} newIndex
35330          */
35331         "columnmoved" : true,
35332         /**
35333          * @event columlockchange
35334          * Fires when a column's locked state is changed
35335          * @param {ColumnModel} this
35336          * @param {Number} colIndex
35337          * @param {Boolean} locked true if locked
35338          */
35339         "columnlockchange" : true
35340     });
35341     Roo.grid.ColumnModel.superclass.constructor.call(this);
35342 };
35343 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35344     /**
35345      * @cfg {String} header The header text to display in the Grid view.
35346      */
35347         /**
35348      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35349      */
35350         /**
35351      * @cfg {String} smHeader Header at Bootsrap Small width
35352      */
35353         /**
35354      * @cfg {String} mdHeader Header at Bootsrap Medium width
35355      */
35356         /**
35357      * @cfg {String} lgHeader Header at Bootsrap Large width
35358      */
35359         /**
35360      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35361      */
35362     /**
35363      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35364      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35365      * specified, the column's index is used as an index into the Record's data Array.
35366      */
35367     /**
35368      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35369      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35370      */
35371     /**
35372      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35373      * Defaults to the value of the {@link #defaultSortable} property.
35374      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35375      */
35376     /**
35377      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35378      */
35379     /**
35380      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35381      */
35382     /**
35383      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35384      */
35385     /**
35386      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35387      */
35388     /**
35389      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35390      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35391      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35392      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35393      */
35394        /**
35395      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35396      */
35397     /**
35398      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35399      */
35400     /**
35401      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35402      */
35403     /**
35404      * @cfg {String} cursor (Optional)
35405      */
35406     /**
35407      * @cfg {String} tooltip (Optional)
35408      */
35409     /**
35410      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35411      */
35412     /**
35413      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35414      */
35415     /**
35416      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35417      */
35418     /**
35419      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35420      */
35421         /**
35422      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35423      */
35424     /**
35425      * Returns the id of the column at the specified index.
35426      * @param {Number} index The column index
35427      * @return {String} the id
35428      */
35429     getColumnId : function(index){
35430         return this.config[index].id;
35431     },
35432
35433     /**
35434      * Returns the column for a specified id.
35435      * @param {String} id The column id
35436      * @return {Object} the column
35437      */
35438     getColumnById : function(id){
35439         return this.lookup[id];
35440     },
35441
35442     
35443     /**
35444      * Returns the column Object for a specified dataIndex.
35445      * @param {String} dataIndex The column dataIndex
35446      * @return {Object|Boolean} the column or false if not found
35447      */
35448     getColumnByDataIndex: function(dataIndex){
35449         var index = this.findColumnIndex(dataIndex);
35450         return index > -1 ? this.config[index] : false;
35451     },
35452     
35453     /**
35454      * Returns the index for a specified column id.
35455      * @param {String} id The column id
35456      * @return {Number} the index, or -1 if not found
35457      */
35458     getIndexById : function(id){
35459         for(var i = 0, len = this.config.length; i < len; i++){
35460             if(this.config[i].id == id){
35461                 return i;
35462             }
35463         }
35464         return -1;
35465     },
35466     
35467     /**
35468      * Returns the index for a specified column dataIndex.
35469      * @param {String} dataIndex The column dataIndex
35470      * @return {Number} the index, or -1 if not found
35471      */
35472     
35473     findColumnIndex : function(dataIndex){
35474         for(var i = 0, len = this.config.length; i < len; i++){
35475             if(this.config[i].dataIndex == dataIndex){
35476                 return i;
35477             }
35478         }
35479         return -1;
35480     },
35481     
35482     
35483     moveColumn : function(oldIndex, newIndex){
35484         var c = this.config[oldIndex];
35485         this.config.splice(oldIndex, 1);
35486         this.config.splice(newIndex, 0, c);
35487         this.dataMap = null;
35488         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35489     },
35490
35491     isLocked : function(colIndex){
35492         return this.config[colIndex].locked === true;
35493     },
35494
35495     setLocked : function(colIndex, value, suppressEvent){
35496         if(this.isLocked(colIndex) == value){
35497             return;
35498         }
35499         this.config[colIndex].locked = value;
35500         if(!suppressEvent){
35501             this.fireEvent("columnlockchange", this, colIndex, value);
35502         }
35503     },
35504
35505     getTotalLockedWidth : function(){
35506         var totalWidth = 0;
35507         for(var i = 0; i < this.config.length; i++){
35508             if(this.isLocked(i) && !this.isHidden(i)){
35509                 this.totalWidth += this.getColumnWidth(i);
35510             }
35511         }
35512         return totalWidth;
35513     },
35514
35515     getLockedCount : function(){
35516         for(var i = 0, len = this.config.length; i < len; i++){
35517             if(!this.isLocked(i)){
35518                 return i;
35519             }
35520         }
35521         
35522         return this.config.length;
35523     },
35524
35525     /**
35526      * Returns the number of columns.
35527      * @return {Number}
35528      */
35529     getColumnCount : function(visibleOnly){
35530         if(visibleOnly === true){
35531             var c = 0;
35532             for(var i = 0, len = this.config.length; i < len; i++){
35533                 if(!this.isHidden(i)){
35534                     c++;
35535                 }
35536             }
35537             return c;
35538         }
35539         return this.config.length;
35540     },
35541
35542     /**
35543      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35544      * @param {Function} fn
35545      * @param {Object} scope (optional)
35546      * @return {Array} result
35547      */
35548     getColumnsBy : function(fn, scope){
35549         var r = [];
35550         for(var i = 0, len = this.config.length; i < len; i++){
35551             var c = this.config[i];
35552             if(fn.call(scope||this, c, i) === true){
35553                 r[r.length] = c;
35554             }
35555         }
35556         return r;
35557     },
35558
35559     /**
35560      * Returns true if the specified column is sortable.
35561      * @param {Number} col The column index
35562      * @return {Boolean}
35563      */
35564     isSortable : function(col){
35565         if(typeof this.config[col].sortable == "undefined"){
35566             return this.defaultSortable;
35567         }
35568         return this.config[col].sortable;
35569     },
35570
35571     /**
35572      * Returns the rendering (formatting) function defined for the column.
35573      * @param {Number} col The column index.
35574      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35575      */
35576     getRenderer : function(col){
35577         if(!this.config[col].renderer){
35578             return Roo.grid.ColumnModel.defaultRenderer;
35579         }
35580         return this.config[col].renderer;
35581     },
35582
35583     /**
35584      * Sets the rendering (formatting) function for a column.
35585      * @param {Number} col The column index
35586      * @param {Function} fn The function to use to process the cell's raw data
35587      * to return HTML markup for the grid view. The render function is called with
35588      * the following parameters:<ul>
35589      * <li>Data value.</li>
35590      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35591      * <li>css A CSS style string to apply to the table cell.</li>
35592      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35593      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35594      * <li>Row index</li>
35595      * <li>Column index</li>
35596      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35597      */
35598     setRenderer : function(col, fn){
35599         this.config[col].renderer = fn;
35600     },
35601
35602     /**
35603      * Returns the width for the specified column.
35604      * @param {Number} col The column index
35605      * @param (optional) {String} gridSize bootstrap width size.
35606      * @return {Number}
35607      */
35608     getColumnWidth : function(col, gridSize)
35609         {
35610                 var cfg = this.config[col];
35611                 
35612                 if (typeof(gridSize) == 'undefined') {
35613                         return cfg.width * 1 || this.defaultWidth;
35614                 }
35615                 if (gridSize === false) { // if we set it..
35616                         return cfg.width || false;
35617                 }
35618                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35619                 
35620                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35621                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35622                                 continue;
35623                         }
35624                         return cfg[ sizes[i] ];
35625                 }
35626                 return 1;
35627                 
35628     },
35629
35630     /**
35631      * Sets the width for a column.
35632      * @param {Number} col The column index
35633      * @param {Number} width The new width
35634      */
35635     setColumnWidth : function(col, width, suppressEvent){
35636         this.config[col].width = width;
35637         this.totalWidth = null;
35638         if(!suppressEvent){
35639              this.fireEvent("widthchange", this, col, width);
35640         }
35641     },
35642
35643     /**
35644      * Returns the total width of all columns.
35645      * @param {Boolean} includeHidden True to include hidden column widths
35646      * @return {Number}
35647      */
35648     getTotalWidth : function(includeHidden){
35649         if(!this.totalWidth){
35650             this.totalWidth = 0;
35651             for(var i = 0, len = this.config.length; i < len; i++){
35652                 if(includeHidden || !this.isHidden(i)){
35653                     this.totalWidth += this.getColumnWidth(i);
35654                 }
35655             }
35656         }
35657         return this.totalWidth;
35658     },
35659
35660     /**
35661      * Returns the header for the specified column.
35662      * @param {Number} col The column index
35663      * @return {String}
35664      */
35665     getColumnHeader : function(col){
35666         return this.config[col].header;
35667     },
35668
35669     /**
35670      * Sets the header for a column.
35671      * @param {Number} col The column index
35672      * @param {String} header The new header
35673      */
35674     setColumnHeader : function(col, header){
35675         this.config[col].header = header;
35676         this.fireEvent("headerchange", this, col, header);
35677     },
35678
35679     /**
35680      * Returns the tooltip for the specified column.
35681      * @param {Number} col The column index
35682      * @return {String}
35683      */
35684     getColumnTooltip : function(col){
35685             return this.config[col].tooltip;
35686     },
35687     /**
35688      * Sets the tooltip for a column.
35689      * @param {Number} col The column index
35690      * @param {String} tooltip The new tooltip
35691      */
35692     setColumnTooltip : function(col, tooltip){
35693             this.config[col].tooltip = tooltip;
35694     },
35695
35696     /**
35697      * Returns the dataIndex for the specified column.
35698      * @param {Number} col The column index
35699      * @return {Number}
35700      */
35701     getDataIndex : function(col){
35702         return this.config[col].dataIndex;
35703     },
35704
35705     /**
35706      * Sets the dataIndex for a column.
35707      * @param {Number} col The column index
35708      * @param {Number} dataIndex The new dataIndex
35709      */
35710     setDataIndex : function(col, dataIndex){
35711         this.config[col].dataIndex = dataIndex;
35712     },
35713
35714     
35715     
35716     /**
35717      * Returns true if the cell is editable.
35718      * @param {Number} colIndex The column index
35719      * @param {Number} rowIndex The row index - this is nto actually used..?
35720      * @return {Boolean}
35721      */
35722     isCellEditable : function(colIndex, rowIndex){
35723         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35724     },
35725
35726     /**
35727      * Returns the editor defined for the cell/column.
35728      * return false or null to disable editing.
35729      * @param {Number} colIndex The column index
35730      * @param {Number} rowIndex The row index
35731      * @return {Object}
35732      */
35733     getCellEditor : function(colIndex, rowIndex){
35734         return this.config[colIndex].editor;
35735     },
35736
35737     /**
35738      * Sets if a column is editable.
35739      * @param {Number} col The column index
35740      * @param {Boolean} editable True if the column is editable
35741      */
35742     setEditable : function(col, editable){
35743         this.config[col].editable = editable;
35744     },
35745
35746
35747     /**
35748      * Returns true if the column is hidden.
35749      * @param {Number} colIndex The column index
35750      * @return {Boolean}
35751      */
35752     isHidden : function(colIndex){
35753         return this.config[colIndex].hidden;
35754     },
35755
35756
35757     /**
35758      * Returns true if the column width cannot be changed
35759      */
35760     isFixed : function(colIndex){
35761         return this.config[colIndex].fixed;
35762     },
35763
35764     /**
35765      * Returns true if the column can be resized
35766      * @return {Boolean}
35767      */
35768     isResizable : function(colIndex){
35769         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35770     },
35771     /**
35772      * Sets if a column is hidden.
35773      * @param {Number} colIndex The column index
35774      * @param {Boolean} hidden True if the column is hidden
35775      */
35776     setHidden : function(colIndex, hidden){
35777         this.config[colIndex].hidden = hidden;
35778         this.totalWidth = null;
35779         this.fireEvent("hiddenchange", this, colIndex, hidden);
35780     },
35781
35782     /**
35783      * Sets the editor for a column.
35784      * @param {Number} col The column index
35785      * @param {Object} editor The editor object
35786      */
35787     setEditor : function(col, editor){
35788         this.config[col].editor = editor;
35789     },
35790     /**
35791      * Add a column (experimental...) - defaults to adding to the end..
35792      * @param {Object} config 
35793     */
35794     addColumn : function(c)
35795     {
35796     
35797         var i = this.config.length;
35798         this.config[i] = c;
35799         
35800         if(typeof c.dataIndex == "undefined"){
35801             c.dataIndex = i;
35802         }
35803         if(typeof c.renderer == "string"){
35804             c.renderer = Roo.util.Format[c.renderer];
35805         }
35806         if(typeof c.id == "undefined"){
35807             c.id = Roo.id();
35808         }
35809         if(c.editor && c.editor.xtype){
35810             c.editor  = Roo.factory(c.editor, Roo.grid);
35811         }
35812         if(c.editor && c.editor.isFormField){
35813             c.editor = new Roo.grid.GridEditor(c.editor);
35814         }
35815         this.lookup[c.id] = c;
35816     }
35817     
35818 });
35819
35820 Roo.grid.ColumnModel.defaultRenderer = function(value)
35821 {
35822     if(typeof value == "object") {
35823         return value;
35824     }
35825         if(typeof value == "string" && value.length < 1){
35826             return "&#160;";
35827         }
35828     
35829         return String.format("{0}", value);
35830 };
35831
35832 // Alias for backwards compatibility
35833 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35834 /*
35835  * Based on:
35836  * Ext JS Library 1.1.1
35837  * Copyright(c) 2006-2007, Ext JS, LLC.
35838  *
35839  * Originally Released Under LGPL - original licence link has changed is not relivant.
35840  *
35841  * Fork - LGPL
35842  * <script type="text/javascript">
35843  */
35844
35845 /**
35846  * @class Roo.grid.AbstractSelectionModel
35847  * @extends Roo.util.Observable
35848  * @abstract
35849  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35850  * implemented by descendant classes.  This class should not be directly instantiated.
35851  * @constructor
35852  */
35853 Roo.grid.AbstractSelectionModel = function(){
35854     this.locked = false;
35855     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35856 };
35857
35858 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35859     /** @ignore Called by the grid automatically. Do not call directly. */
35860     init : function(grid){
35861         this.grid = grid;
35862         this.initEvents();
35863     },
35864
35865     /**
35866      * Locks the selections.
35867      */
35868     lock : function(){
35869         this.locked = true;
35870     },
35871
35872     /**
35873      * Unlocks the selections.
35874      */
35875     unlock : function(){
35876         this.locked = false;
35877     },
35878
35879     /**
35880      * Returns true if the selections are locked.
35881      * @return {Boolean}
35882      */
35883     isLocked : function(){
35884         return this.locked;
35885     }
35886 });/*
35887  * Based on:
35888  * Ext JS Library 1.1.1
35889  * Copyright(c) 2006-2007, Ext JS, LLC.
35890  *
35891  * Originally Released Under LGPL - original licence link has changed is not relivant.
35892  *
35893  * Fork - LGPL
35894  * <script type="text/javascript">
35895  */
35896 /**
35897  * @extends Roo.grid.AbstractSelectionModel
35898  * @class Roo.grid.RowSelectionModel
35899  * The default SelectionModel used by {@link Roo.grid.Grid}.
35900  * It supports multiple selections and keyboard selection/navigation. 
35901  * @constructor
35902  * @param {Object} config
35903  */
35904 Roo.grid.RowSelectionModel = function(config){
35905     Roo.apply(this, config);
35906     this.selections = new Roo.util.MixedCollection(false, function(o){
35907         return o.id;
35908     });
35909
35910     this.last = false;
35911     this.lastActive = false;
35912
35913     this.addEvents({
35914         /**
35915         * @event selectionchange
35916         * Fires when the selection changes
35917         * @param {SelectionModel} this
35918         */
35919        "selectionchange" : true,
35920        /**
35921         * @event afterselectionchange
35922         * Fires after the selection changes (eg. by key press or clicking)
35923         * @param {SelectionModel} this
35924         */
35925        "afterselectionchange" : true,
35926        /**
35927         * @event beforerowselect
35928         * Fires when a row is selected being selected, return false to cancel.
35929         * @param {SelectionModel} this
35930         * @param {Number} rowIndex The selected index
35931         * @param {Boolean} keepExisting False if other selections will be cleared
35932         */
35933        "beforerowselect" : true,
35934        /**
35935         * @event rowselect
35936         * Fires when a row is selected.
35937         * @param {SelectionModel} this
35938         * @param {Number} rowIndex The selected index
35939         * @param {Roo.data.Record} r The record
35940         */
35941        "rowselect" : true,
35942        /**
35943         * @event rowdeselect
35944         * Fires when a row is deselected.
35945         * @param {SelectionModel} this
35946         * @param {Number} rowIndex The selected index
35947         */
35948         "rowdeselect" : true
35949     });
35950     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35951     this.locked = false;
35952 };
35953
35954 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35955     /**
35956      * @cfg {Boolean} singleSelect
35957      * True to allow selection of only one row at a time (defaults to false)
35958      */
35959     singleSelect : false,
35960
35961     // private
35962     initEvents : function(){
35963
35964         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35965             this.grid.on("mousedown", this.handleMouseDown, this);
35966         }else{ // allow click to work like normal
35967             this.grid.on("rowclick", this.handleDragableRowClick, this);
35968         }
35969         // bootstrap does not have a view..
35970         var view = this.grid.view ? this.grid.view : this.grid;
35971         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35972             "up" : function(e){
35973                 if(!e.shiftKey){
35974                     this.selectPrevious(e.shiftKey);
35975                 }else if(this.last !== false && this.lastActive !== false){
35976                     var last = this.last;
35977                     this.selectRange(this.last,  this.lastActive-1);
35978                     view.focusRow(this.lastActive);
35979                     if(last !== false){
35980                         this.last = last;
35981                     }
35982                 }else{
35983                     this.selectFirstRow();
35984                 }
35985                 this.fireEvent("afterselectionchange", this);
35986             },
35987             "down" : function(e){
35988                 if(!e.shiftKey){
35989                     this.selectNext(e.shiftKey);
35990                 }else if(this.last !== false && this.lastActive !== false){
35991                     var last = this.last;
35992                     this.selectRange(this.last,  this.lastActive+1);
35993                     view.focusRow(this.lastActive);
35994                     if(last !== false){
35995                         this.last = last;
35996                     }
35997                 }else{
35998                     this.selectFirstRow();
35999                 }
36000                 this.fireEvent("afterselectionchange", this);
36001             },
36002             scope: this
36003         });
36004
36005          
36006         view.on("refresh", this.onRefresh, this);
36007         view.on("rowupdated", this.onRowUpdated, this);
36008         view.on("rowremoved", this.onRemove, this);
36009     },
36010
36011     // private
36012     onRefresh : function(){
36013         var ds = this.grid.ds, i, v = this.grid.view;
36014         var s = this.selections;
36015         s.each(function(r){
36016             if((i = ds.indexOfId(r.id)) != -1){
36017                 v.onRowSelect(i);
36018                 s.add(ds.getAt(i)); // updating the selection relate data
36019             }else{
36020                 s.remove(r);
36021             }
36022         });
36023     },
36024
36025     // private
36026     onRemove : function(v, index, r){
36027         this.selections.remove(r);
36028     },
36029
36030     // private
36031     onRowUpdated : function(v, index, r){
36032         if(this.isSelected(r)){
36033             v.onRowSelect(index);
36034         }
36035     },
36036
36037     /**
36038      * Select records.
36039      * @param {Array} records The records to select
36040      * @param {Boolean} keepExisting (optional) True to keep existing selections
36041      */
36042     selectRecords : function(records, keepExisting){
36043         if(!keepExisting){
36044             this.clearSelections();
36045         }
36046         var ds = this.grid.ds;
36047         for(var i = 0, len = records.length; i < len; i++){
36048             this.selectRow(ds.indexOf(records[i]), true);
36049         }
36050     },
36051
36052     /**
36053      * Gets the number of selected rows.
36054      * @return {Number}
36055      */
36056     getCount : function(){
36057         return this.selections.length;
36058     },
36059
36060     /**
36061      * Selects the first row in the grid.
36062      */
36063     selectFirstRow : function(){
36064         this.selectRow(0);
36065     },
36066
36067     /**
36068      * Select the last row.
36069      * @param {Boolean} keepExisting (optional) True to keep existing selections
36070      */
36071     selectLastRow : function(keepExisting){
36072         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36073     },
36074
36075     /**
36076      * Selects the row immediately following the last selected row.
36077      * @param {Boolean} keepExisting (optional) True to keep existing selections
36078      */
36079     selectNext : function(keepExisting){
36080         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36081             this.selectRow(this.last+1, keepExisting);
36082             var view = this.grid.view ? this.grid.view : this.grid;
36083             view.focusRow(this.last);
36084         }
36085     },
36086
36087     /**
36088      * Selects the row that precedes the last selected row.
36089      * @param {Boolean} keepExisting (optional) True to keep existing selections
36090      */
36091     selectPrevious : function(keepExisting){
36092         if(this.last){
36093             this.selectRow(this.last-1, keepExisting);
36094             var view = this.grid.view ? this.grid.view : this.grid;
36095             view.focusRow(this.last);
36096         }
36097     },
36098
36099     /**
36100      * Returns the selected records
36101      * @return {Array} Array of selected records
36102      */
36103     getSelections : function(){
36104         return [].concat(this.selections.items);
36105     },
36106
36107     /**
36108      * Returns the first selected record.
36109      * @return {Record}
36110      */
36111     getSelected : function(){
36112         return this.selections.itemAt(0);
36113     },
36114
36115
36116     /**
36117      * Clears all selections.
36118      */
36119     clearSelections : function(fast){
36120         if(this.locked) {
36121             return;
36122         }
36123         if(fast !== true){
36124             var ds = this.grid.ds;
36125             var s = this.selections;
36126             s.each(function(r){
36127                 this.deselectRow(ds.indexOfId(r.id));
36128             }, this);
36129             s.clear();
36130         }else{
36131             this.selections.clear();
36132         }
36133         this.last = false;
36134     },
36135
36136
36137     /**
36138      * Selects all rows.
36139      */
36140     selectAll : function(){
36141         if(this.locked) {
36142             return;
36143         }
36144         this.selections.clear();
36145         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36146             this.selectRow(i, true);
36147         }
36148     },
36149
36150     /**
36151      * Returns True if there is a selection.
36152      * @return {Boolean}
36153      */
36154     hasSelection : function(){
36155         return this.selections.length > 0;
36156     },
36157
36158     /**
36159      * Returns True if the specified row is selected.
36160      * @param {Number/Record} record The record or index of the record to check
36161      * @return {Boolean}
36162      */
36163     isSelected : function(index){
36164         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36165         return (r && this.selections.key(r.id) ? true : false);
36166     },
36167
36168     /**
36169      * Returns True if the specified record id is selected.
36170      * @param {String} id The id of record to check
36171      * @return {Boolean}
36172      */
36173     isIdSelected : function(id){
36174         return (this.selections.key(id) ? true : false);
36175     },
36176
36177     // private
36178     handleMouseDown : function(e, t)
36179     {
36180         var view = this.grid.view ? this.grid.view : this.grid;
36181         var rowIndex;
36182         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36183             return;
36184         };
36185         if(e.shiftKey && this.last !== false){
36186             var last = this.last;
36187             this.selectRange(last, rowIndex, e.ctrlKey);
36188             this.last = last; // reset the last
36189             view.focusRow(rowIndex);
36190         }else{
36191             var isSelected = this.isSelected(rowIndex);
36192             if(e.button !== 0 && isSelected){
36193                 view.focusRow(rowIndex);
36194             }else if(e.ctrlKey && isSelected){
36195                 this.deselectRow(rowIndex);
36196             }else if(!isSelected){
36197                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36198                 view.focusRow(rowIndex);
36199             }
36200         }
36201         this.fireEvent("afterselectionchange", this);
36202     },
36203     // private
36204     handleDragableRowClick :  function(grid, rowIndex, e) 
36205     {
36206         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36207             this.selectRow(rowIndex, false);
36208             var view = this.grid.view ? this.grid.view : this.grid;
36209             view.focusRow(rowIndex);
36210              this.fireEvent("afterselectionchange", this);
36211         }
36212     },
36213     
36214     /**
36215      * Selects multiple rows.
36216      * @param {Array} rows Array of the indexes of the row to select
36217      * @param {Boolean} keepExisting (optional) True to keep existing selections
36218      */
36219     selectRows : function(rows, keepExisting){
36220         if(!keepExisting){
36221             this.clearSelections();
36222         }
36223         for(var i = 0, len = rows.length; i < len; i++){
36224             this.selectRow(rows[i], true);
36225         }
36226     },
36227
36228     /**
36229      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36230      * @param {Number} startRow The index of the first row in the range
36231      * @param {Number} endRow The index of the last row in the range
36232      * @param {Boolean} keepExisting (optional) True to retain existing selections
36233      */
36234     selectRange : function(startRow, endRow, keepExisting){
36235         if(this.locked) {
36236             return;
36237         }
36238         if(!keepExisting){
36239             this.clearSelections();
36240         }
36241         if(startRow <= endRow){
36242             for(var i = startRow; i <= endRow; i++){
36243                 this.selectRow(i, true);
36244             }
36245         }else{
36246             for(var i = startRow; i >= endRow; i--){
36247                 this.selectRow(i, true);
36248             }
36249         }
36250     },
36251
36252     /**
36253      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36254      * @param {Number} startRow The index of the first row in the range
36255      * @param {Number} endRow The index of the last row in the range
36256      */
36257     deselectRange : function(startRow, endRow, preventViewNotify){
36258         if(this.locked) {
36259             return;
36260         }
36261         for(var i = startRow; i <= endRow; i++){
36262             this.deselectRow(i, preventViewNotify);
36263         }
36264     },
36265
36266     /**
36267      * Selects a row.
36268      * @param {Number} row The index of the row to select
36269      * @param {Boolean} keepExisting (optional) True to keep existing selections
36270      */
36271     selectRow : function(index, keepExisting, preventViewNotify){
36272         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36273             return;
36274         }
36275         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36276             if(!keepExisting || this.singleSelect){
36277                 this.clearSelections();
36278             }
36279             var r = this.grid.ds.getAt(index);
36280             this.selections.add(r);
36281             this.last = this.lastActive = index;
36282             if(!preventViewNotify){
36283                 var view = this.grid.view ? this.grid.view : this.grid;
36284                 view.onRowSelect(index);
36285             }
36286             this.fireEvent("rowselect", this, index, r);
36287             this.fireEvent("selectionchange", this);
36288         }
36289     },
36290
36291     /**
36292      * Deselects a row.
36293      * @param {Number} row The index of the row to deselect
36294      */
36295     deselectRow : function(index, preventViewNotify){
36296         if(this.locked) {
36297             return;
36298         }
36299         if(this.last == index){
36300             this.last = false;
36301         }
36302         if(this.lastActive == index){
36303             this.lastActive = false;
36304         }
36305         var r = this.grid.ds.getAt(index);
36306         this.selections.remove(r);
36307         if(!preventViewNotify){
36308             var view = this.grid.view ? this.grid.view : this.grid;
36309             view.onRowDeselect(index);
36310         }
36311         this.fireEvent("rowdeselect", this, index);
36312         this.fireEvent("selectionchange", this);
36313     },
36314
36315     // private
36316     restoreLast : function(){
36317         if(this._last){
36318             this.last = this._last;
36319         }
36320     },
36321
36322     // private
36323     acceptsNav : function(row, col, cm){
36324         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36325     },
36326
36327     // private
36328     onEditorKey : function(field, e){
36329         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36330         if(k == e.TAB){
36331             e.stopEvent();
36332             ed.completeEdit();
36333             if(e.shiftKey){
36334                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36335             }else{
36336                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36337             }
36338         }else if(k == e.ENTER && !e.ctrlKey){
36339             e.stopEvent();
36340             ed.completeEdit();
36341             if(e.shiftKey){
36342                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36343             }else{
36344                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36345             }
36346         }else if(k == e.ESC){
36347             ed.cancelEdit();
36348         }
36349         if(newCell){
36350             g.startEditing(newCell[0], newCell[1]);
36351         }
36352     }
36353 });/*
36354  * Based on:
36355  * Ext JS Library 1.1.1
36356  * Copyright(c) 2006-2007, Ext JS, LLC.
36357  *
36358  * Originally Released Under LGPL - original licence link has changed is not relivant.
36359  *
36360  * Fork - LGPL
36361  * <script type="text/javascript">
36362  */
36363 /**
36364  * @class Roo.grid.CellSelectionModel
36365  * @extends Roo.grid.AbstractSelectionModel
36366  * This class provides the basic implementation for cell selection in a grid.
36367  * @constructor
36368  * @param {Object} config The object containing the configuration of this model.
36369  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36370  */
36371 Roo.grid.CellSelectionModel = function(config){
36372     Roo.apply(this, config);
36373
36374     this.selection = null;
36375
36376     this.addEvents({
36377         /**
36378              * @event beforerowselect
36379              * Fires before a cell is selected.
36380              * @param {SelectionModel} this
36381              * @param {Number} rowIndex The selected row index
36382              * @param {Number} colIndex The selected cell index
36383              */
36384             "beforecellselect" : true,
36385         /**
36386              * @event cellselect
36387              * Fires when a cell is selected.
36388              * @param {SelectionModel} this
36389              * @param {Number} rowIndex The selected row index
36390              * @param {Number} colIndex The selected cell index
36391              */
36392             "cellselect" : true,
36393         /**
36394              * @event selectionchange
36395              * Fires when the active selection changes.
36396              * @param {SelectionModel} this
36397              * @param {Object} selection null for no selection or an object (o) with two properties
36398                 <ul>
36399                 <li>o.record: the record object for the row the selection is in</li>
36400                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36401                 </ul>
36402              */
36403             "selectionchange" : true,
36404         /**
36405              * @event tabend
36406              * Fires when the tab (or enter) was pressed on the last editable cell
36407              * You can use this to trigger add new row.
36408              * @param {SelectionModel} this
36409              */
36410             "tabend" : true,
36411          /**
36412              * @event beforeeditnext
36413              * Fires before the next editable sell is made active
36414              * You can use this to skip to another cell or fire the tabend
36415              *    if you set cell to false
36416              * @param {Object} eventdata object : { cell : [ row, col ] } 
36417              */
36418             "beforeeditnext" : true
36419     });
36420     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36421 };
36422
36423 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36424     
36425     enter_is_tab: false,
36426
36427     /** @ignore */
36428     initEvents : function(){
36429         this.grid.on("mousedown", this.handleMouseDown, this);
36430         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36431         var view = this.grid.view;
36432         view.on("refresh", this.onViewChange, this);
36433         view.on("rowupdated", this.onRowUpdated, this);
36434         view.on("beforerowremoved", this.clearSelections, this);
36435         view.on("beforerowsinserted", this.clearSelections, this);
36436         if(this.grid.isEditor){
36437             this.grid.on("beforeedit", this.beforeEdit,  this);
36438         }
36439     },
36440
36441         //private
36442     beforeEdit : function(e){
36443         this.select(e.row, e.column, false, true, e.record);
36444     },
36445
36446         //private
36447     onRowUpdated : function(v, index, r){
36448         if(this.selection && this.selection.record == r){
36449             v.onCellSelect(index, this.selection.cell[1]);
36450         }
36451     },
36452
36453         //private
36454     onViewChange : function(){
36455         this.clearSelections(true);
36456     },
36457
36458         /**
36459          * Returns the currently selected cell,.
36460          * @return {Array} The selected cell (row, column) or null if none selected.
36461          */
36462     getSelectedCell : function(){
36463         return this.selection ? this.selection.cell : null;
36464     },
36465
36466     /**
36467      * Clears all selections.
36468      * @param {Boolean} true to prevent the gridview from being notified about the change.
36469      */
36470     clearSelections : function(preventNotify){
36471         var s = this.selection;
36472         if(s){
36473             if(preventNotify !== true){
36474                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36475             }
36476             this.selection = null;
36477             this.fireEvent("selectionchange", this, null);
36478         }
36479     },
36480
36481     /**
36482      * Returns true if there is a selection.
36483      * @return {Boolean}
36484      */
36485     hasSelection : function(){
36486         return this.selection ? true : false;
36487     },
36488
36489     /** @ignore */
36490     handleMouseDown : function(e, t){
36491         var v = this.grid.getView();
36492         if(this.isLocked()){
36493             return;
36494         };
36495         var row = v.findRowIndex(t);
36496         var cell = v.findCellIndex(t);
36497         if(row !== false && cell !== false){
36498             this.select(row, cell);
36499         }
36500     },
36501
36502     /**
36503      * Selects a cell.
36504      * @param {Number} rowIndex
36505      * @param {Number} collIndex
36506      */
36507     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36508         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36509             this.clearSelections();
36510             r = r || this.grid.dataSource.getAt(rowIndex);
36511             this.selection = {
36512                 record : r,
36513                 cell : [rowIndex, colIndex]
36514             };
36515             if(!preventViewNotify){
36516                 var v = this.grid.getView();
36517                 v.onCellSelect(rowIndex, colIndex);
36518                 if(preventFocus !== true){
36519                     v.focusCell(rowIndex, colIndex);
36520                 }
36521             }
36522             this.fireEvent("cellselect", this, rowIndex, colIndex);
36523             this.fireEvent("selectionchange", this, this.selection);
36524         }
36525     },
36526
36527         //private
36528     isSelectable : function(rowIndex, colIndex, cm){
36529         return !cm.isHidden(colIndex);
36530     },
36531
36532     /** @ignore */
36533     handleKeyDown : function(e){
36534         //Roo.log('Cell Sel Model handleKeyDown');
36535         if(!e.isNavKeyPress()){
36536             return;
36537         }
36538         var g = this.grid, s = this.selection;
36539         if(!s){
36540             e.stopEvent();
36541             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36542             if(cell){
36543                 this.select(cell[0], cell[1]);
36544             }
36545             return;
36546         }
36547         var sm = this;
36548         var walk = function(row, col, step){
36549             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36550         };
36551         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36552         var newCell;
36553
36554       
36555
36556         switch(k){
36557             case e.TAB:
36558                 // handled by onEditorKey
36559                 if (g.isEditor && g.editing) {
36560                     return;
36561                 }
36562                 if(e.shiftKey) {
36563                     newCell = walk(r, c-1, -1);
36564                 } else {
36565                     newCell = walk(r, c+1, 1);
36566                 }
36567                 break;
36568             
36569             case e.DOWN:
36570                newCell = walk(r+1, c, 1);
36571                 break;
36572             
36573             case e.UP:
36574                 newCell = walk(r-1, c, -1);
36575                 break;
36576             
36577             case e.RIGHT:
36578                 newCell = walk(r, c+1, 1);
36579                 break;
36580             
36581             case e.LEFT:
36582                 newCell = walk(r, c-1, -1);
36583                 break;
36584             
36585             case e.ENTER:
36586                 
36587                 if(g.isEditor && !g.editing){
36588                    g.startEditing(r, c);
36589                    e.stopEvent();
36590                    return;
36591                 }
36592                 
36593                 
36594              break;
36595         };
36596         if(newCell){
36597             this.select(newCell[0], newCell[1]);
36598             e.stopEvent();
36599             
36600         }
36601     },
36602
36603     acceptsNav : function(row, col, cm){
36604         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36605     },
36606     /**
36607      * Selects a cell.
36608      * @param {Number} field (not used) - as it's normally used as a listener
36609      * @param {Number} e - event - fake it by using
36610      *
36611      * var e = Roo.EventObjectImpl.prototype;
36612      * e.keyCode = e.TAB
36613      *
36614      * 
36615      */
36616     onEditorKey : function(field, e){
36617         
36618         var k = e.getKey(),
36619             newCell,
36620             g = this.grid,
36621             ed = g.activeEditor,
36622             forward = false;
36623         ///Roo.log('onEditorKey' + k);
36624         
36625         
36626         if (this.enter_is_tab && k == e.ENTER) {
36627             k = e.TAB;
36628         }
36629         
36630         if(k == e.TAB){
36631             if(e.shiftKey){
36632                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36633             }else{
36634                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36635                 forward = true;
36636             }
36637             
36638             e.stopEvent();
36639             
36640         } else if(k == e.ENTER &&  !e.ctrlKey){
36641             ed.completeEdit();
36642             e.stopEvent();
36643             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36644         
36645                 } else if(k == e.ESC){
36646             ed.cancelEdit();
36647         }
36648                 
36649         if (newCell) {
36650             var ecall = { cell : newCell, forward : forward };
36651             this.fireEvent('beforeeditnext', ecall );
36652             newCell = ecall.cell;
36653                         forward = ecall.forward;
36654         }
36655                 
36656         if(newCell){
36657             //Roo.log('next cell after edit');
36658             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36659         } else if (forward) {
36660             // tabbed past last
36661             this.fireEvent.defer(100, this, ['tabend',this]);
36662         }
36663     }
36664 });/*
36665  * Based on:
36666  * Ext JS Library 1.1.1
36667  * Copyright(c) 2006-2007, Ext JS, LLC.
36668  *
36669  * Originally Released Under LGPL - original licence link has changed is not relivant.
36670  *
36671  * Fork - LGPL
36672  * <script type="text/javascript">
36673  */
36674  
36675 /**
36676  * @class Roo.grid.EditorGrid
36677  * @extends Roo.grid.Grid
36678  * Class for creating and editable grid.
36679  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36680  * The container MUST have some type of size defined for the grid to fill. The container will be 
36681  * automatically set to position relative if it isn't already.
36682  * @param {Object} dataSource The data model to bind to
36683  * @param {Object} colModel The column model with info about this grid's columns
36684  */
36685 Roo.grid.EditorGrid = function(container, config){
36686     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36687     this.getGridEl().addClass("xedit-grid");
36688
36689     if(!this.selModel){
36690         this.selModel = new Roo.grid.CellSelectionModel();
36691     }
36692
36693     this.activeEditor = null;
36694
36695         this.addEvents({
36696             /**
36697              * @event beforeedit
36698              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36699              * <ul style="padding:5px;padding-left:16px;">
36700              * <li>grid - This grid</li>
36701              * <li>record - The record being edited</li>
36702              * <li>field - The field name being edited</li>
36703              * <li>value - The value for the field being edited.</li>
36704              * <li>row - The grid row index</li>
36705              * <li>column - The grid column index</li>
36706              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36707              * </ul>
36708              * @param {Object} e An edit event (see above for description)
36709              */
36710             "beforeedit" : true,
36711             /**
36712              * @event afteredit
36713              * Fires after a cell is edited. <br />
36714              * <ul style="padding:5px;padding-left:16px;">
36715              * <li>grid - This grid</li>
36716              * <li>record - The record being edited</li>
36717              * <li>field - The field name being edited</li>
36718              * <li>value - The value being set</li>
36719              * <li>originalValue - The original value for the field, before the edit.</li>
36720              * <li>row - The grid row index</li>
36721              * <li>column - The grid column index</li>
36722              * </ul>
36723              * @param {Object} e An edit event (see above for description)
36724              */
36725             "afteredit" : true,
36726             /**
36727              * @event validateedit
36728              * Fires after a cell is edited, but before the value is set in the record. 
36729          * You can use this to modify the value being set in the field, Return false
36730              * to cancel the change. The edit event object has the following properties <br />
36731              * <ul style="padding:5px;padding-left:16px;">
36732          * <li>editor - This editor</li>
36733              * <li>grid - This grid</li>
36734              * <li>record - The record being edited</li>
36735              * <li>field - The field name being edited</li>
36736              * <li>value - The value being set</li>
36737              * <li>originalValue - The original value for the field, before the edit.</li>
36738              * <li>row - The grid row index</li>
36739              * <li>column - The grid column index</li>
36740              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36741              * </ul>
36742              * @param {Object} e An edit event (see above for description)
36743              */
36744             "validateedit" : true
36745         });
36746     this.on("bodyscroll", this.stopEditing,  this);
36747     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36748 };
36749
36750 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36751     /**
36752      * @cfg {Number} clicksToEdit
36753      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36754      */
36755     clicksToEdit: 2,
36756
36757     // private
36758     isEditor : true,
36759     // private
36760     trackMouseOver: false, // causes very odd FF errors
36761
36762     onCellDblClick : function(g, row, col){
36763         this.startEditing(row, col);
36764     },
36765
36766     onEditComplete : function(ed, value, startValue){
36767         this.editing = false;
36768         this.activeEditor = null;
36769         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36770         var r = ed.record;
36771         var field = this.colModel.getDataIndex(ed.col);
36772         var e = {
36773             grid: this,
36774             record: r,
36775             field: field,
36776             originalValue: startValue,
36777             value: value,
36778             row: ed.row,
36779             column: ed.col,
36780             cancel:false,
36781             editor: ed
36782         };
36783         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36784         cell.show();
36785           
36786         if(String(value) !== String(startValue)){
36787             
36788             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36789                 r.set(field, e.value);
36790                 // if we are dealing with a combo box..
36791                 // then we also set the 'name' colum to be the displayField
36792                 if (ed.field.displayField && ed.field.name) {
36793                     r.set(ed.field.name, ed.field.el.dom.value);
36794                 }
36795                 
36796                 delete e.cancel; //?? why!!!
36797                 this.fireEvent("afteredit", e);
36798             }
36799         } else {
36800             this.fireEvent("afteredit", e); // always fire it!
36801         }
36802         this.view.focusCell(ed.row, ed.col);
36803     },
36804
36805     /**
36806      * Starts editing the specified for the specified row/column
36807      * @param {Number} rowIndex
36808      * @param {Number} colIndex
36809      */
36810     startEditing : function(row, col){
36811         this.stopEditing();
36812         if(this.colModel.isCellEditable(col, row)){
36813             this.view.ensureVisible(row, col, true);
36814           
36815             var r = this.dataSource.getAt(row);
36816             var field = this.colModel.getDataIndex(col);
36817             var cell = Roo.get(this.view.getCell(row,col));
36818             var e = {
36819                 grid: this,
36820                 record: r,
36821                 field: field,
36822                 value: r.data[field],
36823                 row: row,
36824                 column: col,
36825                 cancel:false 
36826             };
36827             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36828                 this.editing = true;
36829                 var ed = this.colModel.getCellEditor(col, row);
36830                 
36831                 if (!ed) {
36832                     return;
36833                 }
36834                 if(!ed.rendered){
36835                     ed.render(ed.parentEl || document.body);
36836                 }
36837                 ed.field.reset();
36838                
36839                 cell.hide();
36840                 
36841                 (function(){ // complex but required for focus issues in safari, ie and opera
36842                     ed.row = row;
36843                     ed.col = col;
36844                     ed.record = r;
36845                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36846                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36847                     this.activeEditor = ed;
36848                     var v = r.data[field];
36849                     ed.startEdit(this.view.getCell(row, col), v);
36850                     // combo's with 'displayField and name set
36851                     if (ed.field.displayField && ed.field.name) {
36852                         ed.field.el.dom.value = r.data[ed.field.name];
36853                     }
36854                     
36855                     
36856                 }).defer(50, this);
36857             }
36858         }
36859     },
36860         
36861     /**
36862      * Stops any active editing
36863      */
36864     stopEditing : function(){
36865         if(this.activeEditor){
36866             this.activeEditor.completeEdit();
36867         }
36868         this.activeEditor = null;
36869     },
36870         
36871          /**
36872      * Called to get grid's drag proxy text, by default returns this.ddText.
36873      * @return {String}
36874      */
36875     getDragDropText : function(){
36876         var count = this.selModel.getSelectedCell() ? 1 : 0;
36877         return String.format(this.ddText, count, count == 1 ? '' : 's');
36878     }
36879         
36880 });/*
36881  * Based on:
36882  * Ext JS Library 1.1.1
36883  * Copyright(c) 2006-2007, Ext JS, LLC.
36884  *
36885  * Originally Released Under LGPL - original licence link has changed is not relivant.
36886  *
36887  * Fork - LGPL
36888  * <script type="text/javascript">
36889  */
36890
36891 // private - not really -- you end up using it !
36892 // This is a support class used internally by the Grid components
36893
36894 /**
36895  * @class Roo.grid.GridEditor
36896  * @extends Roo.Editor
36897  * Class for creating and editable grid elements.
36898  * @param {Object} config any settings (must include field)
36899  */
36900 Roo.grid.GridEditor = function(field, config){
36901     if (!config && field.field) {
36902         config = field;
36903         field = Roo.factory(config.field, Roo.form);
36904     }
36905     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36906     field.monitorTab = false;
36907 };
36908
36909 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36910     
36911     /**
36912      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36913      */
36914     
36915     alignment: "tl-tl",
36916     autoSize: "width",
36917     hideEl : false,
36918     cls: "x-small-editor x-grid-editor",
36919     shim:false,
36920     shadow:"frame"
36921 });/*
36922  * Based on:
36923  * Ext JS Library 1.1.1
36924  * Copyright(c) 2006-2007, Ext JS, LLC.
36925  *
36926  * Originally Released Under LGPL - original licence link has changed is not relivant.
36927  *
36928  * Fork - LGPL
36929  * <script type="text/javascript">
36930  */
36931   
36932
36933   
36934 Roo.grid.PropertyRecord = Roo.data.Record.create([
36935     {name:'name',type:'string'},  'value'
36936 ]);
36937
36938
36939 Roo.grid.PropertyStore = function(grid, source){
36940     this.grid = grid;
36941     this.store = new Roo.data.Store({
36942         recordType : Roo.grid.PropertyRecord
36943     });
36944     this.store.on('update', this.onUpdate,  this);
36945     if(source){
36946         this.setSource(source);
36947     }
36948     Roo.grid.PropertyStore.superclass.constructor.call(this);
36949 };
36950
36951
36952
36953 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36954     setSource : function(o){
36955         this.source = o;
36956         this.store.removeAll();
36957         var data = [];
36958         for(var k in o){
36959             if(this.isEditableValue(o[k])){
36960                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36961             }
36962         }
36963         this.store.loadRecords({records: data}, {}, true);
36964     },
36965
36966     onUpdate : function(ds, record, type){
36967         if(type == Roo.data.Record.EDIT){
36968             var v = record.data['value'];
36969             var oldValue = record.modified['value'];
36970             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36971                 this.source[record.id] = v;
36972                 record.commit();
36973                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36974             }else{
36975                 record.reject();
36976             }
36977         }
36978     },
36979
36980     getProperty : function(row){
36981        return this.store.getAt(row);
36982     },
36983
36984     isEditableValue: function(val){
36985         if(val && val instanceof Date){
36986             return true;
36987         }else if(typeof val == 'object' || typeof val == 'function'){
36988             return false;
36989         }
36990         return true;
36991     },
36992
36993     setValue : function(prop, value){
36994         this.source[prop] = value;
36995         this.store.getById(prop).set('value', value);
36996     },
36997
36998     getSource : function(){
36999         return this.source;
37000     }
37001 });
37002
37003 Roo.grid.PropertyColumnModel = function(grid, store){
37004     this.grid = grid;
37005     var g = Roo.grid;
37006     g.PropertyColumnModel.superclass.constructor.call(this, [
37007         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37008         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37009     ]);
37010     this.store = store;
37011     this.bselect = Roo.DomHelper.append(document.body, {
37012         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37013             {tag: 'option', value: 'true', html: 'true'},
37014             {tag: 'option', value: 'false', html: 'false'}
37015         ]
37016     });
37017     Roo.id(this.bselect);
37018     var f = Roo.form;
37019     this.editors = {
37020         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37021         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37022         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37023         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37024         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37025     };
37026     this.renderCellDelegate = this.renderCell.createDelegate(this);
37027     this.renderPropDelegate = this.renderProp.createDelegate(this);
37028 };
37029
37030 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37031     
37032     
37033     nameText : 'Name',
37034     valueText : 'Value',
37035     
37036     dateFormat : 'm/j/Y',
37037     
37038     
37039     renderDate : function(dateVal){
37040         return dateVal.dateFormat(this.dateFormat);
37041     },
37042
37043     renderBool : function(bVal){
37044         return bVal ? 'true' : 'false';
37045     },
37046
37047     isCellEditable : function(colIndex, rowIndex){
37048         return colIndex == 1;
37049     },
37050
37051     getRenderer : function(col){
37052         return col == 1 ?
37053             this.renderCellDelegate : this.renderPropDelegate;
37054     },
37055
37056     renderProp : function(v){
37057         return this.getPropertyName(v);
37058     },
37059
37060     renderCell : function(val){
37061         var rv = val;
37062         if(val instanceof Date){
37063             rv = this.renderDate(val);
37064         }else if(typeof val == 'boolean'){
37065             rv = this.renderBool(val);
37066         }
37067         return Roo.util.Format.htmlEncode(rv);
37068     },
37069
37070     getPropertyName : function(name){
37071         var pn = this.grid.propertyNames;
37072         return pn && pn[name] ? pn[name] : name;
37073     },
37074
37075     getCellEditor : function(colIndex, rowIndex){
37076         var p = this.store.getProperty(rowIndex);
37077         var n = p.data['name'], val = p.data['value'];
37078         
37079         if(typeof(this.grid.customEditors[n]) == 'string'){
37080             return this.editors[this.grid.customEditors[n]];
37081         }
37082         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37083             return this.grid.customEditors[n];
37084         }
37085         if(val instanceof Date){
37086             return this.editors['date'];
37087         }else if(typeof val == 'number'){
37088             return this.editors['number'];
37089         }else if(typeof val == 'boolean'){
37090             return this.editors['boolean'];
37091         }else{
37092             return this.editors['string'];
37093         }
37094     }
37095 });
37096
37097 /**
37098  * @class Roo.grid.PropertyGrid
37099  * @extends Roo.grid.EditorGrid
37100  * This class represents the  interface of a component based property grid control.
37101  * <br><br>Usage:<pre><code>
37102  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37103       
37104  });
37105  // set any options
37106  grid.render();
37107  * </code></pre>
37108   
37109  * @constructor
37110  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37111  * The container MUST have some type of size defined for the grid to fill. The container will be
37112  * automatically set to position relative if it isn't already.
37113  * @param {Object} config A config object that sets properties on this grid.
37114  */
37115 Roo.grid.PropertyGrid = function(container, config){
37116     config = config || {};
37117     var store = new Roo.grid.PropertyStore(this);
37118     this.store = store;
37119     var cm = new Roo.grid.PropertyColumnModel(this, store);
37120     store.store.sort('name', 'ASC');
37121     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37122         ds: store.store,
37123         cm: cm,
37124         enableColLock:false,
37125         enableColumnMove:false,
37126         stripeRows:false,
37127         trackMouseOver: false,
37128         clicksToEdit:1
37129     }, config));
37130     this.getGridEl().addClass('x-props-grid');
37131     this.lastEditRow = null;
37132     this.on('columnresize', this.onColumnResize, this);
37133     this.addEvents({
37134          /**
37135              * @event beforepropertychange
37136              * Fires before a property changes (return false to stop?)
37137              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37138              * @param {String} id Record Id
37139              * @param {String} newval New Value
37140          * @param {String} oldval Old Value
37141              */
37142         "beforepropertychange": true,
37143         /**
37144              * @event propertychange
37145              * Fires after a property changes
37146              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37147              * @param {String} id Record Id
37148              * @param {String} newval New Value
37149          * @param {String} oldval Old Value
37150              */
37151         "propertychange": true
37152     });
37153     this.customEditors = this.customEditors || {};
37154 };
37155 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37156     
37157      /**
37158      * @cfg {Object} customEditors map of colnames=> custom editors.
37159      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37160      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37161      * false disables editing of the field.
37162          */
37163     
37164       /**
37165      * @cfg {Object} propertyNames map of property Names to their displayed value
37166          */
37167     
37168     render : function(){
37169         Roo.grid.PropertyGrid.superclass.render.call(this);
37170         this.autoSize.defer(100, this);
37171     },
37172
37173     autoSize : function(){
37174         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37175         if(this.view){
37176             this.view.fitColumns();
37177         }
37178     },
37179
37180     onColumnResize : function(){
37181         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37182         this.autoSize();
37183     },
37184     /**
37185      * Sets the data for the Grid
37186      * accepts a Key => Value object of all the elements avaiable.
37187      * @param {Object} data  to appear in grid.
37188      */
37189     setSource : function(source){
37190         this.store.setSource(source);
37191         //this.autoSize();
37192     },
37193     /**
37194      * Gets all the data from the grid.
37195      * @return {Object} data  data stored in grid
37196      */
37197     getSource : function(){
37198         return this.store.getSource();
37199     }
37200 });/*
37201   
37202  * Licence LGPL
37203  
37204  */
37205  
37206 /**
37207  * @class Roo.grid.Calendar
37208  * @extends Roo.grid.Grid
37209  * This class extends the Grid to provide a calendar widget
37210  * <br><br>Usage:<pre><code>
37211  var grid = new Roo.grid.Calendar("my-container-id", {
37212      ds: myDataStore,
37213      cm: myColModel,
37214      selModel: mySelectionModel,
37215      autoSizeColumns: true,
37216      monitorWindowResize: false,
37217      trackMouseOver: true
37218      eventstore : real data store..
37219  });
37220  // set any options
37221  grid.render();
37222   
37223   * @constructor
37224  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37225  * The container MUST have some type of size defined for the grid to fill. The container will be
37226  * automatically set to position relative if it isn't already.
37227  * @param {Object} config A config object that sets properties on this grid.
37228  */
37229 Roo.grid.Calendar = function(container, config){
37230         // initialize the container
37231         this.container = Roo.get(container);
37232         this.container.update("");
37233         this.container.setStyle("overflow", "hidden");
37234     this.container.addClass('x-grid-container');
37235
37236     this.id = this.container.id;
37237
37238     Roo.apply(this, config);
37239     // check and correct shorthanded configs
37240     
37241     var rows = [];
37242     var d =1;
37243     for (var r = 0;r < 6;r++) {
37244         
37245         rows[r]=[];
37246         for (var c =0;c < 7;c++) {
37247             rows[r][c]= '';
37248         }
37249     }
37250     if (this.eventStore) {
37251         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37252         this.eventStore.on('load',this.onLoad, this);
37253         this.eventStore.on('beforeload',this.clearEvents, this);
37254          
37255     }
37256     
37257     this.dataSource = new Roo.data.Store({
37258             proxy: new Roo.data.MemoryProxy(rows),
37259             reader: new Roo.data.ArrayReader({}, [
37260                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37261     });
37262
37263     this.dataSource.load();
37264     this.ds = this.dataSource;
37265     this.ds.xmodule = this.xmodule || false;
37266     
37267     
37268     var cellRender = function(v,x,r)
37269     {
37270         return String.format(
37271             '<div class="fc-day  fc-widget-content"><div>' +
37272                 '<div class="fc-event-container"></div>' +
37273                 '<div class="fc-day-number">{0}</div>'+
37274                 
37275                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37276             '</div></div>', v);
37277     
37278     }
37279     
37280     
37281     this.colModel = new Roo.grid.ColumnModel( [
37282         {
37283             xtype: 'ColumnModel',
37284             xns: Roo.grid,
37285             dataIndex : 'weekday0',
37286             header : 'Sunday',
37287             renderer : cellRender
37288         },
37289         {
37290             xtype: 'ColumnModel',
37291             xns: Roo.grid,
37292             dataIndex : 'weekday1',
37293             header : 'Monday',
37294             renderer : cellRender
37295         },
37296         {
37297             xtype: 'ColumnModel',
37298             xns: Roo.grid,
37299             dataIndex : 'weekday2',
37300             header : 'Tuesday',
37301             renderer : cellRender
37302         },
37303         {
37304             xtype: 'ColumnModel',
37305             xns: Roo.grid,
37306             dataIndex : 'weekday3',
37307             header : 'Wednesday',
37308             renderer : cellRender
37309         },
37310         {
37311             xtype: 'ColumnModel',
37312             xns: Roo.grid,
37313             dataIndex : 'weekday4',
37314             header : 'Thursday',
37315             renderer : cellRender
37316         },
37317         {
37318             xtype: 'ColumnModel',
37319             xns: Roo.grid,
37320             dataIndex : 'weekday5',
37321             header : 'Friday',
37322             renderer : cellRender
37323         },
37324         {
37325             xtype: 'ColumnModel',
37326             xns: Roo.grid,
37327             dataIndex : 'weekday6',
37328             header : 'Saturday',
37329             renderer : cellRender
37330         }
37331     ]);
37332     this.cm = this.colModel;
37333     this.cm.xmodule = this.xmodule || false;
37334  
37335         
37336           
37337     //this.selModel = new Roo.grid.CellSelectionModel();
37338     //this.sm = this.selModel;
37339     //this.selModel.init(this);
37340     
37341     
37342     if(this.width){
37343         this.container.setWidth(this.width);
37344     }
37345
37346     if(this.height){
37347         this.container.setHeight(this.height);
37348     }
37349     /** @private */
37350         this.addEvents({
37351         // raw events
37352         /**
37353          * @event click
37354          * The raw click event for the entire grid.
37355          * @param {Roo.EventObject} e
37356          */
37357         "click" : true,
37358         /**
37359          * @event dblclick
37360          * The raw dblclick event for the entire grid.
37361          * @param {Roo.EventObject} e
37362          */
37363         "dblclick" : true,
37364         /**
37365          * @event contextmenu
37366          * The raw contextmenu event for the entire grid.
37367          * @param {Roo.EventObject} e
37368          */
37369         "contextmenu" : true,
37370         /**
37371          * @event mousedown
37372          * The raw mousedown event for the entire grid.
37373          * @param {Roo.EventObject} e
37374          */
37375         "mousedown" : true,
37376         /**
37377          * @event mouseup
37378          * The raw mouseup event for the entire grid.
37379          * @param {Roo.EventObject} e
37380          */
37381         "mouseup" : true,
37382         /**
37383          * @event mouseover
37384          * The raw mouseover event for the entire grid.
37385          * @param {Roo.EventObject} e
37386          */
37387         "mouseover" : true,
37388         /**
37389          * @event mouseout
37390          * The raw mouseout event for the entire grid.
37391          * @param {Roo.EventObject} e
37392          */
37393         "mouseout" : true,
37394         /**
37395          * @event keypress
37396          * The raw keypress event for the entire grid.
37397          * @param {Roo.EventObject} e
37398          */
37399         "keypress" : true,
37400         /**
37401          * @event keydown
37402          * The raw keydown event for the entire grid.
37403          * @param {Roo.EventObject} e
37404          */
37405         "keydown" : true,
37406
37407         // custom events
37408
37409         /**
37410          * @event cellclick
37411          * Fires when a cell is clicked
37412          * @param {Grid} this
37413          * @param {Number} rowIndex
37414          * @param {Number} columnIndex
37415          * @param {Roo.EventObject} e
37416          */
37417         "cellclick" : true,
37418         /**
37419          * @event celldblclick
37420          * Fires when a cell is double clicked
37421          * @param {Grid} this
37422          * @param {Number} rowIndex
37423          * @param {Number} columnIndex
37424          * @param {Roo.EventObject} e
37425          */
37426         "celldblclick" : true,
37427         /**
37428          * @event rowclick
37429          * Fires when a row is clicked
37430          * @param {Grid} this
37431          * @param {Number} rowIndex
37432          * @param {Roo.EventObject} e
37433          */
37434         "rowclick" : true,
37435         /**
37436          * @event rowdblclick
37437          * Fires when a row is double clicked
37438          * @param {Grid} this
37439          * @param {Number} rowIndex
37440          * @param {Roo.EventObject} e
37441          */
37442         "rowdblclick" : true,
37443         /**
37444          * @event headerclick
37445          * Fires when a header is clicked
37446          * @param {Grid} this
37447          * @param {Number} columnIndex
37448          * @param {Roo.EventObject} e
37449          */
37450         "headerclick" : true,
37451         /**
37452          * @event headerdblclick
37453          * Fires when a header cell is double clicked
37454          * @param {Grid} this
37455          * @param {Number} columnIndex
37456          * @param {Roo.EventObject} e
37457          */
37458         "headerdblclick" : true,
37459         /**
37460          * @event rowcontextmenu
37461          * Fires when a row is right clicked
37462          * @param {Grid} this
37463          * @param {Number} rowIndex
37464          * @param {Roo.EventObject} e
37465          */
37466         "rowcontextmenu" : true,
37467         /**
37468          * @event cellcontextmenu
37469          * Fires when a cell is right clicked
37470          * @param {Grid} this
37471          * @param {Number} rowIndex
37472          * @param {Number} cellIndex
37473          * @param {Roo.EventObject} e
37474          */
37475          "cellcontextmenu" : true,
37476         /**
37477          * @event headercontextmenu
37478          * Fires when a header is right clicked
37479          * @param {Grid} this
37480          * @param {Number} columnIndex
37481          * @param {Roo.EventObject} e
37482          */
37483         "headercontextmenu" : true,
37484         /**
37485          * @event bodyscroll
37486          * Fires when the body element is scrolled
37487          * @param {Number} scrollLeft
37488          * @param {Number} scrollTop
37489          */
37490         "bodyscroll" : true,
37491         /**
37492          * @event columnresize
37493          * Fires when the user resizes a column
37494          * @param {Number} columnIndex
37495          * @param {Number} newSize
37496          */
37497         "columnresize" : true,
37498         /**
37499          * @event columnmove
37500          * Fires when the user moves a column
37501          * @param {Number} oldIndex
37502          * @param {Number} newIndex
37503          */
37504         "columnmove" : true,
37505         /**
37506          * @event startdrag
37507          * Fires when row(s) start being dragged
37508          * @param {Grid} this
37509          * @param {Roo.GridDD} dd The drag drop object
37510          * @param {event} e The raw browser event
37511          */
37512         "startdrag" : true,
37513         /**
37514          * @event enddrag
37515          * Fires when a drag operation is complete
37516          * @param {Grid} this
37517          * @param {Roo.GridDD} dd The drag drop object
37518          * @param {event} e The raw browser event
37519          */
37520         "enddrag" : true,
37521         /**
37522          * @event dragdrop
37523          * Fires when dragged row(s) are dropped on a valid DD target
37524          * @param {Grid} this
37525          * @param {Roo.GridDD} dd The drag drop object
37526          * @param {String} targetId The target drag drop object
37527          * @param {event} e The raw browser event
37528          */
37529         "dragdrop" : true,
37530         /**
37531          * @event dragover
37532          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37533          * @param {Grid} this
37534          * @param {Roo.GridDD} dd The drag drop object
37535          * @param {String} targetId The target drag drop object
37536          * @param {event} e The raw browser event
37537          */
37538         "dragover" : true,
37539         /**
37540          * @event dragenter
37541          *  Fires when the dragged row(s) first cross another DD target while being dragged
37542          * @param {Grid} this
37543          * @param {Roo.GridDD} dd The drag drop object
37544          * @param {String} targetId The target drag drop object
37545          * @param {event} e The raw browser event
37546          */
37547         "dragenter" : true,
37548         /**
37549          * @event dragout
37550          * Fires when the dragged row(s) leave another DD target while being dragged
37551          * @param {Grid} this
37552          * @param {Roo.GridDD} dd The drag drop object
37553          * @param {String} targetId The target drag drop object
37554          * @param {event} e The raw browser event
37555          */
37556         "dragout" : true,
37557         /**
37558          * @event rowclass
37559          * Fires when a row is rendered, so you can change add a style to it.
37560          * @param {GridView} gridview   The grid view
37561          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37562          */
37563         'rowclass' : true,
37564
37565         /**
37566          * @event render
37567          * Fires when the grid is rendered
37568          * @param {Grid} grid
37569          */
37570         'render' : true,
37571             /**
37572              * @event select
37573              * Fires when a date is selected
37574              * @param {DatePicker} this
37575              * @param {Date} date The selected date
37576              */
37577         'select': true,
37578         /**
37579              * @event monthchange
37580              * Fires when the displayed month changes 
37581              * @param {DatePicker} this
37582              * @param {Date} date The selected month
37583              */
37584         'monthchange': true,
37585         /**
37586              * @event evententer
37587              * Fires when mouse over an event
37588              * @param {Calendar} this
37589              * @param {event} Event
37590              */
37591         'evententer': true,
37592         /**
37593              * @event eventleave
37594              * Fires when the mouse leaves an
37595              * @param {Calendar} this
37596              * @param {event}
37597              */
37598         'eventleave': true,
37599         /**
37600              * @event eventclick
37601              * Fires when the mouse click an
37602              * @param {Calendar} this
37603              * @param {event}
37604              */
37605         'eventclick': true,
37606         /**
37607              * @event eventrender
37608              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37609              * @param {Calendar} this
37610              * @param {data} data to be modified
37611              */
37612         'eventrender': true
37613         
37614     });
37615
37616     Roo.grid.Grid.superclass.constructor.call(this);
37617     this.on('render', function() {
37618         this.view.el.addClass('x-grid-cal'); 
37619         
37620         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37621
37622     },this);
37623     
37624     if (!Roo.grid.Calendar.style) {
37625         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37626             
37627             
37628             '.x-grid-cal .x-grid-col' :  {
37629                 height: 'auto !important',
37630                 'vertical-align': 'top'
37631             },
37632             '.x-grid-cal  .fc-event-hori' : {
37633                 height: '14px'
37634             }
37635              
37636             
37637         }, Roo.id());
37638     }
37639
37640     
37641     
37642 };
37643 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37644     /**
37645      * @cfg {Store} eventStore The store that loads events.
37646      */
37647     eventStore : 25,
37648
37649      
37650     activeDate : false,
37651     startDay : 0,
37652     autoWidth : true,
37653     monitorWindowResize : false,
37654
37655     
37656     resizeColumns : function() {
37657         var col = (this.view.el.getWidth() / 7) - 3;
37658         // loop through cols, and setWidth
37659         for(var i =0 ; i < 7 ; i++){
37660             this.cm.setColumnWidth(i, col);
37661         }
37662     },
37663      setDate :function(date) {
37664         
37665         Roo.log('setDate?');
37666         
37667         this.resizeColumns();
37668         var vd = this.activeDate;
37669         this.activeDate = date;
37670 //        if(vd && this.el){
37671 //            var t = date.getTime();
37672 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37673 //                Roo.log('using add remove');
37674 //                
37675 //                this.fireEvent('monthchange', this, date);
37676 //                
37677 //                this.cells.removeClass("fc-state-highlight");
37678 //                this.cells.each(function(c){
37679 //                   if(c.dateValue == t){
37680 //                       c.addClass("fc-state-highlight");
37681 //                       setTimeout(function(){
37682 //                            try{c.dom.firstChild.focus();}catch(e){}
37683 //                       }, 50);
37684 //                       return false;
37685 //                   }
37686 //                   return true;
37687 //                });
37688 //                return;
37689 //            }
37690 //        }
37691         
37692         var days = date.getDaysInMonth();
37693         
37694         var firstOfMonth = date.getFirstDateOfMonth();
37695         var startingPos = firstOfMonth.getDay()-this.startDay;
37696         
37697         if(startingPos < this.startDay){
37698             startingPos += 7;
37699         }
37700         
37701         var pm = date.add(Date.MONTH, -1);
37702         var prevStart = pm.getDaysInMonth()-startingPos;
37703 //        
37704         
37705         
37706         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37707         
37708         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37709         //this.cells.addClassOnOver('fc-state-hover');
37710         
37711         var cells = this.cells.elements;
37712         var textEls = this.textNodes;
37713         
37714         //Roo.each(cells, function(cell){
37715         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37716         //});
37717         
37718         days += startingPos;
37719
37720         // convert everything to numbers so it's fast
37721         var day = 86400000;
37722         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37723         //Roo.log(d);
37724         //Roo.log(pm);
37725         //Roo.log(prevStart);
37726         
37727         var today = new Date().clearTime().getTime();
37728         var sel = date.clearTime().getTime();
37729         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37730         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37731         var ddMatch = this.disabledDatesRE;
37732         var ddText = this.disabledDatesText;
37733         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37734         var ddaysText = this.disabledDaysText;
37735         var format = this.format;
37736         
37737         var setCellClass = function(cal, cell){
37738             
37739             //Roo.log('set Cell Class');
37740             cell.title = "";
37741             var t = d.getTime();
37742             
37743             //Roo.log(d);
37744             
37745             
37746             cell.dateValue = t;
37747             if(t == today){
37748                 cell.className += " fc-today";
37749                 cell.className += " fc-state-highlight";
37750                 cell.title = cal.todayText;
37751             }
37752             if(t == sel){
37753                 // disable highlight in other month..
37754                 cell.className += " fc-state-highlight";
37755                 
37756             }
37757             // disabling
37758             if(t < min) {
37759                 //cell.className = " fc-state-disabled";
37760                 cell.title = cal.minText;
37761                 return;
37762             }
37763             if(t > max) {
37764                 //cell.className = " fc-state-disabled";
37765                 cell.title = cal.maxText;
37766                 return;
37767             }
37768             if(ddays){
37769                 if(ddays.indexOf(d.getDay()) != -1){
37770                     // cell.title = ddaysText;
37771                    // cell.className = " fc-state-disabled";
37772                 }
37773             }
37774             if(ddMatch && format){
37775                 var fvalue = d.dateFormat(format);
37776                 if(ddMatch.test(fvalue)){
37777                     cell.title = ddText.replace("%0", fvalue);
37778                    cell.className = " fc-state-disabled";
37779                 }
37780             }
37781             
37782             if (!cell.initialClassName) {
37783                 cell.initialClassName = cell.dom.className;
37784             }
37785             
37786             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37787         };
37788
37789         var i = 0;
37790         
37791         for(; i < startingPos; i++) {
37792             cells[i].dayName =  (++prevStart);
37793             Roo.log(textEls[i]);
37794             d.setDate(d.getDate()+1);
37795             
37796             //cells[i].className = "fc-past fc-other-month";
37797             setCellClass(this, cells[i]);
37798         }
37799         
37800         var intDay = 0;
37801         
37802         for(; i < days; i++){
37803             intDay = i - startingPos + 1;
37804             cells[i].dayName =  (intDay);
37805             d.setDate(d.getDate()+1);
37806             
37807             cells[i].className = ''; // "x-date-active";
37808             setCellClass(this, cells[i]);
37809         }
37810         var extraDays = 0;
37811         
37812         for(; i < 42; i++) {
37813             //textEls[i].innerHTML = (++extraDays);
37814             
37815             d.setDate(d.getDate()+1);
37816             cells[i].dayName = (++extraDays);
37817             cells[i].className = "fc-future fc-other-month";
37818             setCellClass(this, cells[i]);
37819         }
37820         
37821         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37822         
37823         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37824         
37825         // this will cause all the cells to mis
37826         var rows= [];
37827         var i =0;
37828         for (var r = 0;r < 6;r++) {
37829             for (var c =0;c < 7;c++) {
37830                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37831             }    
37832         }
37833         
37834         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37835         for(i=0;i<cells.length;i++) {
37836             
37837             this.cells.elements[i].dayName = cells[i].dayName ;
37838             this.cells.elements[i].className = cells[i].className;
37839             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37840             this.cells.elements[i].title = cells[i].title ;
37841             this.cells.elements[i].dateValue = cells[i].dateValue ;
37842         }
37843         
37844         
37845         
37846         
37847         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37848         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37849         
37850         ////if(totalRows != 6){
37851             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37852            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37853        // }
37854         
37855         this.fireEvent('monthchange', this, date);
37856         
37857         
37858     },
37859  /**
37860      * Returns the grid's SelectionModel.
37861      * @return {SelectionModel}
37862      */
37863     getSelectionModel : function(){
37864         if(!this.selModel){
37865             this.selModel = new Roo.grid.CellSelectionModel();
37866         }
37867         return this.selModel;
37868     },
37869
37870     load: function() {
37871         this.eventStore.load()
37872         
37873         
37874         
37875     },
37876     
37877     findCell : function(dt) {
37878         dt = dt.clearTime().getTime();
37879         var ret = false;
37880         this.cells.each(function(c){
37881             //Roo.log("check " +c.dateValue + '?=' + dt);
37882             if(c.dateValue == dt){
37883                 ret = c;
37884                 return false;
37885             }
37886             return true;
37887         });
37888         
37889         return ret;
37890     },
37891     
37892     findCells : function(rec) {
37893         var s = rec.data.start_dt.clone().clearTime().getTime();
37894        // Roo.log(s);
37895         var e= rec.data.end_dt.clone().clearTime().getTime();
37896        // Roo.log(e);
37897         var ret = [];
37898         this.cells.each(function(c){
37899              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37900             
37901             if(c.dateValue > e){
37902                 return ;
37903             }
37904             if(c.dateValue < s){
37905                 return ;
37906             }
37907             ret.push(c);
37908         });
37909         
37910         return ret;    
37911     },
37912     
37913     findBestRow: function(cells)
37914     {
37915         var ret = 0;
37916         
37917         for (var i =0 ; i < cells.length;i++) {
37918             ret  = Math.max(cells[i].rows || 0,ret);
37919         }
37920         return ret;
37921         
37922     },
37923     
37924     
37925     addItem : function(rec)
37926     {
37927         // look for vertical location slot in
37928         var cells = this.findCells(rec);
37929         
37930         rec.row = this.findBestRow(cells);
37931         
37932         // work out the location.
37933         
37934         var crow = false;
37935         var rows = [];
37936         for(var i =0; i < cells.length; i++) {
37937             if (!crow) {
37938                 crow = {
37939                     start : cells[i],
37940                     end :  cells[i]
37941                 };
37942                 continue;
37943             }
37944             if (crow.start.getY() == cells[i].getY()) {
37945                 // on same row.
37946                 crow.end = cells[i];
37947                 continue;
37948             }
37949             // different row.
37950             rows.push(crow);
37951             crow = {
37952                 start: cells[i],
37953                 end : cells[i]
37954             };
37955             
37956         }
37957         
37958         rows.push(crow);
37959         rec.els = [];
37960         rec.rows = rows;
37961         rec.cells = cells;
37962         for (var i = 0; i < cells.length;i++) {
37963             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37964             
37965         }
37966         
37967         
37968     },
37969     
37970     clearEvents: function() {
37971         
37972         if (!this.eventStore.getCount()) {
37973             return;
37974         }
37975         // reset number of rows in cells.
37976         Roo.each(this.cells.elements, function(c){
37977             c.rows = 0;
37978         });
37979         
37980         this.eventStore.each(function(e) {
37981             this.clearEvent(e);
37982         },this);
37983         
37984     },
37985     
37986     clearEvent : function(ev)
37987     {
37988         if (ev.els) {
37989             Roo.each(ev.els, function(el) {
37990                 el.un('mouseenter' ,this.onEventEnter, this);
37991                 el.un('mouseleave' ,this.onEventLeave, this);
37992                 el.remove();
37993             },this);
37994             ev.els = [];
37995         }
37996     },
37997     
37998     
37999     renderEvent : function(ev,ctr) {
38000         if (!ctr) {
38001              ctr = this.view.el.select('.fc-event-container',true).first();
38002         }
38003         
38004          
38005         this.clearEvent(ev);
38006             //code
38007        
38008         
38009         
38010         ev.els = [];
38011         var cells = ev.cells;
38012         var rows = ev.rows;
38013         this.fireEvent('eventrender', this, ev);
38014         
38015         for(var i =0; i < rows.length; i++) {
38016             
38017             cls = '';
38018             if (i == 0) {
38019                 cls += ' fc-event-start';
38020             }
38021             if ((i+1) == rows.length) {
38022                 cls += ' fc-event-end';
38023             }
38024             
38025             //Roo.log(ev.data);
38026             // how many rows should it span..
38027             var cg = this.eventTmpl.append(ctr,Roo.apply({
38028                 fccls : cls
38029                 
38030             }, ev.data) , true);
38031             
38032             
38033             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38034             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38035             cg.on('click', this.onEventClick, this, ev);
38036             
38037             ev.els.push(cg);
38038             
38039             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38040             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38041             //Roo.log(cg);
38042              
38043             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38044             cg.setWidth(ebox.right - sbox.x -2);
38045         }
38046     },
38047     
38048     renderEvents: function()
38049     {   
38050         // first make sure there is enough space..
38051         
38052         if (!this.eventTmpl) {
38053             this.eventTmpl = new Roo.Template(
38054                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38055                     '<div class="fc-event-inner">' +
38056                         '<span class="fc-event-time">{time}</span>' +
38057                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38058                     '</div>' +
38059                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38060                 '</div>'
38061             );
38062                 
38063         }
38064                
38065         
38066         
38067         this.cells.each(function(c) {
38068             //Roo.log(c.select('.fc-day-content div',true).first());
38069             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38070         });
38071         
38072         var ctr = this.view.el.select('.fc-event-container',true).first();
38073         
38074         var cls;
38075         this.eventStore.each(function(ev){
38076             
38077             this.renderEvent(ev);
38078              
38079              
38080         }, this);
38081         this.view.layout();
38082         
38083     },
38084     
38085     onEventEnter: function (e, el,event,d) {
38086         this.fireEvent('evententer', this, el, event);
38087     },
38088     
38089     onEventLeave: function (e, el,event,d) {
38090         this.fireEvent('eventleave', this, el, event);
38091     },
38092     
38093     onEventClick: function (e, el,event,d) {
38094         this.fireEvent('eventclick', this, el, event);
38095     },
38096     
38097     onMonthChange: function () {
38098         this.store.load();
38099     },
38100     
38101     onLoad: function () {
38102         
38103         //Roo.log('calendar onload');
38104 //         
38105         if(this.eventStore.getCount() > 0){
38106             
38107            
38108             
38109             this.eventStore.each(function(d){
38110                 
38111                 
38112                 // FIXME..
38113                 var add =   d.data;
38114                 if (typeof(add.end_dt) == 'undefined')  {
38115                     Roo.log("Missing End time in calendar data: ");
38116                     Roo.log(d);
38117                     return;
38118                 }
38119                 if (typeof(add.start_dt) == 'undefined')  {
38120                     Roo.log("Missing Start time in calendar data: ");
38121                     Roo.log(d);
38122                     return;
38123                 }
38124                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38125                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38126                 add.id = add.id || d.id;
38127                 add.title = add.title || '??';
38128                 
38129                 this.addItem(d);
38130                 
38131              
38132             },this);
38133         }
38134         
38135         this.renderEvents();
38136     }
38137     
38138
38139 });
38140 /*
38141  grid : {
38142                 xtype: 'Grid',
38143                 xns: Roo.grid,
38144                 listeners : {
38145                     render : function ()
38146                     {
38147                         _this.grid = this;
38148                         
38149                         if (!this.view.el.hasClass('course-timesheet')) {
38150                             this.view.el.addClass('course-timesheet');
38151                         }
38152                         if (this.tsStyle) {
38153                             this.ds.load({});
38154                             return; 
38155                         }
38156                         Roo.log('width');
38157                         Roo.log(_this.grid.view.el.getWidth());
38158                         
38159                         
38160                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38161                             '.course-timesheet .x-grid-row' : {
38162                                 height: '80px'
38163                             },
38164                             '.x-grid-row td' : {
38165                                 'vertical-align' : 0
38166                             },
38167                             '.course-edit-link' : {
38168                                 'color' : 'blue',
38169                                 'text-overflow' : 'ellipsis',
38170                                 'overflow' : 'hidden',
38171                                 'white-space' : 'nowrap',
38172                                 'cursor' : 'pointer'
38173                             },
38174                             '.sub-link' : {
38175                                 'color' : 'green'
38176                             },
38177                             '.de-act-sup-link' : {
38178                                 'color' : 'purple',
38179                                 'text-decoration' : 'line-through'
38180                             },
38181                             '.de-act-link' : {
38182                                 'color' : 'red',
38183                                 'text-decoration' : 'line-through'
38184                             },
38185                             '.course-timesheet .course-highlight' : {
38186                                 'border-top-style': 'dashed !important',
38187                                 'border-bottom-bottom': 'dashed !important'
38188                             },
38189                             '.course-timesheet .course-item' : {
38190                                 'font-family'   : 'tahoma, arial, helvetica',
38191                                 'font-size'     : '11px',
38192                                 'overflow'      : 'hidden',
38193                                 'padding-left'  : '10px',
38194                                 'padding-right' : '10px',
38195                                 'padding-top' : '10px' 
38196                             }
38197                             
38198                         }, Roo.id());
38199                                 this.ds.load({});
38200                     }
38201                 },
38202                 autoWidth : true,
38203                 monitorWindowResize : false,
38204                 cellrenderer : function(v,x,r)
38205                 {
38206                     return v;
38207                 },
38208                 sm : {
38209                     xtype: 'CellSelectionModel',
38210                     xns: Roo.grid
38211                 },
38212                 dataSource : {
38213                     xtype: 'Store',
38214                     xns: Roo.data,
38215                     listeners : {
38216                         beforeload : function (_self, options)
38217                         {
38218                             options.params = options.params || {};
38219                             options.params._month = _this.monthField.getValue();
38220                             options.params.limit = 9999;
38221                             options.params['sort'] = 'when_dt';    
38222                             options.params['dir'] = 'ASC';    
38223                             this.proxy.loadResponse = this.loadResponse;
38224                             Roo.log("load?");
38225                             //this.addColumns();
38226                         },
38227                         load : function (_self, records, options)
38228                         {
38229                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38230                                 // if you click on the translation.. you can edit it...
38231                                 var el = Roo.get(this);
38232                                 var id = el.dom.getAttribute('data-id');
38233                                 var d = el.dom.getAttribute('data-date');
38234                                 var t = el.dom.getAttribute('data-time');
38235                                 //var id = this.child('span').dom.textContent;
38236                                 
38237                                 //Roo.log(this);
38238                                 Pman.Dialog.CourseCalendar.show({
38239                                     id : id,
38240                                     when_d : d,
38241                                     when_t : t,
38242                                     productitem_active : id ? 1 : 0
38243                                 }, function() {
38244                                     _this.grid.ds.load({});
38245                                 });
38246                            
38247                            });
38248                            
38249                            _this.panel.fireEvent('resize', [ '', '' ]);
38250                         }
38251                     },
38252                     loadResponse : function(o, success, response){
38253                             // this is overridden on before load..
38254                             
38255                             Roo.log("our code?");       
38256                             //Roo.log(success);
38257                             //Roo.log(response)
38258                             delete this.activeRequest;
38259                             if(!success){
38260                                 this.fireEvent("loadexception", this, o, response);
38261                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38262                                 return;
38263                             }
38264                             var result;
38265                             try {
38266                                 result = o.reader.read(response);
38267                             }catch(e){
38268                                 Roo.log("load exception?");
38269                                 this.fireEvent("loadexception", this, o, response, e);
38270                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38271                                 return;
38272                             }
38273                             Roo.log("ready...");        
38274                             // loop through result.records;
38275                             // and set this.tdate[date] = [] << array of records..
38276                             _this.tdata  = {};
38277                             Roo.each(result.records, function(r){
38278                                 //Roo.log(r.data);
38279                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38280                                     _this.tdata[r.data.when_dt.format('j')] = [];
38281                                 }
38282                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38283                             });
38284                             
38285                             //Roo.log(_this.tdata);
38286                             
38287                             result.records = [];
38288                             result.totalRecords = 6;
38289                     
38290                             // let's generate some duumy records for the rows.
38291                             //var st = _this.dateField.getValue();
38292                             
38293                             // work out monday..
38294                             //st = st.add(Date.DAY, -1 * st.format('w'));
38295                             
38296                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38297                             
38298                             var firstOfMonth = date.getFirstDayOfMonth();
38299                             var days = date.getDaysInMonth();
38300                             var d = 1;
38301                             var firstAdded = false;
38302                             for (var i = 0; i < result.totalRecords ; i++) {
38303                                 //var d= st.add(Date.DAY, i);
38304                                 var row = {};
38305                                 var added = 0;
38306                                 for(var w = 0 ; w < 7 ; w++){
38307                                     if(!firstAdded && firstOfMonth != w){
38308                                         continue;
38309                                     }
38310                                     if(d > days){
38311                                         continue;
38312                                     }
38313                                     firstAdded = true;
38314                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38315                                     row['weekday'+w] = String.format(
38316                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38317                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38318                                                     d,
38319                                                     date.format('Y-m-')+dd
38320                                                 );
38321                                     added++;
38322                                     if(typeof(_this.tdata[d]) != 'undefined'){
38323                                         Roo.each(_this.tdata[d], function(r){
38324                                             var is_sub = '';
38325                                             var deactive = '';
38326                                             var id = r.id;
38327                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38328                                             if(r.parent_id*1>0){
38329                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38330                                                 id = r.parent_id;
38331                                             }
38332                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38333                                                 deactive = 'de-act-link';
38334                                             }
38335                                             
38336                                             row['weekday'+w] += String.format(
38337                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38338                                                     id, //0
38339                                                     r.product_id_name, //1
38340                                                     r.when_dt.format('h:ia'), //2
38341                                                     is_sub, //3
38342                                                     deactive, //4
38343                                                     desc // 5
38344                                             );
38345                                         });
38346                                     }
38347                                     d++;
38348                                 }
38349                                 
38350                                 // only do this if something added..
38351                                 if(added > 0){ 
38352                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38353                                 }
38354                                 
38355                                 
38356                                 // push it twice. (second one with an hour..
38357                                 
38358                             }
38359                             //Roo.log(result);
38360                             this.fireEvent("load", this, o, o.request.arg);
38361                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38362                         },
38363                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38364                     proxy : {
38365                         xtype: 'HttpProxy',
38366                         xns: Roo.data,
38367                         method : 'GET',
38368                         url : baseURL + '/Roo/Shop_course.php'
38369                     },
38370                     reader : {
38371                         xtype: 'JsonReader',
38372                         xns: Roo.data,
38373                         id : 'id',
38374                         fields : [
38375                             {
38376                                 'name': 'id',
38377                                 'type': 'int'
38378                             },
38379                             {
38380                                 'name': 'when_dt',
38381                                 'type': 'string'
38382                             },
38383                             {
38384                                 'name': 'end_dt',
38385                                 'type': 'string'
38386                             },
38387                             {
38388                                 'name': 'parent_id',
38389                                 'type': 'int'
38390                             },
38391                             {
38392                                 'name': 'product_id',
38393                                 'type': 'int'
38394                             },
38395                             {
38396                                 'name': 'productitem_id',
38397                                 'type': 'int'
38398                             },
38399                             {
38400                                 'name': 'guid',
38401                                 'type': 'int'
38402                             }
38403                         ]
38404                     }
38405                 },
38406                 toolbar : {
38407                     xtype: 'Toolbar',
38408                     xns: Roo,
38409                     items : [
38410                         {
38411                             xtype: 'Button',
38412                             xns: Roo.Toolbar,
38413                             listeners : {
38414                                 click : function (_self, e)
38415                                 {
38416                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38417                                     sd.setMonth(sd.getMonth()-1);
38418                                     _this.monthField.setValue(sd.format('Y-m-d'));
38419                                     _this.grid.ds.load({});
38420                                 }
38421                             },
38422                             text : "Back"
38423                         },
38424                         {
38425                             xtype: 'Separator',
38426                             xns: Roo.Toolbar
38427                         },
38428                         {
38429                             xtype: 'MonthField',
38430                             xns: Roo.form,
38431                             listeners : {
38432                                 render : function (_self)
38433                                 {
38434                                     _this.monthField = _self;
38435                                    // _this.monthField.set  today
38436                                 },
38437                                 select : function (combo, date)
38438                                 {
38439                                     _this.grid.ds.load({});
38440                                 }
38441                             },
38442                             value : (function() { return new Date(); })()
38443                         },
38444                         {
38445                             xtype: 'Separator',
38446                             xns: Roo.Toolbar
38447                         },
38448                         {
38449                             xtype: 'TextItem',
38450                             xns: Roo.Toolbar,
38451                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38452                         },
38453                         {
38454                             xtype: 'Fill',
38455                             xns: Roo.Toolbar
38456                         },
38457                         {
38458                             xtype: 'Button',
38459                             xns: Roo.Toolbar,
38460                             listeners : {
38461                                 click : function (_self, e)
38462                                 {
38463                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38464                                     sd.setMonth(sd.getMonth()+1);
38465                                     _this.monthField.setValue(sd.format('Y-m-d'));
38466                                     _this.grid.ds.load({});
38467                                 }
38468                             },
38469                             text : "Next"
38470                         }
38471                     ]
38472                 },
38473                  
38474             }
38475         };
38476         
38477         *//*
38478  * Based on:
38479  * Ext JS Library 1.1.1
38480  * Copyright(c) 2006-2007, Ext JS, LLC.
38481  *
38482  * Originally Released Under LGPL - original licence link has changed is not relivant.
38483  *
38484  * Fork - LGPL
38485  * <script type="text/javascript">
38486  */
38487  
38488 /**
38489  * @class Roo.LoadMask
38490  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38491  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38492  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38493  * element's UpdateManager load indicator and will be destroyed after the initial load.
38494  * @constructor
38495  * Create a new LoadMask
38496  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38497  * @param {Object} config The config object
38498  */
38499 Roo.LoadMask = function(el, config){
38500     this.el = Roo.get(el);
38501     Roo.apply(this, config);
38502     if(this.store){
38503         this.store.on('beforeload', this.onBeforeLoad, this);
38504         this.store.on('load', this.onLoad, this);
38505         this.store.on('loadexception', this.onLoadException, this);
38506         this.removeMask = false;
38507     }else{
38508         var um = this.el.getUpdateManager();
38509         um.showLoadIndicator = false; // disable the default indicator
38510         um.on('beforeupdate', this.onBeforeLoad, this);
38511         um.on('update', this.onLoad, this);
38512         um.on('failure', this.onLoad, this);
38513         this.removeMask = true;
38514     }
38515 };
38516
38517 Roo.LoadMask.prototype = {
38518     /**
38519      * @cfg {Boolean} removeMask
38520      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38521      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38522      */
38523     removeMask : false,
38524     /**
38525      * @cfg {String} msg
38526      * The text to display in a centered loading message box (defaults to 'Loading...')
38527      */
38528     msg : 'Loading...',
38529     /**
38530      * @cfg {String} msgCls
38531      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38532      */
38533     msgCls : 'x-mask-loading',
38534
38535     /**
38536      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38537      * @type Boolean
38538      */
38539     disabled: false,
38540
38541     /**
38542      * Disables the mask to prevent it from being displayed
38543      */
38544     disable : function(){
38545        this.disabled = true;
38546     },
38547
38548     /**
38549      * Enables the mask so that it can be displayed
38550      */
38551     enable : function(){
38552         this.disabled = false;
38553     },
38554     
38555     onLoadException : function()
38556     {
38557         Roo.log(arguments);
38558         
38559         if (typeof(arguments[3]) != 'undefined') {
38560             Roo.MessageBox.alert("Error loading",arguments[3]);
38561         } 
38562         /*
38563         try {
38564             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38565                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38566             }   
38567         } catch(e) {
38568             
38569         }
38570         */
38571     
38572         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38573     },
38574     // private
38575     onLoad : function()
38576     {
38577         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38578     },
38579
38580     // private
38581     onBeforeLoad : function(){
38582         if(!this.disabled){
38583             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38584         }
38585     },
38586
38587     // private
38588     destroy : function(){
38589         if(this.store){
38590             this.store.un('beforeload', this.onBeforeLoad, this);
38591             this.store.un('load', this.onLoad, this);
38592             this.store.un('loadexception', this.onLoadException, this);
38593         }else{
38594             var um = this.el.getUpdateManager();
38595             um.un('beforeupdate', this.onBeforeLoad, this);
38596             um.un('update', this.onLoad, this);
38597             um.un('failure', this.onLoad, this);
38598         }
38599     }
38600 };/*
38601  * Based on:
38602  * Ext JS Library 1.1.1
38603  * Copyright(c) 2006-2007, Ext JS, LLC.
38604  *
38605  * Originally Released Under LGPL - original licence link has changed is not relivant.
38606  *
38607  * Fork - LGPL
38608  * <script type="text/javascript">
38609  */
38610
38611
38612 /**
38613  * @class Roo.XTemplate
38614  * @extends Roo.Template
38615  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38616 <pre><code>
38617 var t = new Roo.XTemplate(
38618         '&lt;select name="{name}"&gt;',
38619                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38620         '&lt;/select&gt;'
38621 );
38622  
38623 // then append, applying the master template values
38624  </code></pre>
38625  *
38626  * Supported features:
38627  *
38628  *  Tags:
38629
38630 <pre><code>
38631       {a_variable} - output encoded.
38632       {a_variable.format:("Y-m-d")} - call a method on the variable
38633       {a_variable:raw} - unencoded output
38634       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38635       {a_variable:this.method_on_template(...)} - call a method on the template object.
38636  
38637 </code></pre>
38638  *  The tpl tag:
38639 <pre><code>
38640         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38641         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38642         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38643         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38644   
38645         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38646         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38647 </code></pre>
38648  *      
38649  */
38650 Roo.XTemplate = function()
38651 {
38652     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38653     if (this.html) {
38654         this.compile();
38655     }
38656 };
38657
38658
38659 Roo.extend(Roo.XTemplate, Roo.Template, {
38660
38661     /**
38662      * The various sub templates
38663      */
38664     tpls : false,
38665     /**
38666      *
38667      * basic tag replacing syntax
38668      * WORD:WORD()
38669      *
38670      * // you can fake an object call by doing this
38671      *  x.t:(test,tesT) 
38672      * 
38673      */
38674     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38675
38676     /**
38677      * compile the template
38678      *
38679      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38680      *
38681      */
38682     compile: function()
38683     {
38684         var s = this.html;
38685      
38686         s = ['<tpl>', s, '</tpl>'].join('');
38687     
38688         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38689             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38690             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38691             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38692             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38693             m,
38694             id     = 0,
38695             tpls   = [];
38696     
38697         while(true == !!(m = s.match(re))){
38698             var forMatch   = m[0].match(nameRe),
38699                 ifMatch   = m[0].match(ifRe),
38700                 execMatch   = m[0].match(execRe),
38701                 namedMatch   = m[0].match(namedRe),
38702                 
38703                 exp  = null, 
38704                 fn   = null,
38705                 exec = null,
38706                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38707                 
38708             if (ifMatch) {
38709                 // if - puts fn into test..
38710                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38711                 if(exp){
38712                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38713                 }
38714             }
38715             
38716             if (execMatch) {
38717                 // exec - calls a function... returns empty if true is  returned.
38718                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38719                 if(exp){
38720                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38721                 }
38722             }
38723             
38724             
38725             if (name) {
38726                 // for = 
38727                 switch(name){
38728                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38729                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38730                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38731                 }
38732             }
38733             var uid = namedMatch ? namedMatch[1] : id;
38734             
38735             
38736             tpls.push({
38737                 id:     namedMatch ? namedMatch[1] : id,
38738                 target: name,
38739                 exec:   exec,
38740                 test:   fn,
38741                 body:   m[1] || ''
38742             });
38743             if (namedMatch) {
38744                 s = s.replace(m[0], '');
38745             } else { 
38746                 s = s.replace(m[0], '{xtpl'+ id + '}');
38747             }
38748             ++id;
38749         }
38750         this.tpls = [];
38751         for(var i = tpls.length-1; i >= 0; --i){
38752             this.compileTpl(tpls[i]);
38753             this.tpls[tpls[i].id] = tpls[i];
38754         }
38755         this.master = tpls[tpls.length-1];
38756         return this;
38757     },
38758     /**
38759      * same as applyTemplate, except it's done to one of the subTemplates
38760      * when using named templates, you can do:
38761      *
38762      * var str = pl.applySubTemplate('your-name', values);
38763      *
38764      * 
38765      * @param {Number} id of the template
38766      * @param {Object} values to apply to template
38767      * @param {Object} parent (normaly the instance of this object)
38768      */
38769     applySubTemplate : function(id, values, parent)
38770     {
38771         
38772         
38773         var t = this.tpls[id];
38774         
38775         
38776         try { 
38777             if(t.test && !t.test.call(this, values, parent)){
38778                 return '';
38779             }
38780         } catch(e) {
38781             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38782             Roo.log(e.toString());
38783             Roo.log(t.test);
38784             return ''
38785         }
38786         try { 
38787             
38788             if(t.exec && t.exec.call(this, values, parent)){
38789                 return '';
38790             }
38791         } catch(e) {
38792             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38793             Roo.log(e.toString());
38794             Roo.log(t.exec);
38795             return ''
38796         }
38797         try {
38798             var vs = t.target ? t.target.call(this, values, parent) : values;
38799             parent = t.target ? values : parent;
38800             if(t.target && vs instanceof Array){
38801                 var buf = [];
38802                 for(var i = 0, len = vs.length; i < len; i++){
38803                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38804                 }
38805                 return buf.join('');
38806             }
38807             return t.compiled.call(this, vs, parent);
38808         } catch (e) {
38809             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38810             Roo.log(e.toString());
38811             Roo.log(t.compiled);
38812             return '';
38813         }
38814     },
38815
38816     compileTpl : function(tpl)
38817     {
38818         var fm = Roo.util.Format;
38819         var useF = this.disableFormats !== true;
38820         var sep = Roo.isGecko ? "+" : ",";
38821         var undef = function(str) {
38822             Roo.log("Property not found :"  + str);
38823             return '';
38824         };
38825         
38826         var fn = function(m, name, format, args)
38827         {
38828             //Roo.log(arguments);
38829             args = args ? args.replace(/\\'/g,"'") : args;
38830             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38831             if (typeof(format) == 'undefined') {
38832                 format= 'htmlEncode';
38833             }
38834             if (format == 'raw' ) {
38835                 format = false;
38836             }
38837             
38838             if(name.substr(0, 4) == 'xtpl'){
38839                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38840             }
38841             
38842             // build an array of options to determine if value is undefined..
38843             
38844             // basically get 'xxxx.yyyy' then do
38845             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38846             //    (function () { Roo.log("Property not found"); return ''; })() :
38847             //    ......
38848             
38849             var udef_ar = [];
38850             var lookfor = '';
38851             Roo.each(name.split('.'), function(st) {
38852                 lookfor += (lookfor.length ? '.': '') + st;
38853                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38854             });
38855             
38856             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38857             
38858             
38859             if(format && useF){
38860                 
38861                 args = args ? ',' + args : "";
38862                  
38863                 if(format.substr(0, 5) != "this."){
38864                     format = "fm." + format + '(';
38865                 }else{
38866                     format = 'this.call("'+ format.substr(5) + '", ';
38867                     args = ", values";
38868                 }
38869                 
38870                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38871             }
38872              
38873             if (args.length) {
38874                 // called with xxyx.yuu:(test,test)
38875                 // change to ()
38876                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38877             }
38878             // raw.. - :raw modifier..
38879             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38880             
38881         };
38882         var body;
38883         // branched to use + in gecko and [].join() in others
38884         if(Roo.isGecko){
38885             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38886                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38887                     "';};};";
38888         }else{
38889             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38890             body.push(tpl.body.replace(/(\r\n|\n)/g,
38891                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38892             body.push("'].join('');};};");
38893             body = body.join('');
38894         }
38895         
38896         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38897        
38898         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38899         eval(body);
38900         
38901         return this;
38902     },
38903
38904     applyTemplate : function(values){
38905         return this.master.compiled.call(this, values, {});
38906         //var s = this.subs;
38907     },
38908
38909     apply : function(){
38910         return this.applyTemplate.apply(this, arguments);
38911     }
38912
38913  });
38914
38915 Roo.XTemplate.from = function(el){
38916     el = Roo.getDom(el);
38917     return new Roo.XTemplate(el.value || el.innerHTML);
38918 };