roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy [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         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787     
788      /**
789      * using 'cn' the nested child reader read the child array into it's child stores.
790      * @param {Object} rec The record with a 'children array
791      */
792     loadDataFromChildren : function(rec)
793     {
794         this.loadData(this.reader.toLoadData(rec));
795     },
796     
797
798     /**
799      * Gets the number of cached records.
800      * <p>
801      * <em>If using paging, this may not be the total size of the dataset. If the data object
802      * used by the Reader contains the dataset size, then the getTotalCount() function returns
803      * the data set size</em>
804      */
805     getCount : function(){
806         return this.data.length || 0;
807     },
808
809     /**
810      * Gets the total number of records in the dataset as returned by the server.
811      * <p>
812      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813      * the dataset size</em>
814      */
815     getTotalCount : function(){
816         return this.totalLength || 0;
817     },
818
819     /**
820      * Returns the sort state of the Store as an object with two properties:
821      * <pre><code>
822  field {String} The name of the field by which the Records are sorted
823  direction {String} The sort order, "ASC" or "DESC"
824      * </code></pre>
825      */
826     getSortState : function(){
827         return this.sortInfo;
828     },
829
830     // private
831     applySort : function(){
832         if(this.sortInfo && !this.remoteSort){
833             var s = this.sortInfo, f = s.field;
834             var st = this.fields.get(f).sortType;
835             var fn = function(r1, r2){
836                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
838             };
839             this.data.sort(s.direction, fn);
840             if(this.snapshot && this.snapshot != this.data){
841                 this.snapshot.sort(s.direction, fn);
842             }
843         }
844     },
845
846     /**
847      * Sets the default sort column and order to be used by the next load operation.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     setDefaultSort : function(field, dir){
852         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
853     },
854
855     /**
856      * Sort the Records.
857      * If remote sorting is used, the sort is performed on the server, and the cache is
858      * reloaded. If local sorting is used, the cache is sorted internally.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     sort : function(fieldName, dir){
863         var f = this.fields.get(fieldName);
864         if(!dir){
865             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
866             
867             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
869             }else{
870                 dir = f.sortDir;
871             }
872         }
873         this.sortToggle[f.name] = dir;
874         this.sortInfo = {field: f.name, direction: dir};
875         if(!this.remoteSort){
876             this.applySort();
877             this.fireEvent("datachanged", this);
878         }else{
879             this.load(this.lastOptions);
880         }
881     },
882
883     /**
884      * Calls the specified function for each of the Records in the cache.
885      * @param {Function} fn The function to call. The Record is passed as the first parameter.
886      * Returning <em>false</em> aborts and exits the iteration.
887      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
888      */
889     each : function(fn, scope){
890         this.data.each(fn, scope);
891     },
892
893     /**
894      * Gets all records modified since the last commit.  Modified records are persisted across load operations
895      * (e.g., during paging).
896      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
897      */
898     getModifiedRecords : function(){
899         return this.modified;
900     },
901
902     // private
903     createFilterFn : function(property, value, anyMatch){
904         if(!value.exec){ // not a regex
905             value = String(value);
906             if(value.length == 0){
907                 return false;
908             }
909             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
910         }
911         return function(r){
912             return value.test(r.data[property]);
913         };
914     },
915
916     /**
917      * Sums the value of <i>property</i> for each record between start and end and returns the result.
918      * @param {String} property A field on your records
919      * @param {Number} start The record index to start at (defaults to 0)
920      * @param {Number} end The last record index to include (defaults to length - 1)
921      * @return {Number} The sum
922      */
923     sum : function(property, start, end){
924         var rs = this.data.items, v = 0;
925         start = start || 0;
926         end = (end || end === 0) ? end : rs.length-1;
927
928         for(var i = start; i <= end; i++){
929             v += (rs[i].data[property] || 0);
930         }
931         return v;
932     },
933
934     /**
935      * Filter the records by a specified property.
936      * @param {String} field A field on your records
937      * @param {String/RegExp} value Either a string that the field
938      * should start with or a RegExp to test against the field
939      * @param {Boolean} anyMatch True to match any part not just the beginning
940      */
941     filter : function(property, value, anyMatch){
942         var fn = this.createFilterFn(property, value, anyMatch);
943         return fn ? this.filterBy(fn) : this.clearFilter();
944     },
945
946     /**
947      * Filter by a function. The specified function will be called with each
948      * record in this data source. If the function returns true the record is included,
949      * otherwise it is filtered.
950      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951      * @param {Object} scope (optional) The scope of the function (defaults to this)
952      */
953     filterBy : function(fn, scope){
954         this.snapshot = this.snapshot || this.data;
955         this.data = this.queryBy(fn, scope||this);
956         this.fireEvent("datachanged", this);
957     },
958
959     /**
960      * Query the records by a specified property.
961      * @param {String} field A field on your records
962      * @param {String/RegExp} value Either a string that the field
963      * should start with or a RegExp to test against the field
964      * @param {Boolean} anyMatch True to match any part not just the beginning
965      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
966      */
967     query : function(property, value, anyMatch){
968         var fn = this.createFilterFn(property, value, anyMatch);
969         return fn ? this.queryBy(fn) : this.data.clone();
970     },
971
972     /**
973      * Query by a function. The specified function will be called with each
974      * record in this data source. If the function returns true the record is included
975      * in the results.
976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977      * @param {Object} scope (optional) The scope of the function (defaults to this)
978       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
979      **/
980     queryBy : function(fn, scope){
981         var data = this.snapshot || this.data;
982         return data.filterBy(fn, scope||this);
983     },
984
985     /**
986      * Collects unique values for a particular dataIndex from this store.
987      * @param {String} dataIndex The property to collect
988      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990      * @return {Array} An array of the unique values
991      **/
992     collect : function(dataIndex, allowNull, bypassFilter){
993         var d = (bypassFilter === true && this.snapshot) ?
994                 this.snapshot.items : this.data.items;
995         var v, sv, r = [], l = {};
996         for(var i = 0, len = d.length; i < len; i++){
997             v = d[i].data[dataIndex];
998             sv = String(v);
999             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1000                 l[sv] = true;
1001                 r[r.length] = v;
1002             }
1003         }
1004         return r;
1005     },
1006
1007     /**
1008      * Revert to a view of the Record cache with no filtering applied.
1009      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1010      */
1011     clearFilter : function(suppressEvent){
1012         if(this.snapshot && this.snapshot != this.data){
1013             this.data = this.snapshot;
1014             delete this.snapshot;
1015             if(suppressEvent !== true){
1016                 this.fireEvent("datachanged", this);
1017             }
1018         }
1019     },
1020
1021     // private
1022     afterEdit : function(record){
1023         if(this.modified.indexOf(record) == -1){
1024             this.modified.push(record);
1025         }
1026         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1027     },
1028     
1029     // private
1030     afterReject : function(record){
1031         this.modified.remove(record);
1032         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1033     },
1034
1035     // private
1036     afterCommit : function(record){
1037         this.modified.remove(record);
1038         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1039     },
1040
1041     /**
1042      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1044      */
1045     commitChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].commit();
1050         }
1051     },
1052
1053     /**
1054      * Cancel outstanding changes on all changed records.
1055      */
1056     rejectChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].reject();
1061         }
1062     },
1063
1064     onMetaChange : function(meta, rtype, o){
1065         this.recordType = rtype;
1066         this.fields = rtype.prototype.fields;
1067         delete this.snapshot;
1068         this.sortInfo = meta.sortInfo || this.sortInfo;
1069         this.modified = [];
1070         this.fireEvent('metachange', this, this.reader.meta);
1071     },
1072     
1073     moveIndex : function(data, type)
1074     {
1075         var index = this.indexOf(data);
1076         
1077         var newIndex = index + type;
1078         
1079         this.remove(data);
1080         
1081         this.insert(newIndex, data);
1082         
1083     }
1084 });/*
1085  * Based on:
1086  * Ext JS Library 1.1.1
1087  * Copyright(c) 2006-2007, Ext JS, LLC.
1088  *
1089  * Originally Released Under LGPL - original licence link has changed is not relivant.
1090  *
1091  * Fork - LGPL
1092  * <script type="text/javascript">
1093  */
1094
1095 /**
1096  * @class Roo.data.SimpleStore
1097  * @extends Roo.data.Store
1098  * Small helper class to make creating Stores from Array data easier.
1099  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100  * @cfg {Array} fields An array of field definition objects, or field name strings.
1101  * @cfg {Object} an existing reader (eg. copied from another store)
1102  * @cfg {Array} data The multi-dimensional array of data
1103  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1104  * @cfg {Roo.data.Reader} reader  [not-required] 
1105  * @constructor
1106  * @param {Object} config
1107  */
1108 Roo.data.SimpleStore = function(config)
1109 {
1110     Roo.data.SimpleStore.superclass.constructor.call(this, {
1111         isLocal : true,
1112         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1113                 id: config.id
1114             },
1115             Roo.data.Record.create(config.fields)
1116         ),
1117         proxy : new Roo.data.MemoryProxy(config.data)
1118     });
1119     this.load();
1120 };
1121 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1122  * Based on:
1123  * Ext JS Library 1.1.1
1124  * Copyright(c) 2006-2007, Ext JS, LLC.
1125  *
1126  * Originally Released Under LGPL - original licence link has changed is not relivant.
1127  *
1128  * Fork - LGPL
1129  * <script type="text/javascript">
1130  */
1131
1132 /**
1133 /**
1134  * @extends Roo.data.Store
1135  * @class Roo.data.JsonStore
1136  * Small helper class to make creating Stores for JSON data easier. <br/>
1137 <pre><code>
1138 var store = new Roo.data.JsonStore({
1139     url: 'get-images.php',
1140     root: 'images',
1141     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1142 });
1143 </code></pre>
1144  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1145  * JsonReader and HttpProxy (unless inline data is provided).</b>
1146  * @cfg {Array} fields An array of field definition objects, or field name strings.
1147  * @constructor
1148  * @param {Object} config
1149  */
1150 Roo.data.JsonStore = function(c){
1151     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1152         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1153         reader: new Roo.data.JsonReader(c, c.fields)
1154     }));
1155 };
1156 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1157  * Based on:
1158  * Ext JS Library 1.1.1
1159  * Copyright(c) 2006-2007, Ext JS, LLC.
1160  *
1161  * Originally Released Under LGPL - original licence link has changed is not relivant.
1162  *
1163  * Fork - LGPL
1164  * <script type="text/javascript">
1165  */
1166
1167  
1168 Roo.data.Field = function(config){
1169     if(typeof config == "string"){
1170         config = {name: config};
1171     }
1172     Roo.apply(this, config);
1173     
1174     if(!this.type){
1175         this.type = "auto";
1176     }
1177     
1178     var st = Roo.data.SortTypes;
1179     // named sortTypes are supported, here we look them up
1180     if(typeof this.sortType == "string"){
1181         this.sortType = st[this.sortType];
1182     }
1183     
1184     // set default sortType for strings and dates
1185     if(!this.sortType){
1186         switch(this.type){
1187             case "string":
1188                 this.sortType = st.asUCString;
1189                 break;
1190             case "date":
1191                 this.sortType = st.asDate;
1192                 break;
1193             default:
1194                 this.sortType = st.none;
1195         }
1196     }
1197
1198     // define once
1199     var stripRe = /[\$,%]/g;
1200
1201     // prebuilt conversion function for this field, instead of
1202     // switching every time we're reading a value
1203     if(!this.convert){
1204         var cv, dateFormat = this.dateFormat;
1205         switch(this.type){
1206             case "":
1207             case "auto":
1208             case undefined:
1209                 cv = function(v){ return v; };
1210                 break;
1211             case "string":
1212                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1213                 break;
1214             case "int":
1215                 cv = function(v){
1216                     return v !== undefined && v !== null && v !== '' ?
1217                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1218                     };
1219                 break;
1220             case "float":
1221                 cv = function(v){
1222                     return v !== undefined && v !== null && v !== '' ?
1223                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1224                     };
1225                 break;
1226             case "bool":
1227             case "boolean":
1228                 cv = function(v){ return v === true || v === "true" || v == 1; };
1229                 break;
1230             case "date":
1231                 cv = function(v){
1232                     if(!v){
1233                         return '';
1234                     }
1235                     if(v instanceof Date){
1236                         return v;
1237                     }
1238                     if(dateFormat){
1239                         if(dateFormat == "timestamp"){
1240                             return new Date(v*1000);
1241                         }
1242                         return Date.parseDate(v, dateFormat);
1243                     }
1244                     var parsed = Date.parse(v);
1245                     return parsed ? new Date(parsed) : null;
1246                 };
1247              break;
1248             
1249         }
1250         this.convert = cv;
1251     }
1252 };
1253
1254 Roo.data.Field.prototype = {
1255     dateFormat: null,
1256     defaultValue: "",
1257     mapping: null,
1258     sortType : null,
1259     sortDir : "ASC"
1260 };/*
1261  * Based on:
1262  * Ext JS Library 1.1.1
1263  * Copyright(c) 2006-2007, Ext JS, LLC.
1264  *
1265  * Originally Released Under LGPL - original licence link has changed is not relivant.
1266  *
1267  * Fork - LGPL
1268  * <script type="text/javascript">
1269  */
1270  
1271 // Base class for reading structured data from a data source.  This class is intended to be
1272 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1273
1274 /**
1275  * @class Roo.data.DataReader
1276  * @abstract
1277  * Base class for reading structured data from a data source.  This class is intended to be
1278  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1279  */
1280
1281 Roo.data.DataReader = function(meta, recordType){
1282     
1283     this.meta = meta;
1284     
1285     this.recordType = recordType instanceof Array ? 
1286         Roo.data.Record.create(recordType) : recordType;
1287 };
1288
1289 Roo.data.DataReader.prototype = {
1290     
1291     
1292     readerType : 'Data',
1293      /**
1294      * Create an empty record
1295      * @param {Object} data (optional) - overlay some values
1296      * @return {Roo.data.Record} record created.
1297      */
1298     newRow :  function(d) {
1299         var da =  {};
1300         this.recordType.prototype.fields.each(function(c) {
1301             switch( c.type) {
1302                 case 'int' : da[c.name] = 0; break;
1303                 case 'date' : da[c.name] = new Date(); break;
1304                 case 'float' : da[c.name] = 0.0; break;
1305                 case 'boolean' : da[c.name] = false; break;
1306                 default : da[c.name] = ""; break;
1307             }
1308             
1309         });
1310         return new this.recordType(Roo.apply(da, d));
1311     }
1312     
1313     
1314 };/*
1315  * Based on:
1316  * Ext JS Library 1.1.1
1317  * Copyright(c) 2006-2007, Ext JS, LLC.
1318  *
1319  * Originally Released Under LGPL - original licence link has changed is not relivant.
1320  *
1321  * Fork - LGPL
1322  * <script type="text/javascript">
1323  */
1324
1325 /**
1326  * @class Roo.data.DataProxy
1327  * @extends Roo.data.Observable
1328  * @abstract
1329  * This class is an abstract base class for implementations which provide retrieval of
1330  * unformatted data objects.<br>
1331  * <p>
1332  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1333  * (of the appropriate type which knows how to parse the data object) to provide a block of
1334  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1335  * <p>
1336  * Custom implementations must implement the load method as described in
1337  * {@link Roo.data.HttpProxy#load}.
1338  */
1339 Roo.data.DataProxy = function(){
1340     this.addEvents({
1341         /**
1342          * @event beforeload
1343          * Fires before a network request is made to retrieve a data object.
1344          * @param {Object} This DataProxy object.
1345          * @param {Object} params The params parameter to the load function.
1346          */
1347         beforeload : true,
1348         /**
1349          * @event load
1350          * Fires before the load method's callback is called.
1351          * @param {Object} This DataProxy object.
1352          * @param {Object} o The data object.
1353          * @param {Object} arg The callback argument object passed to the load function.
1354          */
1355         load : true,
1356         /**
1357          * @event loadexception
1358          * Fires if an Exception occurs during data retrieval.
1359          * @param {Object} This DataProxy object.
1360          * @param {Object} o The data object.
1361          * @param {Object} arg The callback argument object passed to the load function.
1362          * @param {Object} e The Exception.
1363          */
1364         loadexception : true
1365     });
1366     Roo.data.DataProxy.superclass.constructor.call(this);
1367 };
1368
1369 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1370
1371     /**
1372      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1373      */
1374 /*
1375  * Based on:
1376  * Ext JS Library 1.1.1
1377  * Copyright(c) 2006-2007, Ext JS, LLC.
1378  *
1379  * Originally Released Under LGPL - original licence link has changed is not relivant.
1380  *
1381  * Fork - LGPL
1382  * <script type="text/javascript">
1383  */
1384 /**
1385  * @class Roo.data.MemoryProxy
1386  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1387  * to the Reader when its load method is called.
1388  * @constructor
1389  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1390  */
1391 Roo.data.MemoryProxy = function(data){
1392     if (data.data) {
1393         data = data.data;
1394     }
1395     Roo.data.MemoryProxy.superclass.constructor.call(this);
1396     this.data = data;
1397 };
1398
1399 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1400     
1401     /**
1402      * Load data from the requested source (in this case an in-memory
1403      * data object passed to the constructor), read the data object into
1404      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1405      * process that block using the passed callback.
1406      * @param {Object} params This parameter is not used by the MemoryProxy class.
1407      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1408      * object into a block of Roo.data.Records.
1409      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1410      * The function must be passed <ul>
1411      * <li>The Record block object</li>
1412      * <li>The "arg" argument from the load function</li>
1413      * <li>A boolean success indicator</li>
1414      * </ul>
1415      * @param {Object} scope The scope in which to call the callback
1416      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1417      */
1418     load : function(params, reader, callback, scope, arg){
1419         params = params || {};
1420         var result;
1421         try {
1422             result = reader.readRecords(params.data ? params.data :this.data);
1423         }catch(e){
1424             this.fireEvent("loadexception", this, arg, null, e);
1425             callback.call(scope, null, arg, false);
1426             return;
1427         }
1428         callback.call(scope, result, arg, true);
1429     },
1430     
1431     // private
1432     update : function(params, records){
1433         
1434     }
1435 });/*
1436  * Based on:
1437  * Ext JS Library 1.1.1
1438  * Copyright(c) 2006-2007, Ext JS, LLC.
1439  *
1440  * Originally Released Under LGPL - original licence link has changed is not relivant.
1441  *
1442  * Fork - LGPL
1443  * <script type="text/javascript">
1444  */
1445 /**
1446  * @class Roo.data.HttpProxy
1447  * @extends Roo.data.DataProxy
1448  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1449  * configured to reference a certain URL.<br><br>
1450  * <p>
1451  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1452  * from which the running page was served.<br><br>
1453  * <p>
1454  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1455  * <p>
1456  * Be aware that to enable the browser to parse an XML document, the server must set
1457  * the Content-Type header in the HTTP response to "text/xml".
1458  * @constructor
1459  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1460  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1461  * will be used to make the request.
1462  */
1463 Roo.data.HttpProxy = function(conn){
1464     Roo.data.HttpProxy.superclass.constructor.call(this);
1465     // is conn a conn config or a real conn?
1466     this.conn = conn;
1467     this.useAjax = !conn || !conn.events;
1468   
1469 };
1470
1471 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1472     // thse are take from connection...
1473     
1474     /**
1475      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1476      */
1477     /**
1478      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1479      * extra parameters to each request made by this object. (defaults to undefined)
1480      */
1481     /**
1482      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1483      *  to each request made by this object. (defaults to undefined)
1484      */
1485     /**
1486      * @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)
1487      */
1488     /**
1489      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1490      */
1491      /**
1492      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1493      * @type Boolean
1494      */
1495   
1496
1497     /**
1498      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1499      * @type Boolean
1500      */
1501     /**
1502      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1503      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1504      * a finer-grained basis than the DataProxy events.
1505      */
1506     getConnection : function(){
1507         return this.useAjax ? Roo.Ajax : this.conn;
1508     },
1509
1510     /**
1511      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1512      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1513      * process that block using the passed callback.
1514      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1515      * for the request to the remote server.
1516      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1517      * object into a block of Roo.data.Records.
1518      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1519      * The function must be passed <ul>
1520      * <li>The Record block object</li>
1521      * <li>The "arg" argument from the load function</li>
1522      * <li>A boolean success indicator</li>
1523      * </ul>
1524      * @param {Object} scope The scope in which to call the callback
1525      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1526      */
1527     load : function(params, reader, callback, scope, arg){
1528         if(this.fireEvent("beforeload", this, params) !== false){
1529             var  o = {
1530                 params : params || {},
1531                 request: {
1532                     callback : callback,
1533                     scope : scope,
1534                     arg : arg
1535                 },
1536                 reader: reader,
1537                 callback : this.loadResponse,
1538                 scope: this
1539             };
1540             if(this.useAjax){
1541                 Roo.applyIf(o, this.conn);
1542                 if(this.activeRequest){
1543                     Roo.Ajax.abort(this.activeRequest);
1544                 }
1545                 this.activeRequest = Roo.Ajax.request(o);
1546             }else{
1547                 this.conn.request(o);
1548             }
1549         }else{
1550             callback.call(scope||this, null, arg, false);
1551         }
1552     },
1553
1554     // private
1555     loadResponse : function(o, success, response){
1556         delete this.activeRequest;
1557         if(!success){
1558             this.fireEvent("loadexception", this, o, response);
1559             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1560             return;
1561         }
1562         var result;
1563         try {
1564             result = o.reader.read(response);
1565         }catch(e){
1566             this.fireEvent("loadexception", this, o, response, e);
1567             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1568             return;
1569         }
1570         
1571         this.fireEvent("load", this, o, o.request.arg);
1572         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1573     },
1574
1575     // private
1576     update : function(dataSet){
1577
1578     },
1579
1580     // private
1581     updateResponse : function(dataSet){
1582
1583     }
1584 });/*
1585  * Based on:
1586  * Ext JS Library 1.1.1
1587  * Copyright(c) 2006-2007, Ext JS, LLC.
1588  *
1589  * Originally Released Under LGPL - original licence link has changed is not relivant.
1590  *
1591  * Fork - LGPL
1592  * <script type="text/javascript">
1593  */
1594
1595 /**
1596  * @class Roo.data.ScriptTagProxy
1597  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1598  * other than the originating domain of the running page.<br><br>
1599  * <p>
1600  * <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
1601  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1602  * <p>
1603  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1604  * source code that is used as the source inside a &lt;script> tag.<br><br>
1605  * <p>
1606  * In order for the browser to process the returned data, the server must wrap the data object
1607  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1608  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1609  * depending on whether the callback name was passed:
1610  * <p>
1611  * <pre><code>
1612 boolean scriptTag = false;
1613 String cb = request.getParameter("callback");
1614 if (cb != null) {
1615     scriptTag = true;
1616     response.setContentType("text/javascript");
1617 } else {
1618     response.setContentType("application/x-json");
1619 }
1620 Writer out = response.getWriter();
1621 if (scriptTag) {
1622     out.write(cb + "(");
1623 }
1624 out.print(dataBlock.toJsonString());
1625 if (scriptTag) {
1626     out.write(");");
1627 }
1628 </pre></code>
1629  *
1630  * @constructor
1631  * @param {Object} config A configuration object.
1632  */
1633 Roo.data.ScriptTagProxy = function(config){
1634     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1635     Roo.apply(this, config);
1636     this.head = document.getElementsByTagName("head")[0];
1637 };
1638
1639 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1640
1641 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1642     /**
1643      * @cfg {String} url The URL from which to request the data object.
1644      */
1645     /**
1646      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1647      */
1648     timeout : 30000,
1649     /**
1650      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1651      * the server the name of the callback function set up by the load call to process the returned data object.
1652      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1653      * javascript output which calls this named function passing the data object as its only parameter.
1654      */
1655     callbackParam : "callback",
1656     /**
1657      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1658      * name to the request.
1659      */
1660     nocache : true,
1661
1662     /**
1663      * Load data from the configured URL, read the data object into
1664      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1665      * process that block using the passed callback.
1666      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1667      * for the request to the remote server.
1668      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1669      * object into a block of Roo.data.Records.
1670      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1671      * The function must be passed <ul>
1672      * <li>The Record block object</li>
1673      * <li>The "arg" argument from the load function</li>
1674      * <li>A boolean success indicator</li>
1675      * </ul>
1676      * @param {Object} scope The scope in which to call the callback
1677      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1678      */
1679     load : function(params, reader, callback, scope, arg){
1680         if(this.fireEvent("beforeload", this, params) !== false){
1681
1682             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1683
1684             var url = this.url;
1685             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1686             if(this.nocache){
1687                 url += "&_dc=" + (new Date().getTime());
1688             }
1689             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1690             var trans = {
1691                 id : transId,
1692                 cb : "stcCallback"+transId,
1693                 scriptId : "stcScript"+transId,
1694                 params : params,
1695                 arg : arg,
1696                 url : url,
1697                 callback : callback,
1698                 scope : scope,
1699                 reader : reader
1700             };
1701             var conn = this;
1702
1703             window[trans.cb] = function(o){
1704                 conn.handleResponse(o, trans);
1705             };
1706
1707             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1708
1709             if(this.autoAbort !== false){
1710                 this.abort();
1711             }
1712
1713             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1714
1715             var script = document.createElement("script");
1716             script.setAttribute("src", url);
1717             script.setAttribute("type", "text/javascript");
1718             script.setAttribute("id", trans.scriptId);
1719             this.head.appendChild(script);
1720
1721             this.trans = trans;
1722         }else{
1723             callback.call(scope||this, null, arg, false);
1724         }
1725     },
1726
1727     // private
1728     isLoading : function(){
1729         return this.trans ? true : false;
1730     },
1731
1732     /**
1733      * Abort the current server request.
1734      */
1735     abort : function(){
1736         if(this.isLoading()){
1737             this.destroyTrans(this.trans);
1738         }
1739     },
1740
1741     // private
1742     destroyTrans : function(trans, isLoaded){
1743         this.head.removeChild(document.getElementById(trans.scriptId));
1744         clearTimeout(trans.timeoutId);
1745         if(isLoaded){
1746             window[trans.cb] = undefined;
1747             try{
1748                 delete window[trans.cb];
1749             }catch(e){}
1750         }else{
1751             // if hasn't been loaded, wait for load to remove it to prevent script error
1752             window[trans.cb] = function(){
1753                 window[trans.cb] = undefined;
1754                 try{
1755                     delete window[trans.cb];
1756                 }catch(e){}
1757             };
1758         }
1759     },
1760
1761     // private
1762     handleResponse : function(o, trans){
1763         this.trans = false;
1764         this.destroyTrans(trans, true);
1765         var result;
1766         try {
1767             result = trans.reader.readRecords(o);
1768         }catch(e){
1769             this.fireEvent("loadexception", this, o, trans.arg, e);
1770             trans.callback.call(trans.scope||window, null, trans.arg, false);
1771             return;
1772         }
1773         this.fireEvent("load", this, o, trans.arg);
1774         trans.callback.call(trans.scope||window, result, trans.arg, true);
1775     },
1776
1777     // private
1778     handleFailure : function(trans){
1779         this.trans = false;
1780         this.destroyTrans(trans, false);
1781         this.fireEvent("loadexception", this, null, trans.arg);
1782         trans.callback.call(trans.scope||window, null, trans.arg, false);
1783     }
1784 });/*
1785  * Based on:
1786  * Ext JS Library 1.1.1
1787  * Copyright(c) 2006-2007, Ext JS, LLC.
1788  *
1789  * Originally Released Under LGPL - original licence link has changed is not relivant.
1790  *
1791  * Fork - LGPL
1792  * <script type="text/javascript">
1793  */
1794
1795 /**
1796  * @class Roo.data.JsonReader
1797  * @extends Roo.data.DataReader
1798  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1799  * based on mappings in a provided Roo.data.Record constructor.
1800  * 
1801  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1802  * in the reply previously. 
1803  * 
1804  * <p>
1805  * Example code:
1806  * <pre><code>
1807 var RecordDef = Roo.data.Record.create([
1808     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1809     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1810 ]);
1811 var myReader = new Roo.data.JsonReader({
1812     totalProperty: "results",    // The property which contains the total dataset size (optional)
1813     root: "rows",                // The property which contains an Array of row objects
1814     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1815 }, RecordDef);
1816 </code></pre>
1817  * <p>
1818  * This would consume a JSON file like this:
1819  * <pre><code>
1820 { 'results': 2, 'rows': [
1821     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1822     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1823 }
1824 </code></pre>
1825  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1826  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1827  * paged from the remote server.
1828  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1829  * @cfg {String} root name of the property which contains the Array of row objects.
1830  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1831  * @cfg {Array} fields Array of field definition objects
1832  * @constructor
1833  * Create a new JsonReader
1834  * @param {Object} meta Metadata configuration options
1835  * @param {Object} recordType Either an Array of field definition objects,
1836  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1837  */
1838 Roo.data.JsonReader = function(meta, recordType){
1839     
1840     meta = meta || {};
1841     // set some defaults:
1842     Roo.applyIf(meta, {
1843         totalProperty: 'total',
1844         successProperty : 'success',
1845         root : 'data',
1846         id : 'id'
1847     });
1848     
1849     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1850 };
1851 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1852     
1853     readerType : 'Json',
1854     
1855     /**
1856      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1857      * Used by Store query builder to append _requestMeta to params.
1858      * 
1859      */
1860     metaFromRemote : false,
1861     /**
1862      * This method is only used by a DataProxy which has retrieved data from a remote server.
1863      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1864      * @return {Object} data A data block which is used by an Roo.data.Store object as
1865      * a cache of Roo.data.Records.
1866      */
1867     read : function(response){
1868         var json = response.responseText;
1869        
1870         var o = /* eval:var:o */ eval("("+json+")");
1871         if(!o) {
1872             throw {message: "JsonReader.read: Json object not found"};
1873         }
1874         
1875         if(o.metaData){
1876             
1877             delete this.ef;
1878             this.metaFromRemote = true;
1879             this.meta = o.metaData;
1880             this.recordType = Roo.data.Record.create(o.metaData.fields);
1881             this.onMetaChange(this.meta, this.recordType, o);
1882         }
1883         return this.readRecords(o);
1884     },
1885
1886     // private function a store will implement
1887     onMetaChange : function(meta, recordType, o){
1888
1889     },
1890
1891     /**
1892          * @ignore
1893          */
1894     simpleAccess: function(obj, subsc) {
1895         return obj[subsc];
1896     },
1897
1898         /**
1899          * @ignore
1900          */
1901     getJsonAccessor: function(){
1902         var re = /[\[\.]/;
1903         return function(expr) {
1904             try {
1905                 return(re.test(expr))
1906                     ? new Function("obj", "return obj." + expr)
1907                     : function(obj){
1908                         return obj[expr];
1909                     };
1910             } catch(e){}
1911             return Roo.emptyFn;
1912         };
1913     }(),
1914
1915     /**
1916      * Create a data block containing Roo.data.Records from an XML document.
1917      * @param {Object} o An object which contains an Array of row objects in the property specified
1918      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1919      * which contains the total size of the dataset.
1920      * @return {Object} data A data block which is used by an Roo.data.Store object as
1921      * a cache of Roo.data.Records.
1922      */
1923     readRecords : function(o){
1924         /**
1925          * After any data loads, the raw JSON data is available for further custom processing.
1926          * @type Object
1927          */
1928         this.o = o;
1929         var s = this.meta, Record = this.recordType,
1930             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1931
1932 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1933         if (!this.ef) {
1934             if(s.totalProperty) {
1935                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1936                 }
1937                 if(s.successProperty) {
1938                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1939                 }
1940                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1941                 if (s.id) {
1942                         var g = this.getJsonAccessor(s.id);
1943                         this.getId = function(rec) {
1944                                 var r = g(rec);  
1945                                 return (r === undefined || r === "") ? null : r;
1946                         };
1947                 } else {
1948                         this.getId = function(){return null;};
1949                 }
1950             this.ef = [];
1951             for(var jj = 0; jj < fl; jj++){
1952                 f = fi[jj];
1953                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1954                 this.ef[jj] = this.getJsonAccessor(map);
1955             }
1956         }
1957
1958         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1959         if(s.totalProperty){
1960             var vt = parseInt(this.getTotal(o), 10);
1961             if(!isNaN(vt)){
1962                 totalRecords = vt;
1963             }
1964         }
1965         if(s.successProperty){
1966             var vs = this.getSuccess(o);
1967             if(vs === false || vs === 'false'){
1968                 success = false;
1969             }
1970         }
1971         var records = [];
1972         for(var i = 0; i < c; i++){
1973                 var n = root[i];
1974             var values = {};
1975             var id = this.getId(n);
1976             for(var j = 0; j < fl; j++){
1977                 f = fi[j];
1978             var v = this.ef[j](n);
1979             if (!f.convert) {
1980                 Roo.log('missing convert for ' + f.name);
1981                 Roo.log(f);
1982                 continue;
1983             }
1984             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1985             }
1986             var record = new Record(values, id);
1987             record.json = n;
1988             records[i] = record;
1989         }
1990         return {
1991             raw : o,
1992             success : success,
1993             records : records,
1994             totalRecords : totalRecords
1995         };
1996     },
1997     // used when loading children.. @see loadDataFromChildren
1998     toLoadData: function(rec)
1999     {
2000         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2001         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2002         return { data : data, total : data.length };
2003         
2004     }
2005 });/*
2006  * Based on:
2007  * Ext JS Library 1.1.1
2008  * Copyright(c) 2006-2007, Ext JS, LLC.
2009  *
2010  * Originally Released Under LGPL - original licence link has changed is not relivant.
2011  *
2012  * Fork - LGPL
2013  * <script type="text/javascript">
2014  */
2015
2016 /**
2017  * @class Roo.data.XmlReader
2018  * @extends Roo.data.DataReader
2019  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2020  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2021  * <p>
2022  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2023  * header in the HTTP response must be set to "text/xml".</em>
2024  * <p>
2025  * Example code:
2026  * <pre><code>
2027 var RecordDef = Roo.data.Record.create([
2028    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2029    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2030 ]);
2031 var myReader = new Roo.data.XmlReader({
2032    totalRecords: "results", // The element which contains the total dataset size (optional)
2033    record: "row",           // The repeated element which contains row information
2034    id: "id"                 // The element within the row that provides an ID for the record (optional)
2035 }, RecordDef);
2036 </code></pre>
2037  * <p>
2038  * This would consume an XML file like this:
2039  * <pre><code>
2040 &lt;?xml?>
2041 &lt;dataset>
2042  &lt;results>2&lt;/results>
2043  &lt;row>
2044    &lt;id>1&lt;/id>
2045    &lt;name>Bill&lt;/name>
2046    &lt;occupation>Gardener&lt;/occupation>
2047  &lt;/row>
2048  &lt;row>
2049    &lt;id>2&lt;/id>
2050    &lt;name>Ben&lt;/name>
2051    &lt;occupation>Horticulturalist&lt;/occupation>
2052  &lt;/row>
2053 &lt;/dataset>
2054 </code></pre>
2055  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2056  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2057  * paged from the remote server.
2058  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2059  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2060  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2061  * a record identifier value.
2062  * @constructor
2063  * Create a new XmlReader
2064  * @param {Object} meta Metadata configuration options
2065  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2066  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2067  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2068  */
2069 Roo.data.XmlReader = function(meta, recordType){
2070     meta = meta || {};
2071     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2072 };
2073 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2074     
2075     readerType : 'Xml',
2076     
2077     /**
2078      * This method is only used by a DataProxy which has retrieved data from a remote server.
2079          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2080          * to contain a method called 'responseXML' that returns an XML document object.
2081      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2082      * a cache of Roo.data.Records.
2083      */
2084     read : function(response){
2085         var doc = response.responseXML;
2086         if(!doc) {
2087             throw {message: "XmlReader.read: XML Document not available"};
2088         }
2089         return this.readRecords(doc);
2090     },
2091
2092     /**
2093      * Create a data block containing Roo.data.Records from an XML document.
2094          * @param {Object} doc A parsed XML document.
2095      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2096      * a cache of Roo.data.Records.
2097      */
2098     readRecords : function(doc){
2099         /**
2100          * After any data loads/reads, the raw XML Document is available for further custom processing.
2101          * @type XMLDocument
2102          */
2103         this.xmlData = doc;
2104         var root = doc.documentElement || doc;
2105         var q = Roo.DomQuery;
2106         var recordType = this.recordType, fields = recordType.prototype.fields;
2107         var sid = this.meta.id;
2108         var totalRecords = 0, success = true;
2109         if(this.meta.totalRecords){
2110             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2111         }
2112         
2113         if(this.meta.success){
2114             var sv = q.selectValue(this.meta.success, root, true);
2115             success = sv !== false && sv !== 'false';
2116         }
2117         var records = [];
2118         var ns = q.select(this.meta.record, root);
2119         for(var i = 0, len = ns.length; i < len; i++) {
2120                 var n = ns[i];
2121                 var values = {};
2122                 var id = sid ? q.selectValue(sid, n) : undefined;
2123                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2124                     var f = fields.items[j];
2125                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2126                     v = f.convert(v);
2127                     values[f.name] = v;
2128                 }
2129                 var record = new recordType(values, id);
2130                 record.node = n;
2131                 records[records.length] = record;
2132             }
2133
2134             return {
2135                 success : success,
2136                 records : records,
2137                 totalRecords : totalRecords || records.length
2138             };
2139     }
2140 });/*
2141  * Based on:
2142  * Ext JS Library 1.1.1
2143  * Copyright(c) 2006-2007, Ext JS, LLC.
2144  *
2145  * Originally Released Under LGPL - original licence link has changed is not relivant.
2146  *
2147  * Fork - LGPL
2148  * <script type="text/javascript">
2149  */
2150
2151 /**
2152  * @class Roo.data.ArrayReader
2153  * @extends Roo.data.DataReader
2154  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2155  * Each element of that Array represents a row of data fields. The
2156  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2157  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2158  * <p>
2159  * Example code:.
2160  * <pre><code>
2161 var RecordDef = Roo.data.Record.create([
2162     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2163     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2164 ]);
2165 var myReader = new Roo.data.ArrayReader({
2166     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2167 }, RecordDef);
2168 </code></pre>
2169  * <p>
2170  * This would consume an Array like this:
2171  * <pre><code>
2172 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2173   </code></pre>
2174  
2175  * @constructor
2176  * Create a new JsonReader
2177  * @param {Object} meta Metadata configuration options.
2178  * @param {Object|Array} recordType Either an Array of field definition objects
2179  * 
2180  * @cfg {Array} fields Array of field definition objects
2181  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2182  * as specified to {@link Roo.data.Record#create},
2183  * or an {@link Roo.data.Record} object
2184  *
2185  * 
2186  * created using {@link Roo.data.Record#create}.
2187  */
2188 Roo.data.ArrayReader = function(meta, recordType)
2189 {    
2190     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2191 };
2192
2193 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2194     
2195       /**
2196      * Create a data block containing Roo.data.Records from an XML document.
2197      * @param {Object} o An Array of row objects which represents the dataset.
2198      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2199      * a cache of Roo.data.Records.
2200      */
2201     readRecords : function(o)
2202     {
2203         var sid = this.meta ? this.meta.id : null;
2204         var recordType = this.recordType, fields = recordType.prototype.fields;
2205         var records = [];
2206         var root = o;
2207         for(var i = 0; i < root.length; i++){
2208             var n = root[i];
2209             var values = {};
2210             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2211             for(var j = 0, jlen = fields.length; j < jlen; j++){
2212                 var f = fields.items[j];
2213                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2214                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2215                 v = f.convert(v);
2216                 values[f.name] = v;
2217             }
2218             var record = new recordType(values, id);
2219             record.json = n;
2220             records[records.length] = record;
2221         }
2222         return {
2223             records : records,
2224             totalRecords : records.length
2225         };
2226     },
2227     // used when loading children.. @see loadDataFromChildren
2228     toLoadData: function(rec)
2229     {
2230         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2231         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2232         
2233     }
2234     
2235     
2236 });/*
2237  * Based on:
2238  * Ext JS Library 1.1.1
2239  * Copyright(c) 2006-2007, Ext JS, LLC.
2240  *
2241  * Originally Released Under LGPL - original licence link has changed is not relivant.
2242  *
2243  * Fork - LGPL
2244  * <script type="text/javascript">
2245  */
2246
2247
2248 /**
2249  * @class Roo.data.Tree
2250  * @extends Roo.util.Observable
2251  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2252  * in the tree have most standard DOM functionality.
2253  * @constructor
2254  * @param {Node} root (optional) The root node
2255  */
2256 Roo.data.Tree = function(root){
2257    this.nodeHash = {};
2258    /**
2259     * The root node for this tree
2260     * @type Node
2261     */
2262    this.root = null;
2263    if(root){
2264        this.setRootNode(root);
2265    }
2266    this.addEvents({
2267        /**
2268         * @event append
2269         * Fires when a new child node is appended to a node in this tree.
2270         * @param {Tree} tree The owner tree
2271         * @param {Node} parent The parent node
2272         * @param {Node} node The newly appended node
2273         * @param {Number} index The index of the newly appended node
2274         */
2275        "append" : true,
2276        /**
2277         * @event remove
2278         * Fires when a child node is removed from a node in this tree.
2279         * @param {Tree} tree The owner tree
2280         * @param {Node} parent The parent node
2281         * @param {Node} node The child node removed
2282         */
2283        "remove" : true,
2284        /**
2285         * @event move
2286         * Fires when a node is moved to a new location in the tree
2287         * @param {Tree} tree The owner tree
2288         * @param {Node} node The node moved
2289         * @param {Node} oldParent The old parent of this node
2290         * @param {Node} newParent The new parent of this node
2291         * @param {Number} index The index it was moved to
2292         */
2293        "move" : true,
2294        /**
2295         * @event insert
2296         * Fires when a new child node is inserted in a node in this tree.
2297         * @param {Tree} tree The owner tree
2298         * @param {Node} parent The parent node
2299         * @param {Node} node The child node inserted
2300         * @param {Node} refNode The child node the node was inserted before
2301         */
2302        "insert" : true,
2303        /**
2304         * @event beforeappend
2305         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2306         * @param {Tree} tree The owner tree
2307         * @param {Node} parent The parent node
2308         * @param {Node} node The child node to be appended
2309         */
2310        "beforeappend" : true,
2311        /**
2312         * @event beforeremove
2313         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2314         * @param {Tree} tree The owner tree
2315         * @param {Node} parent The parent node
2316         * @param {Node} node The child node to be removed
2317         */
2318        "beforeremove" : true,
2319        /**
2320         * @event beforemove
2321         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2322         * @param {Tree} tree The owner tree
2323         * @param {Node} node The node being moved
2324         * @param {Node} oldParent The parent of the node
2325         * @param {Node} newParent The new parent the node is moving to
2326         * @param {Number} index The index it is being moved to
2327         */
2328        "beforemove" : true,
2329        /**
2330         * @event beforeinsert
2331         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2332         * @param {Tree} tree The owner tree
2333         * @param {Node} parent The parent node
2334         * @param {Node} node The child node to be inserted
2335         * @param {Node} refNode The child node the node is being inserted before
2336         */
2337        "beforeinsert" : true
2338    });
2339
2340     Roo.data.Tree.superclass.constructor.call(this);
2341 };
2342
2343 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2344     pathSeparator: "/",
2345
2346     proxyNodeEvent : function(){
2347         return this.fireEvent.apply(this, arguments);
2348     },
2349
2350     /**
2351      * Returns the root node for this tree.
2352      * @return {Node}
2353      */
2354     getRootNode : function(){
2355         return this.root;
2356     },
2357
2358     /**
2359      * Sets the root node for this tree.
2360      * @param {Node} node
2361      * @return {Node}
2362      */
2363     setRootNode : function(node){
2364         this.root = node;
2365         node.ownerTree = this;
2366         node.isRoot = true;
2367         this.registerNode(node);
2368         return node;
2369     },
2370
2371     /**
2372      * Gets a node in this tree by its id.
2373      * @param {String} id
2374      * @return {Node}
2375      */
2376     getNodeById : function(id){
2377         return this.nodeHash[id];
2378     },
2379
2380     registerNode : function(node){
2381         this.nodeHash[node.id] = node;
2382     },
2383
2384     unregisterNode : function(node){
2385         delete this.nodeHash[node.id];
2386     },
2387
2388     toString : function(){
2389         return "[Tree"+(this.id?" "+this.id:"")+"]";
2390     }
2391 });
2392
2393 /**
2394  * @class Roo.data.Node
2395  * @extends Roo.util.Observable
2396  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2397  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2398  * @constructor
2399  * @param {Object} attributes The attributes/config for the node
2400  */
2401 Roo.data.Node = function(attributes){
2402     /**
2403      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2404      * @type {Object}
2405      */
2406     this.attributes = attributes || {};
2407     this.leaf = this.attributes.leaf;
2408     /**
2409      * The node id. @type String
2410      */
2411     this.id = this.attributes.id;
2412     if(!this.id){
2413         this.id = Roo.id(null, "ynode-");
2414         this.attributes.id = this.id;
2415     }
2416      
2417     
2418     /**
2419      * All child nodes of this node. @type Array
2420      */
2421     this.childNodes = [];
2422     if(!this.childNodes.indexOf){ // indexOf is a must
2423         this.childNodes.indexOf = function(o){
2424             for(var i = 0, len = this.length; i < len; i++){
2425                 if(this[i] == o) {
2426                     return i;
2427                 }
2428             }
2429             return -1;
2430         };
2431     }
2432     /**
2433      * The parent node for this node. @type Node
2434      */
2435     this.parentNode = null;
2436     /**
2437      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2438      */
2439     this.firstChild = null;
2440     /**
2441      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2442      */
2443     this.lastChild = null;
2444     /**
2445      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2446      */
2447     this.previousSibling = null;
2448     /**
2449      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2450      */
2451     this.nextSibling = null;
2452
2453     this.addEvents({
2454        /**
2455         * @event append
2456         * Fires when a new child node is appended
2457         * @param {Tree} tree The owner tree
2458         * @param {Node} this This node
2459         * @param {Node} node The newly appended node
2460         * @param {Number} index The index of the newly appended node
2461         */
2462        "append" : true,
2463        /**
2464         * @event remove
2465         * Fires when a child node is removed
2466         * @param {Tree} tree The owner tree
2467         * @param {Node} this This node
2468         * @param {Node} node The removed node
2469         */
2470        "remove" : true,
2471        /**
2472         * @event move
2473         * Fires when this node is moved to a new location in the tree
2474         * @param {Tree} tree The owner tree
2475         * @param {Node} this This node
2476         * @param {Node} oldParent The old parent of this node
2477         * @param {Node} newParent The new parent of this node
2478         * @param {Number} index The index it was moved to
2479         */
2480        "move" : true,
2481        /**
2482         * @event insert
2483         * Fires when a new child node is inserted.
2484         * @param {Tree} tree The owner tree
2485         * @param {Node} this This node
2486         * @param {Node} node The child node inserted
2487         * @param {Node} refNode The child node the node was inserted before
2488         */
2489        "insert" : true,
2490        /**
2491         * @event beforeappend
2492         * Fires before a new child is appended, return false to cancel the append.
2493         * @param {Tree} tree The owner tree
2494         * @param {Node} this This node
2495         * @param {Node} node The child node to be appended
2496         */
2497        "beforeappend" : true,
2498        /**
2499         * @event beforeremove
2500         * Fires before a child is removed, return false to cancel the remove.
2501         * @param {Tree} tree The owner tree
2502         * @param {Node} this This node
2503         * @param {Node} node The child node to be removed
2504         */
2505        "beforeremove" : true,
2506        /**
2507         * @event beforemove
2508         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2509         * @param {Tree} tree The owner tree
2510         * @param {Node} this This node
2511         * @param {Node} oldParent The parent of this node
2512         * @param {Node} newParent The new parent this node is moving to
2513         * @param {Number} index The index it is being moved to
2514         */
2515        "beforemove" : true,
2516        /**
2517         * @event beforeinsert
2518         * Fires before a new child is inserted, return false to cancel the insert.
2519         * @param {Tree} tree The owner tree
2520         * @param {Node} this This node
2521         * @param {Node} node The child node to be inserted
2522         * @param {Node} refNode The child node the node is being inserted before
2523         */
2524        "beforeinsert" : true
2525    });
2526     this.listeners = this.attributes.listeners;
2527     Roo.data.Node.superclass.constructor.call(this);
2528 };
2529
2530 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2531     fireEvent : function(evtName){
2532         // first do standard event for this node
2533         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2534             return false;
2535         }
2536         // then bubble it up to the tree if the event wasn't cancelled
2537         var ot = this.getOwnerTree();
2538         if(ot){
2539             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2540                 return false;
2541             }
2542         }
2543         return true;
2544     },
2545
2546     /**
2547      * Returns true if this node is a leaf
2548      * @return {Boolean}
2549      */
2550     isLeaf : function(){
2551         return this.leaf === true;
2552     },
2553
2554     // private
2555     setFirstChild : function(node){
2556         this.firstChild = node;
2557     },
2558
2559     //private
2560     setLastChild : function(node){
2561         this.lastChild = node;
2562     },
2563
2564
2565     /**
2566      * Returns true if this node is the last child of its parent
2567      * @return {Boolean}
2568      */
2569     isLast : function(){
2570        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2571     },
2572
2573     /**
2574      * Returns true if this node is the first child of its parent
2575      * @return {Boolean}
2576      */
2577     isFirst : function(){
2578        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2579     },
2580
2581     hasChildNodes : function(){
2582         return !this.isLeaf() && this.childNodes.length > 0;
2583     },
2584
2585     /**
2586      * Insert node(s) as the last child node of this node.
2587      * @param {Node/Array} node The node or Array of nodes to append
2588      * @return {Node} The appended node if single append, or null if an array was passed
2589      */
2590     appendChild : function(node){
2591         var multi = false;
2592         if(node instanceof Array){
2593             multi = node;
2594         }else if(arguments.length > 1){
2595             multi = arguments;
2596         }
2597         
2598         // if passed an array or multiple args do them one by one
2599         if(multi){
2600             for(var i = 0, len = multi.length; i < len; i++) {
2601                 this.appendChild(multi[i]);
2602             }
2603         }else{
2604             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2605                 return false;
2606             }
2607             var index = this.childNodes.length;
2608             var oldParent = node.parentNode;
2609             // it's a move, make sure we move it cleanly
2610             if(oldParent){
2611                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2612                     return false;
2613                 }
2614                 oldParent.removeChild(node);
2615             }
2616             
2617             index = this.childNodes.length;
2618             if(index == 0){
2619                 this.setFirstChild(node);
2620             }
2621             this.childNodes.push(node);
2622             node.parentNode = this;
2623             var ps = this.childNodes[index-1];
2624             if(ps){
2625                 node.previousSibling = ps;
2626                 ps.nextSibling = node;
2627             }else{
2628                 node.previousSibling = null;
2629             }
2630             node.nextSibling = null;
2631             this.setLastChild(node);
2632             node.setOwnerTree(this.getOwnerTree());
2633             this.fireEvent("append", this.ownerTree, this, node, index);
2634             if(this.ownerTree) {
2635                 this.ownerTree.fireEvent("appendnode", this, node, index);
2636             }
2637             if(oldParent){
2638                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2639             }
2640             return node;
2641         }
2642     },
2643
2644     /**
2645      * Removes a child node from this node.
2646      * @param {Node} node The node to remove
2647      * @return {Node} The removed node
2648      */
2649     removeChild : function(node){
2650         var index = this.childNodes.indexOf(node);
2651         if(index == -1){
2652             return false;
2653         }
2654         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2655             return false;
2656         }
2657
2658         // remove it from childNodes collection
2659         this.childNodes.splice(index, 1);
2660
2661         // update siblings
2662         if(node.previousSibling){
2663             node.previousSibling.nextSibling = node.nextSibling;
2664         }
2665         if(node.nextSibling){
2666             node.nextSibling.previousSibling = node.previousSibling;
2667         }
2668
2669         // update child refs
2670         if(this.firstChild == node){
2671             this.setFirstChild(node.nextSibling);
2672         }
2673         if(this.lastChild == node){
2674             this.setLastChild(node.previousSibling);
2675         }
2676
2677         node.setOwnerTree(null);
2678         // clear any references from the node
2679         node.parentNode = null;
2680         node.previousSibling = null;
2681         node.nextSibling = null;
2682         this.fireEvent("remove", this.ownerTree, this, node);
2683         return node;
2684     },
2685
2686     /**
2687      * Inserts the first node before the second node in this nodes childNodes collection.
2688      * @param {Node} node The node to insert
2689      * @param {Node} refNode The node to insert before (if null the node is appended)
2690      * @return {Node} The inserted node
2691      */
2692     insertBefore : function(node, refNode){
2693         if(!refNode){ // like standard Dom, refNode can be null for append
2694             return this.appendChild(node);
2695         }
2696         // nothing to do
2697         if(node == refNode){
2698             return false;
2699         }
2700
2701         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2702             return false;
2703         }
2704         var index = this.childNodes.indexOf(refNode);
2705         var oldParent = node.parentNode;
2706         var refIndex = index;
2707
2708         // when moving internally, indexes will change after remove
2709         if(oldParent == this && this.childNodes.indexOf(node) < index){
2710             refIndex--;
2711         }
2712
2713         // it's a move, make sure we move it cleanly
2714         if(oldParent){
2715             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2716                 return false;
2717             }
2718             oldParent.removeChild(node);
2719         }
2720         if(refIndex == 0){
2721             this.setFirstChild(node);
2722         }
2723         this.childNodes.splice(refIndex, 0, node);
2724         node.parentNode = this;
2725         var ps = this.childNodes[refIndex-1];
2726         if(ps){
2727             node.previousSibling = ps;
2728             ps.nextSibling = node;
2729         }else{
2730             node.previousSibling = null;
2731         }
2732         node.nextSibling = refNode;
2733         refNode.previousSibling = node;
2734         node.setOwnerTree(this.getOwnerTree());
2735         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2736         if(oldParent){
2737             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2738         }
2739         return node;
2740     },
2741
2742     /**
2743      * Returns the child node at the specified index.
2744      * @param {Number} index
2745      * @return {Node}
2746      */
2747     item : function(index){
2748         return this.childNodes[index];
2749     },
2750
2751     /**
2752      * Replaces one child node in this node with another.
2753      * @param {Node} newChild The replacement node
2754      * @param {Node} oldChild The node to replace
2755      * @return {Node} The replaced node
2756      */
2757     replaceChild : function(newChild, oldChild){
2758         this.insertBefore(newChild, oldChild);
2759         this.removeChild(oldChild);
2760         return oldChild;
2761     },
2762
2763     /**
2764      * Returns the index of a child node
2765      * @param {Node} node
2766      * @return {Number} The index of the node or -1 if it was not found
2767      */
2768     indexOf : function(child){
2769         return this.childNodes.indexOf(child);
2770     },
2771
2772     /**
2773      * Returns the tree this node is in.
2774      * @return {Tree}
2775      */
2776     getOwnerTree : function(){
2777         // if it doesn't have one, look for one
2778         if(!this.ownerTree){
2779             var p = this;
2780             while(p){
2781                 if(p.ownerTree){
2782                     this.ownerTree = p.ownerTree;
2783                     break;
2784                 }
2785                 p = p.parentNode;
2786             }
2787         }
2788         return this.ownerTree;
2789     },
2790
2791     /**
2792      * Returns depth of this node (the root node has a depth of 0)
2793      * @return {Number}
2794      */
2795     getDepth : function(){
2796         var depth = 0;
2797         var p = this;
2798         while(p.parentNode){
2799             ++depth;
2800             p = p.parentNode;
2801         }
2802         return depth;
2803     },
2804
2805     // private
2806     setOwnerTree : function(tree){
2807         // if it's move, we need to update everyone
2808         if(tree != this.ownerTree){
2809             if(this.ownerTree){
2810                 this.ownerTree.unregisterNode(this);
2811             }
2812             this.ownerTree = tree;
2813             var cs = this.childNodes;
2814             for(var i = 0, len = cs.length; i < len; i++) {
2815                 cs[i].setOwnerTree(tree);
2816             }
2817             if(tree){
2818                 tree.registerNode(this);
2819             }
2820         }
2821     },
2822
2823     /**
2824      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2825      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2826      * @return {String} The path
2827      */
2828     getPath : function(attr){
2829         attr = attr || "id";
2830         var p = this.parentNode;
2831         var b = [this.attributes[attr]];
2832         while(p){
2833             b.unshift(p.attributes[attr]);
2834             p = p.parentNode;
2835         }
2836         var sep = this.getOwnerTree().pathSeparator;
2837         return sep + b.join(sep);
2838     },
2839
2840     /**
2841      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2842      * function call will be the scope provided or the current node. The arguments to the function
2843      * will be the args provided or the current node. If the function returns false at any point,
2844      * the bubble is stopped.
2845      * @param {Function} fn The function to call
2846      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2847      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2848      */
2849     bubble : function(fn, scope, args){
2850         var p = this;
2851         while(p){
2852             if(fn.call(scope || p, args || p) === false){
2853                 break;
2854             }
2855             p = p.parentNode;
2856         }
2857     },
2858
2859     /**
2860      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2861      * function call will be the scope provided or the current node. The arguments to the function
2862      * will be the args provided or the current node. If the function returns false at any point,
2863      * the cascade is stopped on that branch.
2864      * @param {Function} fn The function to call
2865      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2866      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2867      */
2868     cascade : function(fn, scope, args){
2869         if(fn.call(scope || this, args || this) !== false){
2870             var cs = this.childNodes;
2871             for(var i = 0, len = cs.length; i < len; i++) {
2872                 cs[i].cascade(fn, scope, args);
2873             }
2874         }
2875     },
2876
2877     /**
2878      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2879      * function call will be the scope provided or the current node. The arguments to the function
2880      * will be the args provided or the current node. If the function returns false at any point,
2881      * the iteration stops.
2882      * @param {Function} fn The function to call
2883      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2884      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2885      */
2886     eachChild : function(fn, scope, args){
2887         var cs = this.childNodes;
2888         for(var i = 0, len = cs.length; i < len; i++) {
2889                 if(fn.call(scope || this, args || cs[i]) === false){
2890                     break;
2891                 }
2892         }
2893     },
2894
2895     /**
2896      * Finds the first child that has the attribute with the specified value.
2897      * @param {String} attribute The attribute name
2898      * @param {Mixed} value The value to search for
2899      * @return {Node} The found child or null if none was found
2900      */
2901     findChild : function(attribute, value){
2902         var cs = this.childNodes;
2903         for(var i = 0, len = cs.length; i < len; i++) {
2904                 if(cs[i].attributes[attribute] == value){
2905                     return cs[i];
2906                 }
2907         }
2908         return null;
2909     },
2910
2911     /**
2912      * Finds the first child by a custom function. The child matches if the function passed
2913      * returns true.
2914      * @param {Function} fn
2915      * @param {Object} scope (optional)
2916      * @return {Node} The found child or null if none was found
2917      */
2918     findChildBy : function(fn, scope){
2919         var cs = this.childNodes;
2920         for(var i = 0, len = cs.length; i < len; i++) {
2921                 if(fn.call(scope||cs[i], cs[i]) === true){
2922                     return cs[i];
2923                 }
2924         }
2925         return null;
2926     },
2927
2928     /**
2929      * Sorts this nodes children using the supplied sort function
2930      * @param {Function} fn
2931      * @param {Object} scope (optional)
2932      */
2933     sort : function(fn, scope){
2934         var cs = this.childNodes;
2935         var len = cs.length;
2936         if(len > 0){
2937             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2938             cs.sort(sortFn);
2939             for(var i = 0; i < len; i++){
2940                 var n = cs[i];
2941                 n.previousSibling = cs[i-1];
2942                 n.nextSibling = cs[i+1];
2943                 if(i == 0){
2944                     this.setFirstChild(n);
2945                 }
2946                 if(i == len-1){
2947                     this.setLastChild(n);
2948                 }
2949             }
2950         }
2951     },
2952
2953     /**
2954      * Returns true if this node is an ancestor (at any point) of the passed node.
2955      * @param {Node} node
2956      * @return {Boolean}
2957      */
2958     contains : function(node){
2959         return node.isAncestor(this);
2960     },
2961
2962     /**
2963      * Returns true if the passed node is an ancestor (at any point) of this node.
2964      * @param {Node} node
2965      * @return {Boolean}
2966      */
2967     isAncestor : function(node){
2968         var p = this.parentNode;
2969         while(p){
2970             if(p == node){
2971                 return true;
2972             }
2973             p = p.parentNode;
2974         }
2975         return false;
2976     },
2977
2978     toString : function(){
2979         return "[Node"+(this.id?" "+this.id:"")+"]";
2980     }
2981 });/*
2982  * Based on:
2983  * Ext JS Library 1.1.1
2984  * Copyright(c) 2006-2007, Ext JS, LLC.
2985  *
2986  * Originally Released Under LGPL - original licence link has changed is not relivant.
2987  *
2988  * Fork - LGPL
2989  * <script type="text/javascript">
2990  */
2991
2992
2993 /**
2994  * @class Roo.Shadow
2995  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
2996  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
2997  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2998  * @constructor
2999  * Create a new Shadow
3000  * @param {Object} config The config object
3001  */
3002 Roo.Shadow = function(config){
3003     Roo.apply(this, config);
3004     if(typeof this.mode != "string"){
3005         this.mode = this.defaultMode;
3006     }
3007     var o = this.offset, a = {h: 0};
3008     var rad = Math.floor(this.offset/2);
3009     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3010         case "drop":
3011             a.w = 0;
3012             a.l = a.t = o;
3013             a.t -= 1;
3014             if(Roo.isIE){
3015                 a.l -= this.offset + rad;
3016                 a.t -= this.offset + rad;
3017                 a.w -= rad;
3018                 a.h -= rad;
3019                 a.t += 1;
3020             }
3021         break;
3022         case "sides":
3023             a.w = (o*2);
3024             a.l = -o;
3025             a.t = o-1;
3026             if(Roo.isIE){
3027                 a.l -= (this.offset - rad);
3028                 a.t -= this.offset + rad;
3029                 a.l += 1;
3030                 a.w -= (this.offset - rad)*2;
3031                 a.w -= rad + 1;
3032                 a.h -= 1;
3033             }
3034         break;
3035         case "frame":
3036             a.w = a.h = (o*2);
3037             a.l = a.t = -o;
3038             a.t += 1;
3039             a.h -= 2;
3040             if(Roo.isIE){
3041                 a.l -= (this.offset - rad);
3042                 a.t -= (this.offset - rad);
3043                 a.l += 1;
3044                 a.w -= (this.offset + rad + 1);
3045                 a.h -= (this.offset + rad);
3046                 a.h += 1;
3047             }
3048         break;
3049     };
3050
3051     this.adjusts = a;
3052 };
3053
3054 Roo.Shadow.prototype = {
3055     /**
3056      * @cfg {String} mode
3057      * The shadow display mode.  Supports the following options:<br />
3058      * sides: Shadow displays on both sides and bottom only<br />
3059      * frame: Shadow displays equally on all four sides<br />
3060      * drop: Traditional bottom-right drop shadow (default)
3061      */
3062     mode: false,
3063     /**
3064      * @cfg {String} offset
3065      * The number of pixels to offset the shadow from the element (defaults to 4)
3066      */
3067     offset: 4,
3068
3069     // private
3070     defaultMode: "drop",
3071
3072     /**
3073      * Displays the shadow under the target element
3074      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3075      */
3076     show : function(target){
3077         target = Roo.get(target);
3078         if(!this.el){
3079             this.el = Roo.Shadow.Pool.pull();
3080             if(this.el.dom.nextSibling != target.dom){
3081                 this.el.insertBefore(target);
3082             }
3083         }
3084         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3085         if(Roo.isIE){
3086             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3087         }
3088         this.realign(
3089             target.getLeft(true),
3090             target.getTop(true),
3091             target.getWidth(),
3092             target.getHeight()
3093         );
3094         this.el.dom.style.display = "block";
3095     },
3096
3097     /**
3098      * Returns true if the shadow is visible, else false
3099      */
3100     isVisible : function(){
3101         return this.el ? true : false;  
3102     },
3103
3104     /**
3105      * Direct alignment when values are already available. Show must be called at least once before
3106      * calling this method to ensure it is initialized.
3107      * @param {Number} left The target element left position
3108      * @param {Number} top The target element top position
3109      * @param {Number} width The target element width
3110      * @param {Number} height The target element height
3111      */
3112     realign : function(l, t, w, h){
3113         if(!this.el){
3114             return;
3115         }
3116         var a = this.adjusts, d = this.el.dom, s = d.style;
3117         var iea = 0;
3118         s.left = (l+a.l)+"px";
3119         s.top = (t+a.t)+"px";
3120         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3121  
3122         if(s.width != sws || s.height != shs){
3123             s.width = sws;
3124             s.height = shs;
3125             if(!Roo.isIE){
3126                 var cn = d.childNodes;
3127                 var sww = Math.max(0, (sw-12))+"px";
3128                 cn[0].childNodes[1].style.width = sww;
3129                 cn[1].childNodes[1].style.width = sww;
3130                 cn[2].childNodes[1].style.width = sww;
3131                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3132             }
3133         }
3134     },
3135
3136     /**
3137      * Hides this shadow
3138      */
3139     hide : function(){
3140         if(this.el){
3141             this.el.dom.style.display = "none";
3142             Roo.Shadow.Pool.push(this.el);
3143             delete this.el;
3144         }
3145     },
3146
3147     /**
3148      * Adjust the z-index of this shadow
3149      * @param {Number} zindex The new z-index
3150      */
3151     setZIndex : function(z){
3152         this.zIndex = z;
3153         if(this.el){
3154             this.el.setStyle("z-index", z);
3155         }
3156     }
3157 };
3158
3159 // Private utility class that manages the internal Shadow cache
3160 Roo.Shadow.Pool = function(){
3161     var p = [];
3162     var markup = Roo.isIE ?
3163                  '<div class="x-ie-shadow"></div>' :
3164                  '<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>';
3165     return {
3166         pull : function(){
3167             var sh = p.shift();
3168             if(!sh){
3169                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3170                 sh.autoBoxAdjust = false;
3171             }
3172             return sh;
3173         },
3174
3175         push : function(sh){
3176             p.push(sh);
3177         }
3178     };
3179 }();/*
3180  * Based on:
3181  * Ext JS Library 1.1.1
3182  * Copyright(c) 2006-2007, Ext JS, LLC.
3183  *
3184  * Originally Released Under LGPL - original licence link has changed is not relivant.
3185  *
3186  * Fork - LGPL
3187  * <script type="text/javascript">
3188  */
3189
3190
3191 /**
3192  * @class Roo.SplitBar
3193  * @extends Roo.util.Observable
3194  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3195  * <br><br>
3196  * Usage:
3197  * <pre><code>
3198 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3199                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3200 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3201 split.minSize = 100;
3202 split.maxSize = 600;
3203 split.animate = true;
3204 split.on('moved', splitterMoved);
3205 </code></pre>
3206  * @constructor
3207  * Create a new SplitBar
3208  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3209  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3210  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3211  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3212                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3213                         position of the SplitBar).
3214  */
3215 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3216     
3217     /** @private */
3218     this.el = Roo.get(dragElement, true);
3219     this.el.dom.unselectable = "on";
3220     /** @private */
3221     this.resizingEl = Roo.get(resizingElement, true);
3222
3223     /**
3224      * @private
3225      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3226      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3227      * @type Number
3228      */
3229     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3230     
3231     /**
3232      * The minimum size of the resizing element. (Defaults to 0)
3233      * @type Number
3234      */
3235     this.minSize = 0;
3236     
3237     /**
3238      * The maximum size of the resizing element. (Defaults to 2000)
3239      * @type Number
3240      */
3241     this.maxSize = 2000;
3242     
3243     /**
3244      * Whether to animate the transition to the new size
3245      * @type Boolean
3246      */
3247     this.animate = false;
3248     
3249     /**
3250      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3251      * @type Boolean
3252      */
3253     this.useShim = false;
3254     
3255     /** @private */
3256     this.shim = null;
3257     
3258     if(!existingProxy){
3259         /** @private */
3260         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3261     }else{
3262         this.proxy = Roo.get(existingProxy).dom;
3263     }
3264     /** @private */
3265     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3266     
3267     /** @private */
3268     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3269     
3270     /** @private */
3271     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3272     
3273     /** @private */
3274     this.dragSpecs = {};
3275     
3276     /**
3277      * @private The adapter to use to positon and resize elements
3278      */
3279     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3280     this.adapter.init(this);
3281     
3282     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3283         /** @private */
3284         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3285         this.el.addClass("x-splitbar-h");
3286     }else{
3287         /** @private */
3288         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3289         this.el.addClass("x-splitbar-v");
3290     }
3291     
3292     this.addEvents({
3293         /**
3294          * @event resize
3295          * Fires when the splitter is moved (alias for {@link #event-moved})
3296          * @param {Roo.SplitBar} this
3297          * @param {Number} newSize the new width or height
3298          */
3299         "resize" : true,
3300         /**
3301          * @event moved
3302          * Fires when the splitter is moved
3303          * @param {Roo.SplitBar} this
3304          * @param {Number} newSize the new width or height
3305          */
3306         "moved" : true,
3307         /**
3308          * @event beforeresize
3309          * Fires before the splitter is dragged
3310          * @param {Roo.SplitBar} this
3311          */
3312         "beforeresize" : true,
3313
3314         "beforeapply" : true
3315     });
3316
3317     Roo.util.Observable.call(this);
3318 };
3319
3320 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3321     onStartProxyDrag : function(x, y){
3322         this.fireEvent("beforeresize", this);
3323         if(!this.overlay){
3324             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3325             o.unselectable();
3326             o.enableDisplayMode("block");
3327             // all splitbars share the same overlay
3328             Roo.SplitBar.prototype.overlay = o;
3329         }
3330         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3331         this.overlay.show();
3332         Roo.get(this.proxy).setDisplayed("block");
3333         var size = this.adapter.getElementSize(this);
3334         this.activeMinSize = this.getMinimumSize();;
3335         this.activeMaxSize = this.getMaximumSize();;
3336         var c1 = size - this.activeMinSize;
3337         var c2 = Math.max(this.activeMaxSize - size, 0);
3338         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3339             this.dd.resetConstraints();
3340             this.dd.setXConstraint(
3341                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3342                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3343             );
3344             this.dd.setYConstraint(0, 0);
3345         }else{
3346             this.dd.resetConstraints();
3347             this.dd.setXConstraint(0, 0);
3348             this.dd.setYConstraint(
3349                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3350                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3351             );
3352          }
3353         this.dragSpecs.startSize = size;
3354         this.dragSpecs.startPoint = [x, y];
3355         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3356     },
3357     
3358     /** 
3359      * @private Called after the drag operation by the DDProxy
3360      */
3361     onEndProxyDrag : function(e){
3362         Roo.get(this.proxy).setDisplayed(false);
3363         var endPoint = Roo.lib.Event.getXY(e);
3364         if(this.overlay){
3365             this.overlay.hide();
3366         }
3367         var newSize;
3368         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3369             newSize = this.dragSpecs.startSize + 
3370                 (this.placement == Roo.SplitBar.LEFT ?
3371                     endPoint[0] - this.dragSpecs.startPoint[0] :
3372                     this.dragSpecs.startPoint[0] - endPoint[0]
3373                 );
3374         }else{
3375             newSize = this.dragSpecs.startSize + 
3376                 (this.placement == Roo.SplitBar.TOP ?
3377                     endPoint[1] - this.dragSpecs.startPoint[1] :
3378                     this.dragSpecs.startPoint[1] - endPoint[1]
3379                 );
3380         }
3381         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3382         if(newSize != this.dragSpecs.startSize){
3383             if(this.fireEvent('beforeapply', this, newSize) !== false){
3384                 this.adapter.setElementSize(this, newSize);
3385                 this.fireEvent("moved", this, newSize);
3386                 this.fireEvent("resize", this, newSize);
3387             }
3388         }
3389     },
3390     
3391     /**
3392      * Get the adapter this SplitBar uses
3393      * @return The adapter object
3394      */
3395     getAdapter : function(){
3396         return this.adapter;
3397     },
3398     
3399     /**
3400      * Set the adapter this SplitBar uses
3401      * @param {Object} adapter A SplitBar adapter object
3402      */
3403     setAdapter : function(adapter){
3404         this.adapter = adapter;
3405         this.adapter.init(this);
3406     },
3407     
3408     /**
3409      * Gets the minimum size for the resizing element
3410      * @return {Number} The minimum size
3411      */
3412     getMinimumSize : function(){
3413         return this.minSize;
3414     },
3415     
3416     /**
3417      * Sets the minimum size for the resizing element
3418      * @param {Number} minSize The minimum size
3419      */
3420     setMinimumSize : function(minSize){
3421         this.minSize = minSize;
3422     },
3423     
3424     /**
3425      * Gets the maximum size for the resizing element
3426      * @return {Number} The maximum size
3427      */
3428     getMaximumSize : function(){
3429         return this.maxSize;
3430     },
3431     
3432     /**
3433      * Sets the maximum size for the resizing element
3434      * @param {Number} maxSize The maximum size
3435      */
3436     setMaximumSize : function(maxSize){
3437         this.maxSize = maxSize;
3438     },
3439     
3440     /**
3441      * Sets the initialize size for the resizing element
3442      * @param {Number} size The initial size
3443      */
3444     setCurrentSize : function(size){
3445         var oldAnimate = this.animate;
3446         this.animate = false;
3447         this.adapter.setElementSize(this, size);
3448         this.animate = oldAnimate;
3449     },
3450     
3451     /**
3452      * Destroy this splitbar. 
3453      * @param {Boolean} removeEl True to remove the element
3454      */
3455     destroy : function(removeEl){
3456         if(this.shim){
3457             this.shim.remove();
3458         }
3459         this.dd.unreg();
3460         this.proxy.parentNode.removeChild(this.proxy);
3461         if(removeEl){
3462             this.el.remove();
3463         }
3464     }
3465 });
3466
3467 /**
3468  * @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.
3469  */
3470 Roo.SplitBar.createProxy = function(dir){
3471     var proxy = new Roo.Element(document.createElement("div"));
3472     proxy.unselectable();
3473     var cls = 'x-splitbar-proxy';
3474     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3475     document.body.appendChild(proxy.dom);
3476     return proxy.dom;
3477 };
3478
3479 /** 
3480  * @class Roo.SplitBar.BasicLayoutAdapter
3481  * Default Adapter. It assumes the splitter and resizing element are not positioned
3482  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3483  */
3484 Roo.SplitBar.BasicLayoutAdapter = function(){
3485 };
3486
3487 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3488     // do nothing for now
3489     init : function(s){
3490     
3491     },
3492     /**
3493      * Called before drag operations to get the current size of the resizing element. 
3494      * @param {Roo.SplitBar} s The SplitBar using this adapter
3495      */
3496      getElementSize : function(s){
3497         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3498             return s.resizingEl.getWidth();
3499         }else{
3500             return s.resizingEl.getHeight();
3501         }
3502     },
3503     
3504     /**
3505      * Called after drag operations to set the size of the resizing element.
3506      * @param {Roo.SplitBar} s The SplitBar using this adapter
3507      * @param {Number} newSize The new size to set
3508      * @param {Function} onComplete A function to be invoked when resizing is complete
3509      */
3510     setElementSize : function(s, newSize, onComplete){
3511         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3512             if(!s.animate){
3513                 s.resizingEl.setWidth(newSize);
3514                 if(onComplete){
3515                     onComplete(s, newSize);
3516                 }
3517             }else{
3518                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3519             }
3520         }else{
3521             
3522             if(!s.animate){
3523                 s.resizingEl.setHeight(newSize);
3524                 if(onComplete){
3525                     onComplete(s, newSize);
3526                 }
3527             }else{
3528                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3529             }
3530         }
3531     }
3532 };
3533
3534 /** 
3535  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3536  * @extends Roo.SplitBar.BasicLayoutAdapter
3537  * Adapter that  moves the splitter element to align with the resized sizing element. 
3538  * Used with an absolute positioned SplitBar.
3539  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3540  * document.body, make sure you assign an id to the body element.
3541  */
3542 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3543     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3544     this.container = Roo.get(container);
3545 };
3546
3547 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3548     init : function(s){
3549         this.basic.init(s);
3550     },
3551     
3552     getElementSize : function(s){
3553         return this.basic.getElementSize(s);
3554     },
3555     
3556     setElementSize : function(s, newSize, onComplete){
3557         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3558     },
3559     
3560     moveSplitter : function(s){
3561         var yes = Roo.SplitBar;
3562         switch(s.placement){
3563             case yes.LEFT:
3564                 s.el.setX(s.resizingEl.getRight());
3565                 break;
3566             case yes.RIGHT:
3567                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3568                 break;
3569             case yes.TOP:
3570                 s.el.setY(s.resizingEl.getBottom());
3571                 break;
3572             case yes.BOTTOM:
3573                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3574                 break;
3575         }
3576     }
3577 };
3578
3579 /**
3580  * Orientation constant - Create a vertical SplitBar
3581  * @static
3582  * @type Number
3583  */
3584 Roo.SplitBar.VERTICAL = 1;
3585
3586 /**
3587  * Orientation constant - Create a horizontal SplitBar
3588  * @static
3589  * @type Number
3590  */
3591 Roo.SplitBar.HORIZONTAL = 2;
3592
3593 /**
3594  * Placement constant - The resizing element is to the left of the splitter element
3595  * @static
3596  * @type Number
3597  */
3598 Roo.SplitBar.LEFT = 1;
3599
3600 /**
3601  * Placement constant - The resizing element is to the right of the splitter element
3602  * @static
3603  * @type Number
3604  */
3605 Roo.SplitBar.RIGHT = 2;
3606
3607 /**
3608  * Placement constant - The resizing element is positioned above the splitter element
3609  * @static
3610  * @type Number
3611  */
3612 Roo.SplitBar.TOP = 3;
3613
3614 /**
3615  * Placement constant - The resizing element is positioned under splitter element
3616  * @static
3617  * @type Number
3618  */
3619 Roo.SplitBar.BOTTOM = 4;
3620 /*
3621  * Based on:
3622  * Ext JS Library 1.1.1
3623  * Copyright(c) 2006-2007, Ext JS, LLC.
3624  *
3625  * Originally Released Under LGPL - original licence link has changed is not relivant.
3626  *
3627  * Fork - LGPL
3628  * <script type="text/javascript">
3629  */
3630
3631 /**
3632  * @class Roo.View
3633  * @extends Roo.util.Observable
3634  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3635  * This class also supports single and multi selection modes. <br>
3636  * Create a data model bound view:
3637  <pre><code>
3638  var store = new Roo.data.Store(...);
3639
3640  var view = new Roo.View({
3641     el : "my-element",
3642     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3643  
3644     singleSelect: true,
3645     selectedClass: "ydataview-selected",
3646     store: store
3647  });
3648
3649  // listen for node click?
3650  view.on("click", function(vw, index, node, e){
3651  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3652  });
3653
3654  // load XML data
3655  dataModel.load("foobar.xml");
3656  </code></pre>
3657  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3658  * <br><br>
3659  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3660  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3661  * 
3662  * Note: old style constructor is still suported (container, template, config)
3663  * 
3664  * @constructor
3665  * Create a new View
3666  * @param {Object} config The config object
3667  * 
3668  */
3669 Roo.View = function(config, depreciated_tpl, depreciated_config){
3670     
3671     this.parent = false;
3672     
3673     if (typeof(depreciated_tpl) == 'undefined') {
3674         // new way.. - universal constructor.
3675         Roo.apply(this, config);
3676         this.el  = Roo.get(this.el);
3677     } else {
3678         // old format..
3679         this.el  = Roo.get(config);
3680         this.tpl = depreciated_tpl;
3681         Roo.apply(this, depreciated_config);
3682     }
3683     this.wrapEl  = this.el.wrap().wrap();
3684     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3685     
3686     
3687     if(typeof(this.tpl) == "string"){
3688         this.tpl = new Roo.Template(this.tpl);
3689     } else {
3690         // support xtype ctors..
3691         this.tpl = new Roo.factory(this.tpl, Roo);
3692     }
3693     
3694     
3695     this.tpl.compile();
3696     
3697     /** @private */
3698     this.addEvents({
3699         /**
3700          * @event beforeclick
3701          * Fires before a click is processed. Returns false to cancel the default action.
3702          * @param {Roo.View} this
3703          * @param {Number} index The index of the target node
3704          * @param {HTMLElement} node The target node
3705          * @param {Roo.EventObject} e The raw event object
3706          */
3707             "beforeclick" : true,
3708         /**
3709          * @event click
3710          * Fires when a template node is clicked.
3711          * @param {Roo.View} this
3712          * @param {Number} index The index of the target node
3713          * @param {HTMLElement} node The target node
3714          * @param {Roo.EventObject} e The raw event object
3715          */
3716             "click" : true,
3717         /**
3718          * @event dblclick
3719          * Fires when a template node is double clicked.
3720          * @param {Roo.View} this
3721          * @param {Number} index The index of the target node
3722          * @param {HTMLElement} node The target node
3723          * @param {Roo.EventObject} e The raw event object
3724          */
3725             "dblclick" : true,
3726         /**
3727          * @event contextmenu
3728          * Fires when a template node is right clicked.
3729          * @param {Roo.View} this
3730          * @param {Number} index The index of the target node
3731          * @param {HTMLElement} node The target node
3732          * @param {Roo.EventObject} e The raw event object
3733          */
3734             "contextmenu" : true,
3735         /**
3736          * @event selectionchange
3737          * Fires when the selected nodes change.
3738          * @param {Roo.View} this
3739          * @param {Array} selections Array of the selected nodes
3740          */
3741             "selectionchange" : true,
3742     
3743         /**
3744          * @event beforeselect
3745          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3746          * @param {Roo.View} this
3747          * @param {HTMLElement} node The node to be selected
3748          * @param {Array} selections Array of currently selected nodes
3749          */
3750             "beforeselect" : true,
3751         /**
3752          * @event preparedata
3753          * Fires on every row to render, to allow you to change the data.
3754          * @param {Roo.View} this
3755          * @param {Object} data to be rendered (change this)
3756          */
3757           "preparedata" : true
3758           
3759           
3760         });
3761
3762
3763
3764     this.el.on({
3765         "click": this.onClick,
3766         "dblclick": this.onDblClick,
3767         "contextmenu": this.onContextMenu,
3768         scope:this
3769     });
3770
3771     this.selections = [];
3772     this.nodes = [];
3773     this.cmp = new Roo.CompositeElementLite([]);
3774     if(this.store){
3775         this.store = Roo.factory(this.store, Roo.data);
3776         this.setStore(this.store, true);
3777     }
3778     
3779     if ( this.footer && this.footer.xtype) {
3780            
3781          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3782         
3783         this.footer.dataSource = this.store;
3784         this.footer.container = fctr;
3785         this.footer = Roo.factory(this.footer, Roo);
3786         fctr.insertFirst(this.el);
3787         
3788         // this is a bit insane - as the paging toolbar seems to detach the el..
3789 //        dom.parentNode.parentNode.parentNode
3790          // they get detached?
3791     }
3792     
3793     
3794     Roo.View.superclass.constructor.call(this);
3795     
3796     
3797 };
3798
3799 Roo.extend(Roo.View, Roo.util.Observable, {
3800     
3801      /**
3802      * @cfg {Roo.data.Store} store Data store to load data from.
3803      */
3804     store : false,
3805     
3806     /**
3807      * @cfg {String|Roo.Element} el The container element.
3808      */
3809     el : '',
3810     
3811     /**
3812      * @cfg {String|Roo.Template} tpl The template used by this View 
3813      */
3814     tpl : false,
3815     /**
3816      * @cfg {String} dataName the named area of the template to use as the data area
3817      *                          Works with domtemplates roo-name="name"
3818      */
3819     dataName: false,
3820     /**
3821      * @cfg {String} selectedClass The css class to add to selected nodes
3822      */
3823     selectedClass : "x-view-selected",
3824      /**
3825      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3826      */
3827     emptyText : "",
3828     
3829     /**
3830      * @cfg {String} text to display on mask (default Loading)
3831      */
3832     mask : false,
3833     /**
3834      * @cfg {Boolean} multiSelect Allow multiple selection
3835      */
3836     multiSelect : false,
3837     /**
3838      * @cfg {Boolean} singleSelect Allow single selection
3839      */
3840     singleSelect:  false,
3841     
3842     /**
3843      * @cfg {Boolean} toggleSelect - selecting 
3844      */
3845     toggleSelect : false,
3846     
3847     /**
3848      * @cfg {Boolean} tickable - selecting 
3849      */
3850     tickable : false,
3851     
3852     /**
3853      * Returns the element this view is bound to.
3854      * @return {Roo.Element}
3855      */
3856     getEl : function(){
3857         return this.wrapEl;
3858     },
3859     
3860     
3861
3862     /**
3863      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3864      */
3865     refresh : function(){
3866         //Roo.log('refresh');
3867         var t = this.tpl;
3868         
3869         // if we are using something like 'domtemplate', then
3870         // the what gets used is:
3871         // t.applySubtemplate(NAME, data, wrapping data..)
3872         // the outer template then get' applied with
3873         //     the store 'extra data'
3874         // and the body get's added to the
3875         //      roo-name="data" node?
3876         //      <span class='roo-tpl-{name}'></span> ?????
3877         
3878         
3879         
3880         this.clearSelections();
3881         this.el.update("");
3882         var html = [];
3883         var records = this.store.getRange();
3884         if(records.length < 1) {
3885             
3886             // is this valid??  = should it render a template??
3887             
3888             this.el.update(this.emptyText);
3889             return;
3890         }
3891         var el = this.el;
3892         if (this.dataName) {
3893             this.el.update(t.apply(this.store.meta)); //????
3894             el = this.el.child('.roo-tpl-' + this.dataName);
3895         }
3896         
3897         for(var i = 0, len = records.length; i < len; i++){
3898             var data = this.prepareData(records[i].data, i, records[i]);
3899             this.fireEvent("preparedata", this, data, i, records[i]);
3900             
3901             var d = Roo.apply({}, data);
3902             
3903             if(this.tickable){
3904                 Roo.apply(d, {'roo-id' : Roo.id()});
3905                 
3906                 var _this = this;
3907             
3908                 Roo.each(this.parent.item, function(item){
3909                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3910                         return;
3911                     }
3912                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3913                 });
3914             }
3915             
3916             html[html.length] = Roo.util.Format.trim(
3917                 this.dataName ?
3918                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3919                     t.apply(d)
3920             );
3921         }
3922         
3923         
3924         
3925         el.update(html.join(""));
3926         this.nodes = el.dom.childNodes;
3927         this.updateIndexes(0);
3928     },
3929     
3930
3931     /**
3932      * Function to override to reformat the data that is sent to
3933      * the template for each node.
3934      * DEPRICATED - use the preparedata event handler.
3935      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3936      * a JSON object for an UpdateManager bound view).
3937      */
3938     prepareData : function(data, index, record)
3939     {
3940         this.fireEvent("preparedata", this, data, index, record);
3941         return data;
3942     },
3943
3944     onUpdate : function(ds, record){
3945         // Roo.log('on update');   
3946         this.clearSelections();
3947         var index = this.store.indexOf(record);
3948         var n = this.nodes[index];
3949         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3950         n.parentNode.removeChild(n);
3951         this.updateIndexes(index, index);
3952     },
3953
3954     
3955     
3956 // --------- FIXME     
3957     onAdd : function(ds, records, index)
3958     {
3959         //Roo.log(['on Add', ds, records, index] );        
3960         this.clearSelections();
3961         if(this.nodes.length == 0){
3962             this.refresh();
3963             return;
3964         }
3965         var n = this.nodes[index];
3966         for(var i = 0, len = records.length; i < len; i++){
3967             var d = this.prepareData(records[i].data, i, records[i]);
3968             if(n){
3969                 this.tpl.insertBefore(n, d);
3970             }else{
3971                 
3972                 this.tpl.append(this.el, d);
3973             }
3974         }
3975         this.updateIndexes(index);
3976     },
3977
3978     onRemove : function(ds, record, index){
3979        // Roo.log('onRemove');
3980         this.clearSelections();
3981         var el = this.dataName  ?
3982             this.el.child('.roo-tpl-' + this.dataName) :
3983             this.el; 
3984         
3985         el.dom.removeChild(this.nodes[index]);
3986         this.updateIndexes(index);
3987     },
3988
3989     /**
3990      * Refresh an individual node.
3991      * @param {Number} index
3992      */
3993     refreshNode : function(index){
3994         this.onUpdate(this.store, this.store.getAt(index));
3995     },
3996
3997     updateIndexes : function(startIndex, endIndex){
3998         var ns = this.nodes;
3999         startIndex = startIndex || 0;
4000         endIndex = endIndex || ns.length - 1;
4001         for(var i = startIndex; i <= endIndex; i++){
4002             ns[i].nodeIndex = i;
4003         }
4004     },
4005
4006     /**
4007      * Changes the data store this view uses and refresh the view.
4008      * @param {Store} store
4009      */
4010     setStore : function(store, initial){
4011         if(!initial && this.store){
4012             this.store.un("datachanged", this.refresh);
4013             this.store.un("add", this.onAdd);
4014             this.store.un("remove", this.onRemove);
4015             this.store.un("update", this.onUpdate);
4016             this.store.un("clear", this.refresh);
4017             this.store.un("beforeload", this.onBeforeLoad);
4018             this.store.un("load", this.onLoad);
4019             this.store.un("loadexception", this.onLoad);
4020         }
4021         if(store){
4022           
4023             store.on("datachanged", this.refresh, this);
4024             store.on("add", this.onAdd, this);
4025             store.on("remove", this.onRemove, this);
4026             store.on("update", this.onUpdate, this);
4027             store.on("clear", this.refresh, this);
4028             store.on("beforeload", this.onBeforeLoad, this);
4029             store.on("load", this.onLoad, this);
4030             store.on("loadexception", this.onLoad, this);
4031         }
4032         
4033         if(store){
4034             this.refresh();
4035         }
4036     },
4037     /**
4038      * onbeforeLoad - masks the loading area.
4039      *
4040      */
4041     onBeforeLoad : function(store,opts)
4042     {
4043          //Roo.log('onBeforeLoad');   
4044         if (!opts.add) {
4045             this.el.update("");
4046         }
4047         this.el.mask(this.mask ? this.mask : "Loading" ); 
4048     },
4049     onLoad : function ()
4050     {
4051         this.el.unmask();
4052     },
4053     
4054
4055     /**
4056      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4057      * @param {HTMLElement} node
4058      * @return {HTMLElement} The template node
4059      */
4060     findItemFromChild : function(node){
4061         var el = this.dataName  ?
4062             this.el.child('.roo-tpl-' + this.dataName,true) :
4063             this.el.dom; 
4064         
4065         if(!node || node.parentNode == el){
4066                     return node;
4067             }
4068             var p = node.parentNode;
4069             while(p && p != el){
4070             if(p.parentNode == el){
4071                 return p;
4072             }
4073             p = p.parentNode;
4074         }
4075             return null;
4076     },
4077
4078     /** @ignore */
4079     onClick : function(e){
4080         var item = this.findItemFromChild(e.getTarget());
4081         if(item){
4082             var index = this.indexOf(item);
4083             if(this.onItemClick(item, index, e) !== false){
4084                 this.fireEvent("click", this, index, item, e);
4085             }
4086         }else{
4087             this.clearSelections();
4088         }
4089     },
4090
4091     /** @ignore */
4092     onContextMenu : function(e){
4093         var item = this.findItemFromChild(e.getTarget());
4094         if(item){
4095             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4096         }
4097     },
4098
4099     /** @ignore */
4100     onDblClick : function(e){
4101         var item = this.findItemFromChild(e.getTarget());
4102         if(item){
4103             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4104         }
4105     },
4106
4107     onItemClick : function(item, index, e)
4108     {
4109         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4110             return false;
4111         }
4112         if (this.toggleSelect) {
4113             var m = this.isSelected(item) ? 'unselect' : 'select';
4114             //Roo.log(m);
4115             var _t = this;
4116             _t[m](item, true, false);
4117             return true;
4118         }
4119         if(this.multiSelect || this.singleSelect){
4120             if(this.multiSelect && e.shiftKey && this.lastSelection){
4121                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4122             }else{
4123                 this.select(item, this.multiSelect && e.ctrlKey);
4124                 this.lastSelection = item;
4125             }
4126             
4127             if(!this.tickable){
4128                 e.preventDefault();
4129             }
4130             
4131         }
4132         return true;
4133     },
4134
4135     /**
4136      * Get the number of selected nodes.
4137      * @return {Number}
4138      */
4139     getSelectionCount : function(){
4140         return this.selections.length;
4141     },
4142
4143     /**
4144      * Get the currently selected nodes.
4145      * @return {Array} An array of HTMLElements
4146      */
4147     getSelectedNodes : function(){
4148         return this.selections;
4149     },
4150
4151     /**
4152      * Get the indexes of the selected nodes.
4153      * @return {Array}
4154      */
4155     getSelectedIndexes : function(){
4156         var indexes = [], s = this.selections;
4157         for(var i = 0, len = s.length; i < len; i++){
4158             indexes.push(s[i].nodeIndex);
4159         }
4160         return indexes;
4161     },
4162
4163     /**
4164      * Clear all selections
4165      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4166      */
4167     clearSelections : function(suppressEvent){
4168         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4169             this.cmp.elements = this.selections;
4170             this.cmp.removeClass(this.selectedClass);
4171             this.selections = [];
4172             if(!suppressEvent){
4173                 this.fireEvent("selectionchange", this, this.selections);
4174             }
4175         }
4176     },
4177
4178     /**
4179      * Returns true if the passed node is selected
4180      * @param {HTMLElement/Number} node The node or node index
4181      * @return {Boolean}
4182      */
4183     isSelected : function(node){
4184         var s = this.selections;
4185         if(s.length < 1){
4186             return false;
4187         }
4188         node = this.getNode(node);
4189         return s.indexOf(node) !== -1;
4190     },
4191
4192     /**
4193      * Selects nodes.
4194      * @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
4195      * @param {Boolean} keepExisting (optional) true to keep existing selections
4196      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4197      */
4198     select : function(nodeInfo, keepExisting, suppressEvent){
4199         if(nodeInfo instanceof Array){
4200             if(!keepExisting){
4201                 this.clearSelections(true);
4202             }
4203             for(var i = 0, len = nodeInfo.length; i < len; i++){
4204                 this.select(nodeInfo[i], true, true);
4205             }
4206             return;
4207         } 
4208         var node = this.getNode(nodeInfo);
4209         if(!node || this.isSelected(node)){
4210             return; // already selected.
4211         }
4212         if(!keepExisting){
4213             this.clearSelections(true);
4214         }
4215         
4216         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4217             Roo.fly(node).addClass(this.selectedClass);
4218             this.selections.push(node);
4219             if(!suppressEvent){
4220                 this.fireEvent("selectionchange", this, this.selections);
4221             }
4222         }
4223         
4224         
4225     },
4226       /**
4227      * Unselects nodes.
4228      * @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
4229      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4230      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4231      */
4232     unselect : function(nodeInfo, keepExisting, suppressEvent)
4233     {
4234         if(nodeInfo instanceof Array){
4235             Roo.each(this.selections, function(s) {
4236                 this.unselect(s, nodeInfo);
4237             }, this);
4238             return;
4239         }
4240         var node = this.getNode(nodeInfo);
4241         if(!node || !this.isSelected(node)){
4242             //Roo.log("not selected");
4243             return; // not selected.
4244         }
4245         // fireevent???
4246         var ns = [];
4247         Roo.each(this.selections, function(s) {
4248             if (s == node ) {
4249                 Roo.fly(node).removeClass(this.selectedClass);
4250
4251                 return;
4252             }
4253             ns.push(s);
4254         },this);
4255         
4256         this.selections= ns;
4257         this.fireEvent("selectionchange", this, this.selections);
4258     },
4259
4260     /**
4261      * Gets a template node.
4262      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4263      * @return {HTMLElement} The node or null if it wasn't found
4264      */
4265     getNode : function(nodeInfo){
4266         if(typeof nodeInfo == "string"){
4267             return document.getElementById(nodeInfo);
4268         }else if(typeof nodeInfo == "number"){
4269             return this.nodes[nodeInfo];
4270         }
4271         return nodeInfo;
4272     },
4273
4274     /**
4275      * Gets a range template nodes.
4276      * @param {Number} startIndex
4277      * @param {Number} endIndex
4278      * @return {Array} An array of nodes
4279      */
4280     getNodes : function(start, end){
4281         var ns = this.nodes;
4282         start = start || 0;
4283         end = typeof end == "undefined" ? ns.length - 1 : end;
4284         var nodes = [];
4285         if(start <= end){
4286             for(var i = start; i <= end; i++){
4287                 nodes.push(ns[i]);
4288             }
4289         } else{
4290             for(var i = start; i >= end; i--){
4291                 nodes.push(ns[i]);
4292             }
4293         }
4294         return nodes;
4295     },
4296
4297     /**
4298      * Finds the index of the passed node
4299      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4300      * @return {Number} The index of the node or -1
4301      */
4302     indexOf : function(node){
4303         node = this.getNode(node);
4304         if(typeof node.nodeIndex == "number"){
4305             return node.nodeIndex;
4306         }
4307         var ns = this.nodes;
4308         for(var i = 0, len = ns.length; i < len; i++){
4309             if(ns[i] == node){
4310                 return i;
4311             }
4312         }
4313         return -1;
4314     }
4315 });
4316 /*
4317  * Based on:
4318  * Ext JS Library 1.1.1
4319  * Copyright(c) 2006-2007, Ext JS, LLC.
4320  *
4321  * Originally Released Under LGPL - original licence link has changed is not relivant.
4322  *
4323  * Fork - LGPL
4324  * <script type="text/javascript">
4325  */
4326
4327 /**
4328  * @class Roo.JsonView
4329  * @extends Roo.View
4330  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4331 <pre><code>
4332 var view = new Roo.JsonView({
4333     container: "my-element",
4334     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4335     multiSelect: true, 
4336     jsonRoot: "data" 
4337 });
4338
4339 // listen for node click?
4340 view.on("click", function(vw, index, node, e){
4341     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4342 });
4343
4344 // direct load of JSON data
4345 view.load("foobar.php");
4346
4347 // Example from my blog list
4348 var tpl = new Roo.Template(
4349     '&lt;div class="entry"&gt;' +
4350     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4351     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4352     "&lt;/div&gt;&lt;hr /&gt;"
4353 );
4354
4355 var moreView = new Roo.JsonView({
4356     container :  "entry-list", 
4357     template : tpl,
4358     jsonRoot: "posts"
4359 });
4360 moreView.on("beforerender", this.sortEntries, this);
4361 moreView.load({
4362     url: "/blog/get-posts.php",
4363     params: "allposts=true",
4364     text: "Loading Blog Entries..."
4365 });
4366 </code></pre>
4367
4368 * Note: old code is supported with arguments : (container, template, config)
4369
4370
4371  * @constructor
4372  * Create a new JsonView
4373  * 
4374  * @param {Object} config The config object
4375  * 
4376  */
4377 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4378     
4379     
4380     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4381
4382     var um = this.el.getUpdateManager();
4383     um.setRenderer(this);
4384     um.on("update", this.onLoad, this);
4385     um.on("failure", this.onLoadException, this);
4386
4387     /**
4388      * @event beforerender
4389      * Fires before rendering of the downloaded JSON data.
4390      * @param {Roo.JsonView} this
4391      * @param {Object} data The JSON data loaded
4392      */
4393     /**
4394      * @event load
4395      * Fires when data is loaded.
4396      * @param {Roo.JsonView} this
4397      * @param {Object} data The JSON data loaded
4398      * @param {Object} response The raw Connect response object
4399      */
4400     /**
4401      * @event loadexception
4402      * Fires when loading fails.
4403      * @param {Roo.JsonView} this
4404      * @param {Object} response The raw Connect response object
4405      */
4406     this.addEvents({
4407         'beforerender' : true,
4408         'load' : true,
4409         'loadexception' : true
4410     });
4411 };
4412 Roo.extend(Roo.JsonView, Roo.View, {
4413     /**
4414      * @type {String} The root property in the loaded JSON object that contains the data
4415      */
4416     jsonRoot : "",
4417
4418     /**
4419      * Refreshes the view.
4420      */
4421     refresh : function(){
4422         this.clearSelections();
4423         this.el.update("");
4424         var html = [];
4425         var o = this.jsonData;
4426         if(o && o.length > 0){
4427             for(var i = 0, len = o.length; i < len; i++){
4428                 var data = this.prepareData(o[i], i, o);
4429                 html[html.length] = this.tpl.apply(data);
4430             }
4431         }else{
4432             html.push(this.emptyText);
4433         }
4434         this.el.update(html.join(""));
4435         this.nodes = this.el.dom.childNodes;
4436         this.updateIndexes(0);
4437     },
4438
4439     /**
4440      * 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.
4441      * @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:
4442      <pre><code>
4443      view.load({
4444          url: "your-url.php",
4445          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4446          callback: yourFunction,
4447          scope: yourObject, //(optional scope)
4448          discardUrl: false,
4449          nocache: false,
4450          text: "Loading...",
4451          timeout: 30,
4452          scripts: false
4453      });
4454      </code></pre>
4455      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4456      * 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.
4457      * @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}
4458      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4459      * @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.
4460      */
4461     load : function(){
4462         var um = this.el.getUpdateManager();
4463         um.update.apply(um, arguments);
4464     },
4465
4466     // note - render is a standard framework call...
4467     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4468     render : function(el, response){
4469         
4470         this.clearSelections();
4471         this.el.update("");
4472         var o;
4473         try{
4474             if (response != '') {
4475                 o = Roo.util.JSON.decode(response.responseText);
4476                 if(this.jsonRoot){
4477                     
4478                     o = o[this.jsonRoot];
4479                 }
4480             }
4481         } catch(e){
4482         }
4483         /**
4484          * The current JSON data or null
4485          */
4486         this.jsonData = o;
4487         this.beforeRender();
4488         this.refresh();
4489     },
4490
4491 /**
4492  * Get the number of records in the current JSON dataset
4493  * @return {Number}
4494  */
4495     getCount : function(){
4496         return this.jsonData ? this.jsonData.length : 0;
4497     },
4498
4499 /**
4500  * Returns the JSON object for the specified node(s)
4501  * @param {HTMLElement/Array} node The node or an array of nodes
4502  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4503  * you get the JSON object for the node
4504  */
4505     getNodeData : function(node){
4506         if(node instanceof Array){
4507             var data = [];
4508             for(var i = 0, len = node.length; i < len; i++){
4509                 data.push(this.getNodeData(node[i]));
4510             }
4511             return data;
4512         }
4513         return this.jsonData[this.indexOf(node)] || null;
4514     },
4515
4516     beforeRender : function(){
4517         this.snapshot = this.jsonData;
4518         if(this.sortInfo){
4519             this.sort.apply(this, this.sortInfo);
4520         }
4521         this.fireEvent("beforerender", this, this.jsonData);
4522     },
4523
4524     onLoad : function(el, o){
4525         this.fireEvent("load", this, this.jsonData, o);
4526     },
4527
4528     onLoadException : function(el, o){
4529         this.fireEvent("loadexception", this, o);
4530     },
4531
4532 /**
4533  * Filter the data by a specific property.
4534  * @param {String} property A property on your JSON objects
4535  * @param {String/RegExp} value Either string that the property values
4536  * should start with, or a RegExp to test against the property
4537  */
4538     filter : function(property, value){
4539         if(this.jsonData){
4540             var data = [];
4541             var ss = this.snapshot;
4542             if(typeof value == "string"){
4543                 var vlen = value.length;
4544                 if(vlen == 0){
4545                     this.clearFilter();
4546                     return;
4547                 }
4548                 value = value.toLowerCase();
4549                 for(var i = 0, len = ss.length; i < len; i++){
4550                     var o = ss[i];
4551                     if(o[property].substr(0, vlen).toLowerCase() == value){
4552                         data.push(o);
4553                     }
4554                 }
4555             } else if(value.exec){ // regex?
4556                 for(var i = 0, len = ss.length; i < len; i++){
4557                     var o = ss[i];
4558                     if(value.test(o[property])){
4559                         data.push(o);
4560                     }
4561                 }
4562             } else{
4563                 return;
4564             }
4565             this.jsonData = data;
4566             this.refresh();
4567         }
4568     },
4569
4570 /**
4571  * Filter by a function. The passed function will be called with each
4572  * object in the current dataset. If the function returns true the value is kept,
4573  * otherwise it is filtered.
4574  * @param {Function} fn
4575  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4576  */
4577     filterBy : function(fn, scope){
4578         if(this.jsonData){
4579             var data = [];
4580             var ss = this.snapshot;
4581             for(var i = 0, len = ss.length; i < len; i++){
4582                 var o = ss[i];
4583                 if(fn.call(scope || this, o)){
4584                     data.push(o);
4585                 }
4586             }
4587             this.jsonData = data;
4588             this.refresh();
4589         }
4590     },
4591
4592 /**
4593  * Clears the current filter.
4594  */
4595     clearFilter : function(){
4596         if(this.snapshot && this.jsonData != this.snapshot){
4597             this.jsonData = this.snapshot;
4598             this.refresh();
4599         }
4600     },
4601
4602
4603 /**
4604  * Sorts the data for this view and refreshes it.
4605  * @param {String} property A property on your JSON objects to sort on
4606  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4607  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4608  */
4609     sort : function(property, dir, sortType){
4610         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4611         if(this.jsonData){
4612             var p = property;
4613             var dsc = dir && dir.toLowerCase() == "desc";
4614             var f = function(o1, o2){
4615                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4616                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4617                 ;
4618                 if(v1 < v2){
4619                     return dsc ? +1 : -1;
4620                 } else if(v1 > v2){
4621                     return dsc ? -1 : +1;
4622                 } else{
4623                     return 0;
4624                 }
4625             };
4626             this.jsonData.sort(f);
4627             this.refresh();
4628             if(this.jsonData != this.snapshot){
4629                 this.snapshot.sort(f);
4630             }
4631         }
4632     }
4633 });/*
4634  * Based on:
4635  * Ext JS Library 1.1.1
4636  * Copyright(c) 2006-2007, Ext JS, LLC.
4637  *
4638  * Originally Released Under LGPL - original licence link has changed is not relivant.
4639  *
4640  * Fork - LGPL
4641  * <script type="text/javascript">
4642  */
4643  
4644
4645 /**
4646  * @class Roo.ColorPalette
4647  * @extends Roo.Component
4648  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4649  * Here's an example of typical usage:
4650  * <pre><code>
4651 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4652 cp.render('my-div');
4653
4654 cp.on('select', function(palette, selColor){
4655     // do something with selColor
4656 });
4657 </code></pre>
4658  * @constructor
4659  * Create a new ColorPalette
4660  * @param {Object} config The config object
4661  */
4662 Roo.ColorPalette = function(config){
4663     Roo.ColorPalette.superclass.constructor.call(this, config);
4664     this.addEvents({
4665         /**
4666              * @event select
4667              * Fires when a color is selected
4668              * @param {ColorPalette} this
4669              * @param {String} color The 6-digit color hex code (without the # symbol)
4670              */
4671         select: true
4672     });
4673
4674     if(this.handler){
4675         this.on("select", this.handler, this.scope, true);
4676     }
4677 };
4678 Roo.extend(Roo.ColorPalette, Roo.Component, {
4679     /**
4680      * @cfg {String} itemCls
4681      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4682      */
4683     itemCls : "x-color-palette",
4684     /**
4685      * @cfg {String} value
4686      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4687      * the hex codes are case-sensitive.
4688      */
4689     value : null,
4690     clickEvent:'click',
4691     // private
4692     ctype: "Roo.ColorPalette",
4693
4694     /**
4695      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4696      */
4697     allowReselect : false,
4698
4699     /**
4700      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4701      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4702      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4703      * of colors with the width setting until the box is symmetrical.</p>
4704      * <p>You can override individual colors if needed:</p>
4705      * <pre><code>
4706 var cp = new Roo.ColorPalette();
4707 cp.colors[0] = "FF0000";  // change the first box to red
4708 </code></pre>
4709
4710 Or you can provide a custom array of your own for complete control:
4711 <pre><code>
4712 var cp = new Roo.ColorPalette();
4713 cp.colors = ["000000", "993300", "333300"];
4714 </code></pre>
4715      * @type Array
4716      */
4717     colors : [
4718         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4719         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4720         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4721         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4722         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4723     ],
4724
4725     // private
4726     onRender : function(container, position){
4727         var t = new Roo.MasterTemplate(
4728             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4729         );
4730         var c = this.colors;
4731         for(var i = 0, len = c.length; i < len; i++){
4732             t.add([c[i]]);
4733         }
4734         var el = document.createElement("div");
4735         el.className = this.itemCls;
4736         t.overwrite(el);
4737         container.dom.insertBefore(el, position);
4738         this.el = Roo.get(el);
4739         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4740         if(this.clickEvent != 'click'){
4741             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4742         }
4743     },
4744
4745     // private
4746     afterRender : function(){
4747         Roo.ColorPalette.superclass.afterRender.call(this);
4748         if(this.value){
4749             var s = this.value;
4750             this.value = null;
4751             this.select(s);
4752         }
4753     },
4754
4755     // private
4756     handleClick : function(e, t){
4757         e.preventDefault();
4758         if(!this.disabled){
4759             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4760             this.select(c.toUpperCase());
4761         }
4762     },
4763
4764     /**
4765      * Selects the specified color in the palette (fires the select event)
4766      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4767      */
4768     select : function(color){
4769         color = color.replace("#", "");
4770         if(color != this.value || this.allowReselect){
4771             var el = this.el;
4772             if(this.value){
4773                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4774             }
4775             el.child("a.color-"+color).addClass("x-color-palette-sel");
4776             this.value = color;
4777             this.fireEvent("select", this, color);
4778         }
4779     }
4780 });/*
4781  * Based on:
4782  * Ext JS Library 1.1.1
4783  * Copyright(c) 2006-2007, Ext JS, LLC.
4784  *
4785  * Originally Released Under LGPL - original licence link has changed is not relivant.
4786  *
4787  * Fork - LGPL
4788  * <script type="text/javascript">
4789  */
4790  
4791 /**
4792  * @class Roo.DatePicker
4793  * @extends Roo.Component
4794  * Simple date picker class.
4795  * @constructor
4796  * Create a new DatePicker
4797  * @param {Object} config The config object
4798  */
4799 Roo.DatePicker = function(config){
4800     Roo.DatePicker.superclass.constructor.call(this, config);
4801
4802     this.value = config && config.value ?
4803                  config.value.clearTime() : new Date().clearTime();
4804
4805     this.addEvents({
4806         /**
4807              * @event select
4808              * Fires when a date is selected
4809              * @param {DatePicker} this
4810              * @param {Date} date The selected date
4811              */
4812         'select': true,
4813         /**
4814              * @event monthchange
4815              * Fires when the displayed month changes 
4816              * @param {DatePicker} this
4817              * @param {Date} date The selected month
4818              */
4819         'monthchange': true
4820     });
4821
4822     if(this.handler){
4823         this.on("select", this.handler,  this.scope || this);
4824     }
4825     // build the disabledDatesRE
4826     if(!this.disabledDatesRE && this.disabledDates){
4827         var dd = this.disabledDates;
4828         var re = "(?:";
4829         for(var i = 0; i < dd.length; i++){
4830             re += dd[i];
4831             if(i != dd.length-1) {
4832                 re += "|";
4833             }
4834         }
4835         this.disabledDatesRE = new RegExp(re + ")");
4836     }
4837 };
4838
4839 Roo.extend(Roo.DatePicker, Roo.Component, {
4840     /**
4841      * @cfg {String} todayText
4842      * The text to display on the button that selects the current date (defaults to "Today")
4843      */
4844     todayText : "Today",
4845     /**
4846      * @cfg {String} okText
4847      * The text to display on the ok button
4848      */
4849     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4850     /**
4851      * @cfg {String} cancelText
4852      * The text to display on the cancel button
4853      */
4854     cancelText : "Cancel",
4855     /**
4856      * @cfg {String} todayTip
4857      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4858      */
4859     todayTip : "{0} (Spacebar)",
4860     /**
4861      * @cfg {Date} minDate
4862      * Minimum allowable date (JavaScript date object, defaults to null)
4863      */
4864     minDate : null,
4865     /**
4866      * @cfg {Date} maxDate
4867      * Maximum allowable date (JavaScript date object, defaults to null)
4868      */
4869     maxDate : null,
4870     /**
4871      * @cfg {String} minText
4872      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4873      */
4874     minText : "This date is before the minimum date",
4875     /**
4876      * @cfg {String} maxText
4877      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4878      */
4879     maxText : "This date is after the maximum date",
4880     /**
4881      * @cfg {String} format
4882      * The default date format string which can be overriden for localization support.  The format must be
4883      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4884      */
4885     format : "m/d/y",
4886     /**
4887      * @cfg {Array} disabledDays
4888      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4889      */
4890     disabledDays : null,
4891     /**
4892      * @cfg {String} disabledDaysText
4893      * The tooltip to display when the date falls on a disabled day (defaults to "")
4894      */
4895     disabledDaysText : "",
4896     /**
4897      * @cfg {RegExp} disabledDatesRE
4898      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4899      */
4900     disabledDatesRE : null,
4901     /**
4902      * @cfg {String} disabledDatesText
4903      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4904      */
4905     disabledDatesText : "",
4906     /**
4907      * @cfg {Boolean} constrainToViewport
4908      * True to constrain the date picker to the viewport (defaults to true)
4909      */
4910     constrainToViewport : true,
4911     /**
4912      * @cfg {Array} monthNames
4913      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4914      */
4915     monthNames : Date.monthNames,
4916     /**
4917      * @cfg {Array} dayNames
4918      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4919      */
4920     dayNames : Date.dayNames,
4921     /**
4922      * @cfg {String} nextText
4923      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4924      */
4925     nextText: 'Next Month (Control+Right)',
4926     /**
4927      * @cfg {String} prevText
4928      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4929      */
4930     prevText: 'Previous Month (Control+Left)',
4931     /**
4932      * @cfg {String} monthYearText
4933      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4934      */
4935     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4936     /**
4937      * @cfg {Number} startDay
4938      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4939      */
4940     startDay : 0,
4941     /**
4942      * @cfg {Bool} showClear
4943      * Show a clear button (usefull for date form elements that can be blank.)
4944      */
4945     
4946     showClear: false,
4947     
4948     /**
4949      * Sets the value of the date field
4950      * @param {Date} value The date to set
4951      */
4952     setValue : function(value){
4953         var old = this.value;
4954         
4955         if (typeof(value) == 'string') {
4956          
4957             value = Date.parseDate(value, this.format);
4958         }
4959         if (!value) {
4960             value = new Date();
4961         }
4962         
4963         this.value = value.clearTime(true);
4964         if(this.el){
4965             this.update(this.value);
4966         }
4967     },
4968
4969     /**
4970      * Gets the current selected value of the date field
4971      * @return {Date} The selected date
4972      */
4973     getValue : function(){
4974         return this.value;
4975     },
4976
4977     // private
4978     focus : function(){
4979         if(this.el){
4980             this.update(this.activeDate);
4981         }
4982     },
4983
4984     // privateval
4985     onRender : function(container, position){
4986         
4987         var m = [
4988              '<table cellspacing="0">',
4989                 '<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>',
4990                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4991         var dn = this.dayNames;
4992         for(var i = 0; i < 7; i++){
4993             var d = this.startDay+i;
4994             if(d > 6){
4995                 d = d-7;
4996             }
4997             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4998         }
4999         m[m.length] = "</tr></thead><tbody><tr>";
5000         for(var i = 0; i < 42; i++) {
5001             if(i % 7 == 0 && i != 0){
5002                 m[m.length] = "</tr><tr>";
5003             }
5004             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5005         }
5006         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5007             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5008
5009         var el = document.createElement("div");
5010         el.className = "x-date-picker";
5011         el.innerHTML = m.join("");
5012
5013         container.dom.insertBefore(el, position);
5014
5015         this.el = Roo.get(el);
5016         this.eventEl = Roo.get(el.firstChild);
5017
5018         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5019             handler: this.showPrevMonth,
5020             scope: this,
5021             preventDefault:true,
5022             stopDefault:true
5023         });
5024
5025         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5026             handler: this.showNextMonth,
5027             scope: this,
5028             preventDefault:true,
5029             stopDefault:true
5030         });
5031
5032         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5033
5034         this.monthPicker = this.el.down('div.x-date-mp');
5035         this.monthPicker.enableDisplayMode('block');
5036         
5037         var kn = new Roo.KeyNav(this.eventEl, {
5038             "left" : function(e){
5039                 e.ctrlKey ?
5040                     this.showPrevMonth() :
5041                     this.update(this.activeDate.add("d", -1));
5042             },
5043
5044             "right" : function(e){
5045                 e.ctrlKey ?
5046                     this.showNextMonth() :
5047                     this.update(this.activeDate.add("d", 1));
5048             },
5049
5050             "up" : function(e){
5051                 e.ctrlKey ?
5052                     this.showNextYear() :
5053                     this.update(this.activeDate.add("d", -7));
5054             },
5055
5056             "down" : function(e){
5057                 e.ctrlKey ?
5058                     this.showPrevYear() :
5059                     this.update(this.activeDate.add("d", 7));
5060             },
5061
5062             "pageUp" : function(e){
5063                 this.showNextMonth();
5064             },
5065
5066             "pageDown" : function(e){
5067                 this.showPrevMonth();
5068             },
5069
5070             "enter" : function(e){
5071                 e.stopPropagation();
5072                 return true;
5073             },
5074
5075             scope : this
5076         });
5077
5078         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5079
5080         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5081
5082         this.el.unselectable();
5083         
5084         this.cells = this.el.select("table.x-date-inner tbody td");
5085         this.textNodes = this.el.query("table.x-date-inner tbody span");
5086
5087         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5088             text: "&#160;",
5089             tooltip: this.monthYearText
5090         });
5091
5092         this.mbtn.on('click', this.showMonthPicker, this);
5093         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5094
5095
5096         var today = (new Date()).dateFormat(this.format);
5097         
5098         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5099         if (this.showClear) {
5100             baseTb.add( new Roo.Toolbar.Fill());
5101         }
5102         baseTb.add({
5103             text: String.format(this.todayText, today),
5104             tooltip: String.format(this.todayTip, today),
5105             handler: this.selectToday,
5106             scope: this
5107         });
5108         
5109         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5110             
5111         //});
5112         if (this.showClear) {
5113             
5114             baseTb.add( new Roo.Toolbar.Fill());
5115             baseTb.add({
5116                 text: '&#160;',
5117                 cls: 'x-btn-icon x-btn-clear',
5118                 handler: function() {
5119                     //this.value = '';
5120                     this.fireEvent("select", this, '');
5121                 },
5122                 scope: this
5123             });
5124         }
5125         
5126         
5127         if(Roo.isIE){
5128             this.el.repaint();
5129         }
5130         this.update(this.value);
5131     },
5132
5133     createMonthPicker : function(){
5134         if(!this.monthPicker.dom.firstChild){
5135             var buf = ['<table border="0" cellspacing="0">'];
5136             for(var i = 0; i < 6; i++){
5137                 buf.push(
5138                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5139                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5140                     i == 0 ?
5141                     '<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>' :
5142                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5143                 );
5144             }
5145             buf.push(
5146                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5147                     this.okText,
5148                     '</button><button type="button" class="x-date-mp-cancel">',
5149                     this.cancelText,
5150                     '</button></td></tr>',
5151                 '</table>'
5152             );
5153             this.monthPicker.update(buf.join(''));
5154             this.monthPicker.on('click', this.onMonthClick, this);
5155             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5156
5157             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5158             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5159
5160             this.mpMonths.each(function(m, a, i){
5161                 i += 1;
5162                 if((i%2) == 0){
5163                     m.dom.xmonth = 5 + Math.round(i * .5);
5164                 }else{
5165                     m.dom.xmonth = Math.round((i-1) * .5);
5166                 }
5167             });
5168         }
5169     },
5170
5171     showMonthPicker : function(){
5172         this.createMonthPicker();
5173         var size = this.el.getSize();
5174         this.monthPicker.setSize(size);
5175         this.monthPicker.child('table').setSize(size);
5176
5177         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5178         this.updateMPMonth(this.mpSelMonth);
5179         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5180         this.updateMPYear(this.mpSelYear);
5181
5182         this.monthPicker.slideIn('t', {duration:.2});
5183     },
5184
5185     updateMPYear : function(y){
5186         this.mpyear = y;
5187         var ys = this.mpYears.elements;
5188         for(var i = 1; i <= 10; i++){
5189             var td = ys[i-1], y2;
5190             if((i%2) == 0){
5191                 y2 = y + Math.round(i * .5);
5192                 td.firstChild.innerHTML = y2;
5193                 td.xyear = y2;
5194             }else{
5195                 y2 = y - (5-Math.round(i * .5));
5196                 td.firstChild.innerHTML = y2;
5197                 td.xyear = y2;
5198             }
5199             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5200         }
5201     },
5202
5203     updateMPMonth : function(sm){
5204         this.mpMonths.each(function(m, a, i){
5205             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5206         });
5207     },
5208
5209     selectMPMonth: function(m){
5210         
5211     },
5212
5213     onMonthClick : function(e, t){
5214         e.stopEvent();
5215         var el = new Roo.Element(t), pn;
5216         if(el.is('button.x-date-mp-cancel')){
5217             this.hideMonthPicker();
5218         }
5219         else if(el.is('button.x-date-mp-ok')){
5220             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5221             this.hideMonthPicker();
5222         }
5223         else if(pn = el.up('td.x-date-mp-month', 2)){
5224             this.mpMonths.removeClass('x-date-mp-sel');
5225             pn.addClass('x-date-mp-sel');
5226             this.mpSelMonth = pn.dom.xmonth;
5227         }
5228         else if(pn = el.up('td.x-date-mp-year', 2)){
5229             this.mpYears.removeClass('x-date-mp-sel');
5230             pn.addClass('x-date-mp-sel');
5231             this.mpSelYear = pn.dom.xyear;
5232         }
5233         else if(el.is('a.x-date-mp-prev')){
5234             this.updateMPYear(this.mpyear-10);
5235         }
5236         else if(el.is('a.x-date-mp-next')){
5237             this.updateMPYear(this.mpyear+10);
5238         }
5239     },
5240
5241     onMonthDblClick : function(e, t){
5242         e.stopEvent();
5243         var el = new Roo.Element(t), pn;
5244         if(pn = el.up('td.x-date-mp-month', 2)){
5245             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5246             this.hideMonthPicker();
5247         }
5248         else if(pn = el.up('td.x-date-mp-year', 2)){
5249             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5250             this.hideMonthPicker();
5251         }
5252     },
5253
5254     hideMonthPicker : function(disableAnim){
5255         if(this.monthPicker){
5256             if(disableAnim === true){
5257                 this.monthPicker.hide();
5258             }else{
5259                 this.monthPicker.slideOut('t', {duration:.2});
5260             }
5261         }
5262     },
5263
5264     // private
5265     showPrevMonth : function(e){
5266         this.update(this.activeDate.add("mo", -1));
5267     },
5268
5269     // private
5270     showNextMonth : function(e){
5271         this.update(this.activeDate.add("mo", 1));
5272     },
5273
5274     // private
5275     showPrevYear : function(){
5276         this.update(this.activeDate.add("y", -1));
5277     },
5278
5279     // private
5280     showNextYear : function(){
5281         this.update(this.activeDate.add("y", 1));
5282     },
5283
5284     // private
5285     handleMouseWheel : function(e){
5286         var delta = e.getWheelDelta();
5287         if(delta > 0){
5288             this.showPrevMonth();
5289             e.stopEvent();
5290         } else if(delta < 0){
5291             this.showNextMonth();
5292             e.stopEvent();
5293         }
5294     },
5295
5296     // private
5297     handleDateClick : function(e, t){
5298         e.stopEvent();
5299         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5300             this.setValue(new Date(t.dateValue));
5301             this.fireEvent("select", this, this.value);
5302         }
5303     },
5304
5305     // private
5306     selectToday : function(){
5307         this.setValue(new Date().clearTime());
5308         this.fireEvent("select", this, this.value);
5309     },
5310
5311     // private
5312     update : function(date)
5313     {
5314         var vd = this.activeDate;
5315         this.activeDate = date;
5316         if(vd && this.el){
5317             var t = date.getTime();
5318             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5319                 this.cells.removeClass("x-date-selected");
5320                 this.cells.each(function(c){
5321                    if(c.dom.firstChild.dateValue == t){
5322                        c.addClass("x-date-selected");
5323                        setTimeout(function(){
5324                             try{c.dom.firstChild.focus();}catch(e){}
5325                        }, 50);
5326                        return false;
5327                    }
5328                 });
5329                 return;
5330             }
5331         }
5332         
5333         var days = date.getDaysInMonth();
5334         var firstOfMonth = date.getFirstDateOfMonth();
5335         var startingPos = firstOfMonth.getDay()-this.startDay;
5336
5337         if(startingPos <= this.startDay){
5338             startingPos += 7;
5339         }
5340
5341         var pm = date.add("mo", -1);
5342         var prevStart = pm.getDaysInMonth()-startingPos;
5343
5344         var cells = this.cells.elements;
5345         var textEls = this.textNodes;
5346         days += startingPos;
5347
5348         // convert everything to numbers so it's fast
5349         var day = 86400000;
5350         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5351         var today = new Date().clearTime().getTime();
5352         var sel = date.clearTime().getTime();
5353         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5354         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5355         var ddMatch = this.disabledDatesRE;
5356         var ddText = this.disabledDatesText;
5357         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5358         var ddaysText = this.disabledDaysText;
5359         var format = this.format;
5360
5361         var setCellClass = function(cal, cell){
5362             cell.title = "";
5363             var t = d.getTime();
5364             cell.firstChild.dateValue = t;
5365             if(t == today){
5366                 cell.className += " x-date-today";
5367                 cell.title = cal.todayText;
5368             }
5369             if(t == sel){
5370                 cell.className += " x-date-selected";
5371                 setTimeout(function(){
5372                     try{cell.firstChild.focus();}catch(e){}
5373                 }, 50);
5374             }
5375             // disabling
5376             if(t < min) {
5377                 cell.className = " x-date-disabled";
5378                 cell.title = cal.minText;
5379                 return;
5380             }
5381             if(t > max) {
5382                 cell.className = " x-date-disabled";
5383                 cell.title = cal.maxText;
5384                 return;
5385             }
5386             if(ddays){
5387                 if(ddays.indexOf(d.getDay()) != -1){
5388                     cell.title = ddaysText;
5389                     cell.className = " x-date-disabled";
5390                 }
5391             }
5392             if(ddMatch && format){
5393                 var fvalue = d.dateFormat(format);
5394                 if(ddMatch.test(fvalue)){
5395                     cell.title = ddText.replace("%0", fvalue);
5396                     cell.className = " x-date-disabled";
5397                 }
5398             }
5399         };
5400
5401         var i = 0;
5402         for(; i < startingPos; i++) {
5403             textEls[i].innerHTML = (++prevStart);
5404             d.setDate(d.getDate()+1);
5405             cells[i].className = "x-date-prevday";
5406             setCellClass(this, cells[i]);
5407         }
5408         for(; i < days; i++){
5409             intDay = i - startingPos + 1;
5410             textEls[i].innerHTML = (intDay);
5411             d.setDate(d.getDate()+1);
5412             cells[i].className = "x-date-active";
5413             setCellClass(this, cells[i]);
5414         }
5415         var extraDays = 0;
5416         for(; i < 42; i++) {
5417              textEls[i].innerHTML = (++extraDays);
5418              d.setDate(d.getDate()+1);
5419              cells[i].className = "x-date-nextday";
5420              setCellClass(this, cells[i]);
5421         }
5422
5423         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5424         this.fireEvent('monthchange', this, date);
5425         
5426         if(!this.internalRender){
5427             var main = this.el.dom.firstChild;
5428             var w = main.offsetWidth;
5429             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5430             Roo.fly(main).setWidth(w);
5431             this.internalRender = true;
5432             // opera does not respect the auto grow header center column
5433             // then, after it gets a width opera refuses to recalculate
5434             // without a second pass
5435             if(Roo.isOpera && !this.secondPass){
5436                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5437                 this.secondPass = true;
5438                 this.update.defer(10, this, [date]);
5439             }
5440         }
5441         
5442         
5443     }
5444 });        /*
5445  * Based on:
5446  * Ext JS Library 1.1.1
5447  * Copyright(c) 2006-2007, Ext JS, LLC.
5448  *
5449  * Originally Released Under LGPL - original licence link has changed is not relivant.
5450  *
5451  * Fork - LGPL
5452  * <script type="text/javascript">
5453  */
5454 /**
5455  * @class Roo.TabPanel
5456  * @extends Roo.util.Observable
5457  * A lightweight tab container.
5458  * <br><br>
5459  * Usage:
5460  * <pre><code>
5461 // basic tabs 1, built from existing content
5462 var tabs = new Roo.TabPanel("tabs1");
5463 tabs.addTab("script", "View Script");
5464 tabs.addTab("markup", "View Markup");
5465 tabs.activate("script");
5466
5467 // more advanced tabs, built from javascript
5468 var jtabs = new Roo.TabPanel("jtabs");
5469 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5470
5471 // set up the UpdateManager
5472 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5473 var updater = tab2.getUpdateManager();
5474 updater.setDefaultUrl("ajax1.htm");
5475 tab2.on('activate', updater.refresh, updater, true);
5476
5477 // Use setUrl for Ajax loading
5478 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5479 tab3.setUrl("ajax2.htm", null, true);
5480
5481 // Disabled tab
5482 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5483 tab4.disable();
5484
5485 jtabs.activate("jtabs-1");
5486  * </code></pre>
5487  * @constructor
5488  * Create a new TabPanel.
5489  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5490  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5491  */
5492 Roo.TabPanel = function(container, config){
5493     /**
5494     * The container element for this TabPanel.
5495     * @type Roo.Element
5496     */
5497     this.el = Roo.get(container, true);
5498     if(config){
5499         if(typeof config == "boolean"){
5500             this.tabPosition = config ? "bottom" : "top";
5501         }else{
5502             Roo.apply(this, config);
5503         }
5504     }
5505     if(this.tabPosition == "bottom"){
5506         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5507         this.el.addClass("x-tabs-bottom");
5508     }
5509     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5510     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5511     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5512     if(Roo.isIE){
5513         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5514     }
5515     if(this.tabPosition != "bottom"){
5516         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5517          * @type Roo.Element
5518          */
5519         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5520         this.el.addClass("x-tabs-top");
5521     }
5522     this.items = [];
5523
5524     this.bodyEl.setStyle("position", "relative");
5525
5526     this.active = null;
5527     this.activateDelegate = this.activate.createDelegate(this);
5528
5529     this.addEvents({
5530         /**
5531          * @event tabchange
5532          * Fires when the active tab changes
5533          * @param {Roo.TabPanel} this
5534          * @param {Roo.TabPanelItem} activePanel The new active tab
5535          */
5536         "tabchange": true,
5537         /**
5538          * @event beforetabchange
5539          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5540          * @param {Roo.TabPanel} this
5541          * @param {Object} e Set cancel to true on this object to cancel the tab change
5542          * @param {Roo.TabPanelItem} tab The tab being changed to
5543          */
5544         "beforetabchange" : true
5545     });
5546
5547     Roo.EventManager.onWindowResize(this.onResize, this);
5548     this.cpad = this.el.getPadding("lr");
5549     this.hiddenCount = 0;
5550
5551
5552     // toolbar on the tabbar support...
5553     if (this.toolbar) {
5554         var tcfg = this.toolbar;
5555         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5556         this.toolbar = new Roo.Toolbar(tcfg);
5557         if (Roo.isSafari) {
5558             var tbl = tcfg.container.child('table', true);
5559             tbl.setAttribute('width', '100%');
5560         }
5561         
5562     }
5563    
5564
5565
5566     Roo.TabPanel.superclass.constructor.call(this);
5567 };
5568
5569 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5570     /*
5571      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5572      */
5573     tabPosition : "top",
5574     /*
5575      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5576      */
5577     currentTabWidth : 0,
5578     /*
5579      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5580      */
5581     minTabWidth : 40,
5582     /*
5583      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5584      */
5585     maxTabWidth : 250,
5586     /*
5587      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5588      */
5589     preferredTabWidth : 175,
5590     /*
5591      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5592      */
5593     resizeTabs : false,
5594     /*
5595      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5596      */
5597     monitorResize : true,
5598     /*
5599      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5600      */
5601     toolbar : false,
5602
5603     /**
5604      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5605      * @param {String} id The id of the div to use <b>or create</b>
5606      * @param {String} text The text for the tab
5607      * @param {String} content (optional) Content to put in the TabPanelItem body
5608      * @param {Boolean} closable (optional) True to create a close icon on the tab
5609      * @return {Roo.TabPanelItem} The created TabPanelItem
5610      */
5611     addTab : function(id, text, content, closable){
5612         var item = new Roo.TabPanelItem(this, id, text, closable);
5613         this.addTabItem(item);
5614         if(content){
5615             item.setContent(content);
5616         }
5617         return item;
5618     },
5619
5620     /**
5621      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5622      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5623      * @return {Roo.TabPanelItem}
5624      */
5625     getTab : function(id){
5626         return this.items[id];
5627     },
5628
5629     /**
5630      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5631      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5632      */
5633     hideTab : function(id){
5634         var t = this.items[id];
5635         if(!t.isHidden()){
5636            t.setHidden(true);
5637            this.hiddenCount++;
5638            this.autoSizeTabs();
5639         }
5640     },
5641
5642     /**
5643      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5644      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5645      */
5646     unhideTab : function(id){
5647         var t = this.items[id];
5648         if(t.isHidden()){
5649            t.setHidden(false);
5650            this.hiddenCount--;
5651            this.autoSizeTabs();
5652         }
5653     },
5654
5655     /**
5656      * Adds an existing {@link Roo.TabPanelItem}.
5657      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5658      */
5659     addTabItem : function(item){
5660         this.items[item.id] = item;
5661         this.items.push(item);
5662         if(this.resizeTabs){
5663            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5664            this.autoSizeTabs();
5665         }else{
5666             item.autoSize();
5667         }
5668     },
5669
5670     /**
5671      * Removes a {@link Roo.TabPanelItem}.
5672      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5673      */
5674     removeTab : function(id){
5675         var items = this.items;
5676         var tab = items[id];
5677         if(!tab) { return; }
5678         var index = items.indexOf(tab);
5679         if(this.active == tab && items.length > 1){
5680             var newTab = this.getNextAvailable(index);
5681             if(newTab) {
5682                 newTab.activate();
5683             }
5684         }
5685         this.stripEl.dom.removeChild(tab.pnode.dom);
5686         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5687             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5688         }
5689         items.splice(index, 1);
5690         delete this.items[tab.id];
5691         tab.fireEvent("close", tab);
5692         tab.purgeListeners();
5693         this.autoSizeTabs();
5694     },
5695
5696     getNextAvailable : function(start){
5697         var items = this.items;
5698         var index = start;
5699         // look for a next tab that will slide over to
5700         // replace the one being removed
5701         while(index < items.length){
5702             var item = items[++index];
5703             if(item && !item.isHidden()){
5704                 return item;
5705             }
5706         }
5707         // if one isn't found select the previous tab (on the left)
5708         index = start;
5709         while(index >= 0){
5710             var item = items[--index];
5711             if(item && !item.isHidden()){
5712                 return item;
5713             }
5714         }
5715         return null;
5716     },
5717
5718     /**
5719      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5720      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5721      */
5722     disableTab : function(id){
5723         var tab = this.items[id];
5724         if(tab && this.active != tab){
5725             tab.disable();
5726         }
5727     },
5728
5729     /**
5730      * Enables a {@link Roo.TabPanelItem} that is disabled.
5731      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5732      */
5733     enableTab : function(id){
5734         var tab = this.items[id];
5735         tab.enable();
5736     },
5737
5738     /**
5739      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5740      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5741      * @return {Roo.TabPanelItem} The TabPanelItem.
5742      */
5743     activate : function(id){
5744         var tab = this.items[id];
5745         if(!tab){
5746             return null;
5747         }
5748         if(tab == this.active || tab.disabled){
5749             return tab;
5750         }
5751         var e = {};
5752         this.fireEvent("beforetabchange", this, e, tab);
5753         if(e.cancel !== true && !tab.disabled){
5754             if(this.active){
5755                 this.active.hide();
5756             }
5757             this.active = this.items[id];
5758             this.active.show();
5759             this.fireEvent("tabchange", this, this.active);
5760         }
5761         return tab;
5762     },
5763
5764     /**
5765      * Gets the active {@link Roo.TabPanelItem}.
5766      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5767      */
5768     getActiveTab : function(){
5769         return this.active;
5770     },
5771
5772     /**
5773      * Updates the tab body element to fit the height of the container element
5774      * for overflow scrolling
5775      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5776      */
5777     syncHeight : function(targetHeight){
5778         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5779         var bm = this.bodyEl.getMargins();
5780         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5781         this.bodyEl.setHeight(newHeight);
5782         return newHeight;
5783     },
5784
5785     onResize : function(){
5786         if(this.monitorResize){
5787             this.autoSizeTabs();
5788         }
5789     },
5790
5791     /**
5792      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5793      */
5794     beginUpdate : function(){
5795         this.updating = true;
5796     },
5797
5798     /**
5799      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5800      */
5801     endUpdate : function(){
5802         this.updating = false;
5803         this.autoSizeTabs();
5804     },
5805
5806     /**
5807      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5808      */
5809     autoSizeTabs : function(){
5810         var count = this.items.length;
5811         var vcount = count - this.hiddenCount;
5812         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5813             return;
5814         }
5815         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5816         var availWidth = Math.floor(w / vcount);
5817         var b = this.stripBody;
5818         if(b.getWidth() > w){
5819             var tabs = this.items;
5820             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5821             if(availWidth < this.minTabWidth){
5822                 /*if(!this.sleft){    // incomplete scrolling code
5823                     this.createScrollButtons();
5824                 }
5825                 this.showScroll();
5826                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5827             }
5828         }else{
5829             if(this.currentTabWidth < this.preferredTabWidth){
5830                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5831             }
5832         }
5833     },
5834
5835     /**
5836      * Returns the number of tabs in this TabPanel.
5837      * @return {Number}
5838      */
5839      getCount : function(){
5840          return this.items.length;
5841      },
5842
5843     /**
5844      * Resizes all the tabs to the passed width
5845      * @param {Number} The new width
5846      */
5847     setTabWidth : function(width){
5848         this.currentTabWidth = width;
5849         for(var i = 0, len = this.items.length; i < len; i++) {
5850                 if(!this.items[i].isHidden()) {
5851                 this.items[i].setWidth(width);
5852             }
5853         }
5854     },
5855
5856     /**
5857      * Destroys this TabPanel
5858      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5859      */
5860     destroy : function(removeEl){
5861         Roo.EventManager.removeResizeListener(this.onResize, this);
5862         for(var i = 0, len = this.items.length; i < len; i++){
5863             this.items[i].purgeListeners();
5864         }
5865         if(removeEl === true){
5866             this.el.update("");
5867             this.el.remove();
5868         }
5869     }
5870 });
5871
5872 /**
5873  * @class Roo.TabPanelItem
5874  * @extends Roo.util.Observable
5875  * Represents an individual item (tab plus body) in a TabPanel.
5876  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5877  * @param {String} id The id of this TabPanelItem
5878  * @param {String} text The text for the tab of this TabPanelItem
5879  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5880  */
5881 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5882     /**
5883      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5884      * @type Roo.TabPanel
5885      */
5886     this.tabPanel = tabPanel;
5887     /**
5888      * The id for this TabPanelItem
5889      * @type String
5890      */
5891     this.id = id;
5892     /** @private */
5893     this.disabled = false;
5894     /** @private */
5895     this.text = text;
5896     /** @private */
5897     this.loaded = false;
5898     this.closable = closable;
5899
5900     /**
5901      * The body element for this TabPanelItem.
5902      * @type Roo.Element
5903      */
5904     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5905     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5906     this.bodyEl.setStyle("display", "block");
5907     this.bodyEl.setStyle("zoom", "1");
5908     this.hideAction();
5909
5910     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5911     /** @private */
5912     this.el = Roo.get(els.el, true);
5913     this.inner = Roo.get(els.inner, true);
5914     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5915     this.pnode = Roo.get(els.el.parentNode, true);
5916     this.el.on("mousedown", this.onTabMouseDown, this);
5917     this.el.on("click", this.onTabClick, this);
5918     /** @private */
5919     if(closable){
5920         var c = Roo.get(els.close, true);
5921         c.dom.title = this.closeText;
5922         c.addClassOnOver("close-over");
5923         c.on("click", this.closeClick, this);
5924      }
5925
5926     this.addEvents({
5927          /**
5928          * @event activate
5929          * Fires when this tab becomes the active tab.
5930          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5931          * @param {Roo.TabPanelItem} this
5932          */
5933         "activate": true,
5934         /**
5935          * @event beforeclose
5936          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5937          * @param {Roo.TabPanelItem} this
5938          * @param {Object} e Set cancel to true on this object to cancel the close.
5939          */
5940         "beforeclose": true,
5941         /**
5942          * @event close
5943          * Fires when this tab is closed.
5944          * @param {Roo.TabPanelItem} this
5945          */
5946          "close": true,
5947         /**
5948          * @event deactivate
5949          * Fires when this tab is no longer the active tab.
5950          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5951          * @param {Roo.TabPanelItem} this
5952          */
5953          "deactivate" : true
5954     });
5955     this.hidden = false;
5956
5957     Roo.TabPanelItem.superclass.constructor.call(this);
5958 };
5959
5960 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5961     purgeListeners : function(){
5962        Roo.util.Observable.prototype.purgeListeners.call(this);
5963        this.el.removeAllListeners();
5964     },
5965     /**
5966      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5967      */
5968     show : function(){
5969         this.pnode.addClass("on");
5970         this.showAction();
5971         if(Roo.isOpera){
5972             this.tabPanel.stripWrap.repaint();
5973         }
5974         this.fireEvent("activate", this.tabPanel, this);
5975     },
5976
5977     /**
5978      * Returns true if this tab is the active tab.
5979      * @return {Boolean}
5980      */
5981     isActive : function(){
5982         return this.tabPanel.getActiveTab() == this;
5983     },
5984
5985     /**
5986      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5987      */
5988     hide : function(){
5989         this.pnode.removeClass("on");
5990         this.hideAction();
5991         this.fireEvent("deactivate", this.tabPanel, this);
5992     },
5993
5994     hideAction : function(){
5995         this.bodyEl.hide();
5996         this.bodyEl.setStyle("position", "absolute");
5997         this.bodyEl.setLeft("-20000px");
5998         this.bodyEl.setTop("-20000px");
5999     },
6000
6001     showAction : function(){
6002         this.bodyEl.setStyle("position", "relative");
6003         this.bodyEl.setTop("");
6004         this.bodyEl.setLeft("");
6005         this.bodyEl.show();
6006     },
6007
6008     /**
6009      * Set the tooltip for the tab.
6010      * @param {String} tooltip The tab's tooltip
6011      */
6012     setTooltip : function(text){
6013         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6014             this.textEl.dom.qtip = text;
6015             this.textEl.dom.removeAttribute('title');
6016         }else{
6017             this.textEl.dom.title = text;
6018         }
6019     },
6020
6021     onTabClick : function(e){
6022         e.preventDefault();
6023         this.tabPanel.activate(this.id);
6024     },
6025
6026     onTabMouseDown : function(e){
6027         e.preventDefault();
6028         this.tabPanel.activate(this.id);
6029     },
6030
6031     getWidth : function(){
6032         return this.inner.getWidth();
6033     },
6034
6035     setWidth : function(width){
6036         var iwidth = width - this.pnode.getPadding("lr");
6037         this.inner.setWidth(iwidth);
6038         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6039         this.pnode.setWidth(width);
6040     },
6041
6042     /**
6043      * Show or hide the tab
6044      * @param {Boolean} hidden True to hide or false to show.
6045      */
6046     setHidden : function(hidden){
6047         this.hidden = hidden;
6048         this.pnode.setStyle("display", hidden ? "none" : "");
6049     },
6050
6051     /**
6052      * Returns true if this tab is "hidden"
6053      * @return {Boolean}
6054      */
6055     isHidden : function(){
6056         return this.hidden;
6057     },
6058
6059     /**
6060      * Returns the text for this tab
6061      * @return {String}
6062      */
6063     getText : function(){
6064         return this.text;
6065     },
6066
6067     autoSize : function(){
6068         //this.el.beginMeasure();
6069         this.textEl.setWidth(1);
6070         /*
6071          *  #2804 [new] Tabs in Roojs
6072          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6073          */
6074         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6075         //this.el.endMeasure();
6076     },
6077
6078     /**
6079      * Sets the text for the tab (Note: this also sets the tooltip text)
6080      * @param {String} text The tab's text and tooltip
6081      */
6082     setText : function(text){
6083         this.text = text;
6084         this.textEl.update(text);
6085         this.setTooltip(text);
6086         if(!this.tabPanel.resizeTabs){
6087             this.autoSize();
6088         }
6089     },
6090     /**
6091      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6092      */
6093     activate : function(){
6094         this.tabPanel.activate(this.id);
6095     },
6096
6097     /**
6098      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6099      */
6100     disable : function(){
6101         if(this.tabPanel.active != this){
6102             this.disabled = true;
6103             this.pnode.addClass("disabled");
6104         }
6105     },
6106
6107     /**
6108      * Enables this TabPanelItem if it was previously disabled.
6109      */
6110     enable : function(){
6111         this.disabled = false;
6112         this.pnode.removeClass("disabled");
6113     },
6114
6115     /**
6116      * Sets the content for this TabPanelItem.
6117      * @param {String} content The content
6118      * @param {Boolean} loadScripts true to look for and load scripts
6119      */
6120     setContent : function(content, loadScripts){
6121         this.bodyEl.update(content, loadScripts);
6122     },
6123
6124     /**
6125      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6126      * @return {Roo.UpdateManager} The UpdateManager
6127      */
6128     getUpdateManager : function(){
6129         return this.bodyEl.getUpdateManager();
6130     },
6131
6132     /**
6133      * Set a URL to be used to load the content for this TabPanelItem.
6134      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6135      * @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)
6136      * @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)
6137      * @return {Roo.UpdateManager} The UpdateManager
6138      */
6139     setUrl : function(url, params, loadOnce){
6140         if(this.refreshDelegate){
6141             this.un('activate', this.refreshDelegate);
6142         }
6143         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6144         this.on("activate", this.refreshDelegate);
6145         return this.bodyEl.getUpdateManager();
6146     },
6147
6148     /** @private */
6149     _handleRefresh : function(url, params, loadOnce){
6150         if(!loadOnce || !this.loaded){
6151             var updater = this.bodyEl.getUpdateManager();
6152             updater.update(url, params, this._setLoaded.createDelegate(this));
6153         }
6154     },
6155
6156     /**
6157      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6158      *   Will fail silently if the setUrl method has not been called.
6159      *   This does not activate the panel, just updates its content.
6160      */
6161     refresh : function(){
6162         if(this.refreshDelegate){
6163            this.loaded = false;
6164            this.refreshDelegate();
6165         }
6166     },
6167
6168     /** @private */
6169     _setLoaded : function(){
6170         this.loaded = true;
6171     },
6172
6173     /** @private */
6174     closeClick : function(e){
6175         var o = {};
6176         e.stopEvent();
6177         this.fireEvent("beforeclose", this, o);
6178         if(o.cancel !== true){
6179             this.tabPanel.removeTab(this.id);
6180         }
6181     },
6182     /**
6183      * The text displayed in the tooltip for the close icon.
6184      * @type String
6185      */
6186     closeText : "Close this tab"
6187 });
6188
6189 /** @private */
6190 Roo.TabPanel.prototype.createStrip = function(container){
6191     var strip = document.createElement("div");
6192     strip.className = "x-tabs-wrap";
6193     container.appendChild(strip);
6194     return strip;
6195 };
6196 /** @private */
6197 Roo.TabPanel.prototype.createStripList = function(strip){
6198     // div wrapper for retard IE
6199     // returns the "tr" element.
6200     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6201         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6202         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6203     return strip.firstChild.firstChild.firstChild.firstChild;
6204 };
6205 /** @private */
6206 Roo.TabPanel.prototype.createBody = function(container){
6207     var body = document.createElement("div");
6208     Roo.id(body, "tab-body");
6209     Roo.fly(body).addClass("x-tabs-body");
6210     container.appendChild(body);
6211     return body;
6212 };
6213 /** @private */
6214 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6215     var body = Roo.getDom(id);
6216     if(!body){
6217         body = document.createElement("div");
6218         body.id = id;
6219     }
6220     Roo.fly(body).addClass("x-tabs-item-body");
6221     bodyEl.insertBefore(body, bodyEl.firstChild);
6222     return body;
6223 };
6224 /** @private */
6225 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6226     var td = document.createElement("td");
6227     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6228     //stripEl.appendChild(td);
6229     if(closable){
6230         td.className = "x-tabs-closable";
6231         if(!this.closeTpl){
6232             this.closeTpl = new Roo.Template(
6233                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6234                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6235                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6236             );
6237         }
6238         var el = this.closeTpl.overwrite(td, {"text": text});
6239         var close = el.getElementsByTagName("div")[0];
6240         var inner = el.getElementsByTagName("em")[0];
6241         return {"el": el, "close": close, "inner": inner};
6242     } else {
6243         if(!this.tabTpl){
6244             this.tabTpl = new Roo.Template(
6245                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6246                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6247             );
6248         }
6249         var el = this.tabTpl.overwrite(td, {"text": text});
6250         var inner = el.getElementsByTagName("em")[0];
6251         return {"el": el, "inner": inner};
6252     }
6253 };/*
6254  * Based on:
6255  * Ext JS Library 1.1.1
6256  * Copyright(c) 2006-2007, Ext JS, LLC.
6257  *
6258  * Originally Released Under LGPL - original licence link has changed is not relivant.
6259  *
6260  * Fork - LGPL
6261  * <script type="text/javascript">
6262  */
6263
6264 /**
6265  * @class Roo.Button
6266  * @extends Roo.util.Observable
6267  * Simple Button class
6268  * @cfg {String} text The button text
6269  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6270  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6271  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6272  * @cfg {Object} scope The scope of the handler
6273  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6274  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6275  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6276  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6277  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6278  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6279    applies if enableToggle = true)
6280  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6281  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6282   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6283  * @constructor
6284  * Create a new button
6285  * @param {Object} config The config object
6286  */
6287 Roo.Button = function(renderTo, config)
6288 {
6289     if (!config) {
6290         config = renderTo;
6291         renderTo = config.renderTo || false;
6292     }
6293     
6294     Roo.apply(this, config);
6295     this.addEvents({
6296         /**
6297              * @event click
6298              * Fires when this button is clicked
6299              * @param {Button} this
6300              * @param {EventObject} e The click event
6301              */
6302             "click" : true,
6303         /**
6304              * @event toggle
6305              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6306              * @param {Button} this
6307              * @param {Boolean} pressed
6308              */
6309             "toggle" : true,
6310         /**
6311              * @event mouseover
6312              * Fires when the mouse hovers over the button
6313              * @param {Button} this
6314              * @param {Event} e The event object
6315              */
6316         'mouseover' : true,
6317         /**
6318              * @event mouseout
6319              * Fires when the mouse exits the button
6320              * @param {Button} this
6321              * @param {Event} e The event object
6322              */
6323         'mouseout': true,
6324          /**
6325              * @event render
6326              * Fires when the button is rendered
6327              * @param {Button} this
6328              */
6329         'render': true
6330     });
6331     if(this.menu){
6332         this.menu = Roo.menu.MenuMgr.get(this.menu);
6333     }
6334     // register listeners first!!  - so render can be captured..
6335     Roo.util.Observable.call(this);
6336     if(renderTo){
6337         this.render(renderTo);
6338     }
6339     
6340   
6341 };
6342
6343 Roo.extend(Roo.Button, Roo.util.Observable, {
6344     /**
6345      * 
6346      */
6347     
6348     /**
6349      * Read-only. True if this button is hidden
6350      * @type Boolean
6351      */
6352     hidden : false,
6353     /**
6354      * Read-only. True if this button is disabled
6355      * @type Boolean
6356      */
6357     disabled : false,
6358     /**
6359      * Read-only. True if this button is pressed (only if enableToggle = true)
6360      * @type Boolean
6361      */
6362     pressed : false,
6363
6364     /**
6365      * @cfg {Number} tabIndex 
6366      * The DOM tabIndex for this button (defaults to undefined)
6367      */
6368     tabIndex : undefined,
6369
6370     /**
6371      * @cfg {Boolean} enableToggle
6372      * True to enable pressed/not pressed toggling (defaults to false)
6373      */
6374     enableToggle: false,
6375     /**
6376      * @cfg {Roo.menu.Menu} menu
6377      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6378      */
6379     menu : undefined,
6380     /**
6381      * @cfg {String} menuAlign
6382      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6383      */
6384     menuAlign : "tl-bl?",
6385
6386     /**
6387      * @cfg {String} iconCls
6388      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6389      */
6390     iconCls : undefined,
6391     /**
6392      * @cfg {String} type
6393      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6394      */
6395     type : 'button',
6396
6397     // private
6398     menuClassTarget: 'tr',
6399
6400     /**
6401      * @cfg {String} clickEvent
6402      * The type of event to map to the button's event handler (defaults to 'click')
6403      */
6404     clickEvent : 'click',
6405
6406     /**
6407      * @cfg {Boolean} handleMouseEvents
6408      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6409      */
6410     handleMouseEvents : true,
6411
6412     /**
6413      * @cfg {String} tooltipType
6414      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6415      */
6416     tooltipType : 'qtip',
6417
6418     /**
6419      * @cfg {String} cls
6420      * A CSS class to apply to the button's main element.
6421      */
6422     
6423     /**
6424      * @cfg {Roo.Template} template (Optional)
6425      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6426      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6427      * require code modifications if required elements (e.g. a button) aren't present.
6428      */
6429
6430     // private
6431     render : function(renderTo){
6432         var btn;
6433         if(this.hideParent){
6434             this.parentEl = Roo.get(renderTo);
6435         }
6436         if(!this.dhconfig){
6437             if(!this.template){
6438                 if(!Roo.Button.buttonTemplate){
6439                     // hideous table template
6440                     Roo.Button.buttonTemplate = new Roo.Template(
6441                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6442                         '<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>',
6443                         "</tr></tbody></table>");
6444                 }
6445                 this.template = Roo.Button.buttonTemplate;
6446             }
6447             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6448             var btnEl = btn.child("button:first");
6449             btnEl.on('focus', this.onFocus, this);
6450             btnEl.on('blur', this.onBlur, this);
6451             if(this.cls){
6452                 btn.addClass(this.cls);
6453             }
6454             if(this.icon){
6455                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6456             }
6457             if(this.iconCls){
6458                 btnEl.addClass(this.iconCls);
6459                 if(!this.cls){
6460                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6461                 }
6462             }
6463             if(this.tabIndex !== undefined){
6464                 btnEl.dom.tabIndex = this.tabIndex;
6465             }
6466             if(this.tooltip){
6467                 if(typeof this.tooltip == 'object'){
6468                     Roo.QuickTips.tips(Roo.apply({
6469                           target: btnEl.id
6470                     }, this.tooltip));
6471                 } else {
6472                     btnEl.dom[this.tooltipType] = this.tooltip;
6473                 }
6474             }
6475         }else{
6476             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6477         }
6478         this.el = btn;
6479         if(this.id){
6480             this.el.dom.id = this.el.id = this.id;
6481         }
6482         if(this.menu){
6483             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6484             this.menu.on("show", this.onMenuShow, this);
6485             this.menu.on("hide", this.onMenuHide, this);
6486         }
6487         btn.addClass("x-btn");
6488         if(Roo.isIE && !Roo.isIE7){
6489             this.autoWidth.defer(1, this);
6490         }else{
6491             this.autoWidth();
6492         }
6493         if(this.handleMouseEvents){
6494             btn.on("mouseover", this.onMouseOver, this);
6495             btn.on("mouseout", this.onMouseOut, this);
6496             btn.on("mousedown", this.onMouseDown, this);
6497         }
6498         btn.on(this.clickEvent, this.onClick, this);
6499         //btn.on("mouseup", this.onMouseUp, this);
6500         if(this.hidden){
6501             this.hide();
6502         }
6503         if(this.disabled){
6504             this.disable();
6505         }
6506         Roo.ButtonToggleMgr.register(this);
6507         if(this.pressed){
6508             this.el.addClass("x-btn-pressed");
6509         }
6510         if(this.repeat){
6511             var repeater = new Roo.util.ClickRepeater(btn,
6512                 typeof this.repeat == "object" ? this.repeat : {}
6513             );
6514             repeater.on("click", this.onClick,  this);
6515         }
6516         
6517         this.fireEvent('render', this);
6518         
6519     },
6520     /**
6521      * Returns the button's underlying element
6522      * @return {Roo.Element} The element
6523      */
6524     getEl : function(){
6525         return this.el;  
6526     },
6527     
6528     /**
6529      * Destroys this Button and removes any listeners.
6530      */
6531     destroy : function(){
6532         Roo.ButtonToggleMgr.unregister(this);
6533         this.el.removeAllListeners();
6534         this.purgeListeners();
6535         this.el.remove();
6536     },
6537
6538     // private
6539     autoWidth : function(){
6540         if(this.el){
6541             this.el.setWidth("auto");
6542             if(Roo.isIE7 && Roo.isStrict){
6543                 var ib = this.el.child('button');
6544                 if(ib && ib.getWidth() > 20){
6545                     ib.clip();
6546                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6547                 }
6548             }
6549             if(this.minWidth){
6550                 if(this.hidden){
6551                     this.el.beginMeasure();
6552                 }
6553                 if(this.el.getWidth() < this.minWidth){
6554                     this.el.setWidth(this.minWidth);
6555                 }
6556                 if(this.hidden){
6557                     this.el.endMeasure();
6558                 }
6559             }
6560         }
6561     },
6562
6563     /**
6564      * Assigns this button's click handler
6565      * @param {Function} handler The function to call when the button is clicked
6566      * @param {Object} scope (optional) Scope for the function passed in
6567      */
6568     setHandler : function(handler, scope){
6569         this.handler = handler;
6570         this.scope = scope;  
6571     },
6572     
6573     /**
6574      * Sets this button's text
6575      * @param {String} text The button text
6576      */
6577     setText : function(text){
6578         this.text = text;
6579         if(this.el){
6580             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6581         }
6582         this.autoWidth();
6583     },
6584     
6585     /**
6586      * Gets the text for this button
6587      * @return {String} The button text
6588      */
6589     getText : function(){
6590         return this.text;  
6591     },
6592     
6593     /**
6594      * Show this button
6595      */
6596     show: function(){
6597         this.hidden = false;
6598         if(this.el){
6599             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6600         }
6601     },
6602     
6603     /**
6604      * Hide this button
6605      */
6606     hide: function(){
6607         this.hidden = true;
6608         if(this.el){
6609             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6610         }
6611     },
6612     
6613     /**
6614      * Convenience function for boolean show/hide
6615      * @param {Boolean} visible True to show, false to hide
6616      */
6617     setVisible: function(visible){
6618         if(visible) {
6619             this.show();
6620         }else{
6621             this.hide();
6622         }
6623     },
6624     
6625     /**
6626      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6627      * @param {Boolean} state (optional) Force a particular state
6628      */
6629     toggle : function(state){
6630         state = state === undefined ? !this.pressed : state;
6631         if(state != this.pressed){
6632             if(state){
6633                 this.el.addClass("x-btn-pressed");
6634                 this.pressed = true;
6635                 this.fireEvent("toggle", this, true);
6636             }else{
6637                 this.el.removeClass("x-btn-pressed");
6638                 this.pressed = false;
6639                 this.fireEvent("toggle", this, false);
6640             }
6641             if(this.toggleHandler){
6642                 this.toggleHandler.call(this.scope || this, this, state);
6643             }
6644         }
6645     },
6646     
6647     /**
6648      * Focus the button
6649      */
6650     focus : function(){
6651         this.el.child('button:first').focus();
6652     },
6653     
6654     /**
6655      * Disable this button
6656      */
6657     disable : function(){
6658         if(this.el){
6659             this.el.addClass("x-btn-disabled");
6660         }
6661         this.disabled = true;
6662     },
6663     
6664     /**
6665      * Enable this button
6666      */
6667     enable : function(){
6668         if(this.el){
6669             this.el.removeClass("x-btn-disabled");
6670         }
6671         this.disabled = false;
6672     },
6673
6674     /**
6675      * Convenience function for boolean enable/disable
6676      * @param {Boolean} enabled True to enable, false to disable
6677      */
6678     setDisabled : function(v){
6679         this[v !== true ? "enable" : "disable"]();
6680     },
6681
6682     // private
6683     onClick : function(e)
6684     {
6685         if(e){
6686             e.preventDefault();
6687         }
6688         if(e.button != 0){
6689             return;
6690         }
6691         if(!this.disabled){
6692             if(this.enableToggle){
6693                 this.toggle();
6694             }
6695             if(this.menu && !this.menu.isVisible()){
6696                 this.menu.show(this.el, this.menuAlign);
6697             }
6698             this.fireEvent("click", this, e);
6699             if(this.handler){
6700                 this.el.removeClass("x-btn-over");
6701                 this.handler.call(this.scope || this, this, e);
6702             }
6703         }
6704     },
6705     // private
6706     onMouseOver : function(e){
6707         if(!this.disabled){
6708             this.el.addClass("x-btn-over");
6709             this.fireEvent('mouseover', this, e);
6710         }
6711     },
6712     // private
6713     onMouseOut : function(e){
6714         if(!e.within(this.el,  true)){
6715             this.el.removeClass("x-btn-over");
6716             this.fireEvent('mouseout', this, e);
6717         }
6718     },
6719     // private
6720     onFocus : function(e){
6721         if(!this.disabled){
6722             this.el.addClass("x-btn-focus");
6723         }
6724     },
6725     // private
6726     onBlur : function(e){
6727         this.el.removeClass("x-btn-focus");
6728     },
6729     // private
6730     onMouseDown : function(e){
6731         if(!this.disabled && e.button == 0){
6732             this.el.addClass("x-btn-click");
6733             Roo.get(document).on('mouseup', this.onMouseUp, this);
6734         }
6735     },
6736     // private
6737     onMouseUp : function(e){
6738         if(e.button == 0){
6739             this.el.removeClass("x-btn-click");
6740             Roo.get(document).un('mouseup', this.onMouseUp, this);
6741         }
6742     },
6743     // private
6744     onMenuShow : function(e){
6745         this.el.addClass("x-btn-menu-active");
6746     },
6747     // private
6748     onMenuHide : function(e){
6749         this.el.removeClass("x-btn-menu-active");
6750     }   
6751 });
6752
6753 // Private utility class used by Button
6754 Roo.ButtonToggleMgr = function(){
6755    var groups = {};
6756    
6757    function toggleGroup(btn, state){
6758        if(state){
6759            var g = groups[btn.toggleGroup];
6760            for(var i = 0, l = g.length; i < l; i++){
6761                if(g[i] != btn){
6762                    g[i].toggle(false);
6763                }
6764            }
6765        }
6766    }
6767    
6768    return {
6769        register : function(btn){
6770            if(!btn.toggleGroup){
6771                return;
6772            }
6773            var g = groups[btn.toggleGroup];
6774            if(!g){
6775                g = groups[btn.toggleGroup] = [];
6776            }
6777            g.push(btn);
6778            btn.on("toggle", toggleGroup);
6779        },
6780        
6781        unregister : function(btn){
6782            if(!btn.toggleGroup){
6783                return;
6784            }
6785            var g = groups[btn.toggleGroup];
6786            if(g){
6787                g.remove(btn);
6788                btn.un("toggle", toggleGroup);
6789            }
6790        }
6791    };
6792 }();/*
6793  * Based on:
6794  * Ext JS Library 1.1.1
6795  * Copyright(c) 2006-2007, Ext JS, LLC.
6796  *
6797  * Originally Released Under LGPL - original licence link has changed is not relivant.
6798  *
6799  * Fork - LGPL
6800  * <script type="text/javascript">
6801  */
6802  
6803 /**
6804  * @class Roo.SplitButton
6805  * @extends Roo.Button
6806  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6807  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6808  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6809  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6810  * @cfg {String} arrowTooltip The title attribute of the arrow
6811  * @constructor
6812  * Create a new menu button
6813  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6814  * @param {Object} config The config object
6815  */
6816 Roo.SplitButton = function(renderTo, config){
6817     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6818     /**
6819      * @event arrowclick
6820      * Fires when this button's arrow is clicked
6821      * @param {SplitButton} this
6822      * @param {EventObject} e The click event
6823      */
6824     this.addEvents({"arrowclick":true});
6825 };
6826
6827 Roo.extend(Roo.SplitButton, Roo.Button, {
6828     render : function(renderTo){
6829         // this is one sweet looking template!
6830         var tpl = new Roo.Template(
6831             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6832             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6833             '<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>',
6834             "</tbody></table></td><td>",
6835             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6836             '<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>',
6837             "</tbody></table></td></tr></table>"
6838         );
6839         var btn = tpl.append(renderTo, [this.text, this.type], true);
6840         var btnEl = btn.child("button");
6841         if(this.cls){
6842             btn.addClass(this.cls);
6843         }
6844         if(this.icon){
6845             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6846         }
6847         if(this.iconCls){
6848             btnEl.addClass(this.iconCls);
6849             if(!this.cls){
6850                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6851             }
6852         }
6853         this.el = btn;
6854         if(this.handleMouseEvents){
6855             btn.on("mouseover", this.onMouseOver, this);
6856             btn.on("mouseout", this.onMouseOut, this);
6857             btn.on("mousedown", this.onMouseDown, this);
6858             btn.on("mouseup", this.onMouseUp, this);
6859         }
6860         btn.on(this.clickEvent, this.onClick, this);
6861         if(this.tooltip){
6862             if(typeof this.tooltip == 'object'){
6863                 Roo.QuickTips.tips(Roo.apply({
6864                       target: btnEl.id
6865                 }, this.tooltip));
6866             } else {
6867                 btnEl.dom[this.tooltipType] = this.tooltip;
6868             }
6869         }
6870         if(this.arrowTooltip){
6871             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6872         }
6873         if(this.hidden){
6874             this.hide();
6875         }
6876         if(this.disabled){
6877             this.disable();
6878         }
6879         if(this.pressed){
6880             this.el.addClass("x-btn-pressed");
6881         }
6882         if(Roo.isIE && !Roo.isIE7){
6883             this.autoWidth.defer(1, this);
6884         }else{
6885             this.autoWidth();
6886         }
6887         if(this.menu){
6888             this.menu.on("show", this.onMenuShow, this);
6889             this.menu.on("hide", this.onMenuHide, this);
6890         }
6891         this.fireEvent('render', this);
6892     },
6893
6894     // private
6895     autoWidth : function(){
6896         if(this.el){
6897             var tbl = this.el.child("table:first");
6898             var tbl2 = this.el.child("table:last");
6899             this.el.setWidth("auto");
6900             tbl.setWidth("auto");
6901             if(Roo.isIE7 && Roo.isStrict){
6902                 var ib = this.el.child('button:first');
6903                 if(ib && ib.getWidth() > 20){
6904                     ib.clip();
6905                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6906                 }
6907             }
6908             if(this.minWidth){
6909                 if(this.hidden){
6910                     this.el.beginMeasure();
6911                 }
6912                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6913                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6914                 }
6915                 if(this.hidden){
6916                     this.el.endMeasure();
6917                 }
6918             }
6919             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6920         } 
6921     },
6922     /**
6923      * Sets this button's click handler
6924      * @param {Function} handler The function to call when the button is clicked
6925      * @param {Object} scope (optional) Scope for the function passed above
6926      */
6927     setHandler : function(handler, scope){
6928         this.handler = handler;
6929         this.scope = scope;  
6930     },
6931     
6932     /**
6933      * Sets this button's arrow click handler
6934      * @param {Function} handler The function to call when the arrow is clicked
6935      * @param {Object} scope (optional) Scope for the function passed above
6936      */
6937     setArrowHandler : function(handler, scope){
6938         this.arrowHandler = handler;
6939         this.scope = scope;  
6940     },
6941     
6942     /**
6943      * Focus the button
6944      */
6945     focus : function(){
6946         if(this.el){
6947             this.el.child("button:first").focus();
6948         }
6949     },
6950
6951     // private
6952     onClick : function(e){
6953         e.preventDefault();
6954         if(!this.disabled){
6955             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6956                 if(this.menu && !this.menu.isVisible()){
6957                     this.menu.show(this.el, this.menuAlign);
6958                 }
6959                 this.fireEvent("arrowclick", this, e);
6960                 if(this.arrowHandler){
6961                     this.arrowHandler.call(this.scope || this, this, e);
6962                 }
6963             }else{
6964                 this.fireEvent("click", this, e);
6965                 if(this.handler){
6966                     this.handler.call(this.scope || this, this, e);
6967                 }
6968             }
6969         }
6970     },
6971     // private
6972     onMouseDown : function(e){
6973         if(!this.disabled){
6974             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6975         }
6976     },
6977     // private
6978     onMouseUp : function(e){
6979         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6980     }   
6981 });
6982
6983
6984 // backwards compat
6985 Roo.MenuButton = Roo.SplitButton;/*
6986  * Based on:
6987  * Ext JS Library 1.1.1
6988  * Copyright(c) 2006-2007, Ext JS, LLC.
6989  *
6990  * Originally Released Under LGPL - original licence link has changed is not relivant.
6991  *
6992  * Fork - LGPL
6993  * <script type="text/javascript">
6994  */
6995
6996 /**
6997  * @class Roo.Toolbar
6998  * @children   Roo.Toolbar.Item Roo.form.Field
6999  * Basic Toolbar class.
7000  * @constructor
7001  * Creates a new Toolbar
7002  * @param {Object} container The config object
7003  */ 
7004 Roo.Toolbar = function(container, buttons, config)
7005 {
7006     /// old consturctor format still supported..
7007     if(container instanceof Array){ // omit the container for later rendering
7008         buttons = container;
7009         config = buttons;
7010         container = null;
7011     }
7012     if (typeof(container) == 'object' && container.xtype) {
7013         config = container;
7014         container = config.container;
7015         buttons = config.buttons || []; // not really - use items!!
7016     }
7017     var xitems = [];
7018     if (config && config.items) {
7019         xitems = config.items;
7020         delete config.items;
7021     }
7022     Roo.apply(this, config);
7023     this.buttons = buttons;
7024     
7025     if(container){
7026         this.render(container);
7027     }
7028     this.xitems = xitems;
7029     Roo.each(xitems, function(b) {
7030         this.add(b);
7031     }, this);
7032     
7033 };
7034
7035 Roo.Toolbar.prototype = {
7036     /**
7037      * @cfg {Array} items
7038      * array of button configs or elements to add (will be converted to a MixedCollection)
7039      */
7040     items: false,
7041     /**
7042      * @cfg {String/HTMLElement/Element} container
7043      * The id or element that will contain the toolbar
7044      */
7045     // private
7046     render : function(ct){
7047         this.el = Roo.get(ct);
7048         if(this.cls){
7049             this.el.addClass(this.cls);
7050         }
7051         // using a table allows for vertical alignment
7052         // 100% width is needed by Safari...
7053         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7054         this.tr = this.el.child("tr", true);
7055         var autoId = 0;
7056         this.items = new Roo.util.MixedCollection(false, function(o){
7057             return o.id || ("item" + (++autoId));
7058         });
7059         if(this.buttons){
7060             this.add.apply(this, this.buttons);
7061             delete this.buttons;
7062         }
7063     },
7064
7065     /**
7066      * Adds element(s) to the toolbar -- this function takes a variable number of 
7067      * arguments of mixed type and adds them to the toolbar.
7068      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7069      * <ul>
7070      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7071      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7072      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7073      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7074      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7075      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7076      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7077      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7078      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7079      * </ul>
7080      * @param {Mixed} arg2
7081      * @param {Mixed} etc.
7082      */
7083     add : function(){
7084         var a = arguments, l = a.length;
7085         for(var i = 0; i < l; i++){
7086             this._add(a[i]);
7087         }
7088     },
7089     // private..
7090     _add : function(el) {
7091         
7092         if (el.xtype) {
7093             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7094         }
7095         
7096         if (el.applyTo){ // some kind of form field
7097             return this.addField(el);
7098         } 
7099         if (el.render){ // some kind of Toolbar.Item
7100             return this.addItem(el);
7101         }
7102         if (typeof el == "string"){ // string
7103             if(el == "separator" || el == "-"){
7104                 return this.addSeparator();
7105             }
7106             if (el == " "){
7107                 return this.addSpacer();
7108             }
7109             if(el == "->"){
7110                 return this.addFill();
7111             }
7112             return this.addText(el);
7113             
7114         }
7115         if(el.tagName){ // element
7116             return this.addElement(el);
7117         }
7118         if(typeof el == "object"){ // must be button config?
7119             return this.addButton(el);
7120         }
7121         // and now what?!?!
7122         return false;
7123         
7124     },
7125     
7126     /**
7127      * Add an Xtype element
7128      * @param {Object} xtype Xtype Object
7129      * @return {Object} created Object
7130      */
7131     addxtype : function(e){
7132         return this.add(e);  
7133     },
7134     
7135     /**
7136      * Returns the Element for this toolbar.
7137      * @return {Roo.Element}
7138      */
7139     getEl : function(){
7140         return this.el;  
7141     },
7142     
7143     /**
7144      * Adds a separator
7145      * @return {Roo.Toolbar.Item} The separator item
7146      */
7147     addSeparator : function(){
7148         return this.addItem(new Roo.Toolbar.Separator());
7149     },
7150
7151     /**
7152      * Adds a spacer element
7153      * @return {Roo.Toolbar.Spacer} The spacer item
7154      */
7155     addSpacer : function(){
7156         return this.addItem(new Roo.Toolbar.Spacer());
7157     },
7158
7159     /**
7160      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7161      * @return {Roo.Toolbar.Fill} The fill item
7162      */
7163     addFill : function(){
7164         return this.addItem(new Roo.Toolbar.Fill());
7165     },
7166
7167     /**
7168      * Adds any standard HTML element to the toolbar
7169      * @param {String/HTMLElement/Element} el The element or id of the element to add
7170      * @return {Roo.Toolbar.Item} The element's item
7171      */
7172     addElement : function(el){
7173         return this.addItem(new Roo.Toolbar.Item(el));
7174     },
7175     /**
7176      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7177      * @type Roo.util.MixedCollection  
7178      */
7179     items : false,
7180      
7181     /**
7182      * Adds any Toolbar.Item or subclass
7183      * @param {Roo.Toolbar.Item} item
7184      * @return {Roo.Toolbar.Item} The item
7185      */
7186     addItem : function(item){
7187         var td = this.nextBlock();
7188         item.render(td);
7189         this.items.add(item);
7190         return item;
7191     },
7192     
7193     /**
7194      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7195      * @param {Object/Array} config A button config or array of configs
7196      * @return {Roo.Toolbar.Button/Array}
7197      */
7198     addButton : function(config){
7199         if(config instanceof Array){
7200             var buttons = [];
7201             for(var i = 0, len = config.length; i < len; i++) {
7202                 buttons.push(this.addButton(config[i]));
7203             }
7204             return buttons;
7205         }
7206         var b = config;
7207         if(!(config instanceof Roo.Toolbar.Button)){
7208             b = config.split ?
7209                 new Roo.Toolbar.SplitButton(config) :
7210                 new Roo.Toolbar.Button(config);
7211         }
7212         var td = this.nextBlock();
7213         b.render(td);
7214         this.items.add(b);
7215         return b;
7216     },
7217     
7218     /**
7219      * Adds text to the toolbar
7220      * @param {String} text The text to add
7221      * @return {Roo.Toolbar.Item} The element's item
7222      */
7223     addText : function(text){
7224         return this.addItem(new Roo.Toolbar.TextItem(text));
7225     },
7226     
7227     /**
7228      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7229      * @param {Number} index The index where the item is to be inserted
7230      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7231      * @return {Roo.Toolbar.Button/Item}
7232      */
7233     insertButton : function(index, item){
7234         if(item instanceof Array){
7235             var buttons = [];
7236             for(var i = 0, len = item.length; i < len; i++) {
7237                buttons.push(this.insertButton(index + i, item[i]));
7238             }
7239             return buttons;
7240         }
7241         if (!(item instanceof Roo.Toolbar.Button)){
7242            item = new Roo.Toolbar.Button(item);
7243         }
7244         var td = document.createElement("td");
7245         this.tr.insertBefore(td, this.tr.childNodes[index]);
7246         item.render(td);
7247         this.items.insert(index, item);
7248         return item;
7249     },
7250     
7251     /**
7252      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7253      * @param {Object} config
7254      * @return {Roo.Toolbar.Item} The element's item
7255      */
7256     addDom : function(config, returnEl){
7257         var td = this.nextBlock();
7258         Roo.DomHelper.overwrite(td, config);
7259         var ti = new Roo.Toolbar.Item(td.firstChild);
7260         ti.render(td);
7261         this.items.add(ti);
7262         return ti;
7263     },
7264
7265     /**
7266      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7267      * @type Roo.util.MixedCollection  
7268      */
7269     fields : false,
7270     
7271     /**
7272      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7273      * Note: the field should not have been rendered yet. For a field that has already been
7274      * rendered, use {@link #addElement}.
7275      * @param {Roo.form.Field} field
7276      * @return {Roo.ToolbarItem}
7277      */
7278      
7279       
7280     addField : function(field) {
7281         if (!this.fields) {
7282             var autoId = 0;
7283             this.fields = new Roo.util.MixedCollection(false, function(o){
7284                 return o.id || ("item" + (++autoId));
7285             });
7286
7287         }
7288         
7289         var td = this.nextBlock();
7290         field.render(td);
7291         var ti = new Roo.Toolbar.Item(td.firstChild);
7292         ti.render(td);
7293         this.items.add(ti);
7294         this.fields.add(field);
7295         return ti;
7296     },
7297     /**
7298      * Hide the toolbar
7299      * @method hide
7300      */
7301      
7302       
7303     hide : function()
7304     {
7305         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7306         this.el.child('div').hide();
7307     },
7308     /**
7309      * Show the toolbar
7310      * @method show
7311      */
7312     show : function()
7313     {
7314         this.el.child('div').show();
7315     },
7316       
7317     // private
7318     nextBlock : function(){
7319         var td = document.createElement("td");
7320         this.tr.appendChild(td);
7321         return td;
7322     },
7323
7324     // private
7325     destroy : function(){
7326         if(this.items){ // rendered?
7327             Roo.destroy.apply(Roo, this.items.items);
7328         }
7329         if(this.fields){ // rendered?
7330             Roo.destroy.apply(Roo, this.fields.items);
7331         }
7332         Roo.Element.uncache(this.el, this.tr);
7333     }
7334 };
7335
7336 /**
7337  * @class Roo.Toolbar.Item
7338  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7339  * @constructor
7340  * Creates a new Item
7341  * @param {HTMLElement} el 
7342  */
7343 Roo.Toolbar.Item = function(el){
7344     var cfg = {};
7345     if (typeof (el.xtype) != 'undefined') {
7346         cfg = el;
7347         el = cfg.el;
7348     }
7349     
7350     this.el = Roo.getDom(el);
7351     this.id = Roo.id(this.el);
7352     this.hidden = false;
7353     
7354     this.addEvents({
7355          /**
7356              * @event render
7357              * Fires when the button is rendered
7358              * @param {Button} this
7359              */
7360         'render': true
7361     });
7362     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7363 };
7364 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7365 //Roo.Toolbar.Item.prototype = {
7366     
7367     /**
7368      * Get this item's HTML Element
7369      * @return {HTMLElement}
7370      */
7371     getEl : function(){
7372        return this.el;  
7373     },
7374
7375     // private
7376     render : function(td){
7377         
7378          this.td = td;
7379         td.appendChild(this.el);
7380         
7381         this.fireEvent('render', this);
7382     },
7383     
7384     /**
7385      * Removes and destroys this item.
7386      */
7387     destroy : function(){
7388         this.td.parentNode.removeChild(this.td);
7389     },
7390     
7391     /**
7392      * Shows this item.
7393      */
7394     show: function(){
7395         this.hidden = false;
7396         this.td.style.display = "";
7397     },
7398     
7399     /**
7400      * Hides this item.
7401      */
7402     hide: function(){
7403         this.hidden = true;
7404         this.td.style.display = "none";
7405     },
7406     
7407     /**
7408      * Convenience function for boolean show/hide.
7409      * @param {Boolean} visible true to show/false to hide
7410      */
7411     setVisible: function(visible){
7412         if(visible) {
7413             this.show();
7414         }else{
7415             this.hide();
7416         }
7417     },
7418     
7419     /**
7420      * Try to focus this item.
7421      */
7422     focus : function(){
7423         Roo.fly(this.el).focus();
7424     },
7425     
7426     /**
7427      * Disables this item.
7428      */
7429     disable : function(){
7430         Roo.fly(this.td).addClass("x-item-disabled");
7431         this.disabled = true;
7432         this.el.disabled = true;
7433     },
7434     
7435     /**
7436      * Enables this item.
7437      */
7438     enable : function(){
7439         Roo.fly(this.td).removeClass("x-item-disabled");
7440         this.disabled = false;
7441         this.el.disabled = false;
7442     }
7443 });
7444
7445
7446 /**
7447  * @class Roo.Toolbar.Separator
7448  * @extends Roo.Toolbar.Item
7449  * A simple toolbar separator class
7450  * @constructor
7451  * Creates a new Separator
7452  */
7453 Roo.Toolbar.Separator = function(cfg){
7454     
7455     var s = document.createElement("span");
7456     s.className = "ytb-sep";
7457     if (cfg) {
7458         cfg.el = s;
7459     }
7460     
7461     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7462 };
7463 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7464     enable:Roo.emptyFn,
7465     disable:Roo.emptyFn,
7466     focus:Roo.emptyFn
7467 });
7468
7469 /**
7470  * @class Roo.Toolbar.Spacer
7471  * @extends Roo.Toolbar.Item
7472  * A simple element that adds extra horizontal space to a toolbar.
7473  * @constructor
7474  * Creates a new Spacer
7475  */
7476 Roo.Toolbar.Spacer = function(cfg){
7477     var s = document.createElement("div");
7478     s.className = "ytb-spacer";
7479     if (cfg) {
7480         cfg.el = s;
7481     }
7482     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7483 };
7484 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7485     enable:Roo.emptyFn,
7486     disable:Roo.emptyFn,
7487     focus:Roo.emptyFn
7488 });
7489
7490 /**
7491  * @class Roo.Toolbar.Fill
7492  * @extends Roo.Toolbar.Spacer
7493  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7494  * @constructor
7495  * Creates a new Spacer
7496  */
7497 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7498     // private
7499     render : function(td){
7500         td.style.width = '100%';
7501         Roo.Toolbar.Fill.superclass.render.call(this, td);
7502     }
7503 });
7504
7505 /**
7506  * @class Roo.Toolbar.TextItem
7507  * @extends Roo.Toolbar.Item
7508  * A simple class that renders text directly into a toolbar.
7509  * @constructor
7510  * Creates a new TextItem
7511  * @cfg {string} text 
7512  */
7513 Roo.Toolbar.TextItem = function(cfg){
7514     var  text = cfg || "";
7515     if (typeof(cfg) == 'object') {
7516         text = cfg.text || "";
7517     }  else {
7518         cfg = null;
7519     }
7520     var s = document.createElement("span");
7521     s.className = "ytb-text";
7522     s.innerHTML = text;
7523     if (cfg) {
7524         cfg.el  = s;
7525     }
7526     
7527     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7528 };
7529 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7530     
7531      
7532     enable:Roo.emptyFn,
7533     disable:Roo.emptyFn,
7534     focus:Roo.emptyFn
7535 });
7536
7537 /**
7538  * @class Roo.Toolbar.Button
7539  * @extends Roo.Button
7540  * A button that renders into a toolbar.
7541  * @constructor
7542  * Creates a new Button
7543  * @param {Object} config A standard {@link Roo.Button} config object
7544  */
7545 Roo.Toolbar.Button = function(config){
7546     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7547 };
7548 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7549 {
7550     
7551     
7552     render : function(td){
7553         this.td = td;
7554         Roo.Toolbar.Button.superclass.render.call(this, td);
7555     },
7556     
7557     /**
7558      * Removes and destroys this button
7559      */
7560     destroy : function(){
7561         Roo.Toolbar.Button.superclass.destroy.call(this);
7562         this.td.parentNode.removeChild(this.td);
7563     },
7564     
7565     /**
7566      * Shows this button
7567      */
7568     show: function(){
7569         this.hidden = false;
7570         this.td.style.display = "";
7571     },
7572     
7573     /**
7574      * Hides this button
7575      */
7576     hide: function(){
7577         this.hidden = true;
7578         this.td.style.display = "none";
7579     },
7580
7581     /**
7582      * Disables this item
7583      */
7584     disable : function(){
7585         Roo.fly(this.td).addClass("x-item-disabled");
7586         this.disabled = true;
7587     },
7588
7589     /**
7590      * Enables this item
7591      */
7592     enable : function(){
7593         Roo.fly(this.td).removeClass("x-item-disabled");
7594         this.disabled = false;
7595     }
7596 });
7597 // backwards compat
7598 Roo.ToolbarButton = Roo.Toolbar.Button;
7599
7600 /**
7601  * @class Roo.Toolbar.SplitButton
7602  * @extends Roo.SplitButton
7603  * A menu button that renders into a toolbar.
7604  * @constructor
7605  * Creates a new SplitButton
7606  * @param {Object} config A standard {@link Roo.SplitButton} config object
7607  */
7608 Roo.Toolbar.SplitButton = function(config){
7609     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7610 };
7611 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7612     render : function(td){
7613         this.td = td;
7614         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7615     },
7616     
7617     /**
7618      * Removes and destroys this button
7619      */
7620     destroy : function(){
7621         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7622         this.td.parentNode.removeChild(this.td);
7623     },
7624     
7625     /**
7626      * Shows this button
7627      */
7628     show: function(){
7629         this.hidden = false;
7630         this.td.style.display = "";
7631     },
7632     
7633     /**
7634      * Hides this button
7635      */
7636     hide: function(){
7637         this.hidden = true;
7638         this.td.style.display = "none";
7639     }
7640 });
7641
7642 // backwards compat
7643 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7644  * Based on:
7645  * Ext JS Library 1.1.1
7646  * Copyright(c) 2006-2007, Ext JS, LLC.
7647  *
7648  * Originally Released Under LGPL - original licence link has changed is not relivant.
7649  *
7650  * Fork - LGPL
7651  * <script type="text/javascript">
7652  */
7653  
7654 /**
7655  * @class Roo.PagingToolbar
7656  * @extends Roo.Toolbar
7657  * @children   Roo.Toolbar.Item Roo.form.Field
7658  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7659  * @constructor
7660  * Create a new PagingToolbar
7661  * @param {Object} config The config object
7662  */
7663 Roo.PagingToolbar = function(el, ds, config)
7664 {
7665     // old args format still supported... - xtype is prefered..
7666     if (typeof(el) == 'object' && el.xtype) {
7667         // created from xtype...
7668         config = el;
7669         ds = el.dataSource;
7670         el = config.container;
7671     }
7672     var items = [];
7673     if (config.items) {
7674         items = config.items;
7675         config.items = [];
7676     }
7677     
7678     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7679     this.ds = ds;
7680     this.cursor = 0;
7681     this.renderButtons(this.el);
7682     this.bind(ds);
7683     
7684     // supprot items array.
7685    
7686     Roo.each(items, function(e) {
7687         this.add(Roo.factory(e));
7688     },this);
7689     
7690 };
7691
7692 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7693     /**
7694      * @cfg {Roo.data.Store} dataSource
7695      * The underlying data store providing the paged data
7696      */
7697     /**
7698      * @cfg {String/HTMLElement/Element} container
7699      * container The id or element that will contain the toolbar
7700      */
7701     /**
7702      * @cfg {Boolean} displayInfo
7703      * True to display the displayMsg (defaults to false)
7704      */
7705     /**
7706      * @cfg {Number} pageSize
7707      * The number of records to display per page (defaults to 20)
7708      */
7709     pageSize: 20,
7710     /**
7711      * @cfg {String} displayMsg
7712      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7713      */
7714     displayMsg : 'Displaying {0} - {1} of {2}',
7715     /**
7716      * @cfg {String} emptyMsg
7717      * The message to display when no records are found (defaults to "No data to display")
7718      */
7719     emptyMsg : 'No data to display',
7720     /**
7721      * Customizable piece of the default paging text (defaults to "Page")
7722      * @type String
7723      */
7724     beforePageText : "Page",
7725     /**
7726      * Customizable piece of the default paging text (defaults to "of %0")
7727      * @type String
7728      */
7729     afterPageText : "of {0}",
7730     /**
7731      * Customizable piece of the default paging text (defaults to "First Page")
7732      * @type String
7733      */
7734     firstText : "First Page",
7735     /**
7736      * Customizable piece of the default paging text (defaults to "Previous Page")
7737      * @type String
7738      */
7739     prevText : "Previous Page",
7740     /**
7741      * Customizable piece of the default paging text (defaults to "Next Page")
7742      * @type String
7743      */
7744     nextText : "Next Page",
7745     /**
7746      * Customizable piece of the default paging text (defaults to "Last Page")
7747      * @type String
7748      */
7749     lastText : "Last Page",
7750     /**
7751      * Customizable piece of the default paging text (defaults to "Refresh")
7752      * @type String
7753      */
7754     refreshText : "Refresh",
7755
7756     // private
7757     renderButtons : function(el){
7758         Roo.PagingToolbar.superclass.render.call(this, el);
7759         this.first = this.addButton({
7760             tooltip: this.firstText,
7761             cls: "x-btn-icon x-grid-page-first",
7762             disabled: true,
7763             handler: this.onClick.createDelegate(this, ["first"])
7764         });
7765         this.prev = this.addButton({
7766             tooltip: this.prevText,
7767             cls: "x-btn-icon x-grid-page-prev",
7768             disabled: true,
7769             handler: this.onClick.createDelegate(this, ["prev"])
7770         });
7771         //this.addSeparator();
7772         this.add(this.beforePageText);
7773         this.field = Roo.get(this.addDom({
7774            tag: "input",
7775            type: "text",
7776            size: "3",
7777            value: "1",
7778            cls: "x-grid-page-number"
7779         }).el);
7780         this.field.on("keydown", this.onPagingKeydown, this);
7781         this.field.on("focus", function(){this.dom.select();});
7782         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7783         this.field.setHeight(18);
7784         //this.addSeparator();
7785         this.next = this.addButton({
7786             tooltip: this.nextText,
7787             cls: "x-btn-icon x-grid-page-next",
7788             disabled: true,
7789             handler: this.onClick.createDelegate(this, ["next"])
7790         });
7791         this.last = this.addButton({
7792             tooltip: this.lastText,
7793             cls: "x-btn-icon x-grid-page-last",
7794             disabled: true,
7795             handler: this.onClick.createDelegate(this, ["last"])
7796         });
7797         //this.addSeparator();
7798         this.loading = this.addButton({
7799             tooltip: this.refreshText,
7800             cls: "x-btn-icon x-grid-loading",
7801             handler: this.onClick.createDelegate(this, ["refresh"])
7802         });
7803
7804         if(this.displayInfo){
7805             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7806         }
7807     },
7808
7809     // private
7810     updateInfo : function(){
7811         if(this.displayEl){
7812             var count = this.ds.getCount();
7813             var msg = count == 0 ?
7814                 this.emptyMsg :
7815                 String.format(
7816                     this.displayMsg,
7817                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7818                 );
7819             this.displayEl.update(msg);
7820         }
7821     },
7822
7823     // private
7824     onLoad : function(ds, r, o){
7825        this.cursor = o.params ? o.params.start : 0;
7826        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7827
7828        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7829        this.field.dom.value = ap;
7830        this.first.setDisabled(ap == 1);
7831        this.prev.setDisabled(ap == 1);
7832        this.next.setDisabled(ap == ps);
7833        this.last.setDisabled(ap == ps);
7834        this.loading.enable();
7835        this.updateInfo();
7836     },
7837
7838     // private
7839     getPageData : function(){
7840         var total = this.ds.getTotalCount();
7841         return {
7842             total : total,
7843             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7844             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7845         };
7846     },
7847
7848     // private
7849     onLoadError : function(){
7850         this.loading.enable();
7851     },
7852
7853     // private
7854     onPagingKeydown : function(e){
7855         var k = e.getKey();
7856         var d = this.getPageData();
7857         if(k == e.RETURN){
7858             var v = this.field.dom.value, pageNum;
7859             if(!v || isNaN(pageNum = parseInt(v, 10))){
7860                 this.field.dom.value = d.activePage;
7861                 return;
7862             }
7863             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7864             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7865             e.stopEvent();
7866         }
7867         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))
7868         {
7869           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7870           this.field.dom.value = pageNum;
7871           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7872           e.stopEvent();
7873         }
7874         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7875         {
7876           var v = this.field.dom.value, pageNum; 
7877           var increment = (e.shiftKey) ? 10 : 1;
7878           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7879             increment *= -1;
7880           }
7881           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7882             this.field.dom.value = d.activePage;
7883             return;
7884           }
7885           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7886           {
7887             this.field.dom.value = parseInt(v, 10) + increment;
7888             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7889             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7890           }
7891           e.stopEvent();
7892         }
7893     },
7894
7895     // private
7896     beforeLoad : function(){
7897         if(this.loading){
7898             this.loading.disable();
7899         }
7900     },
7901
7902     // private
7903     onClick : function(which){
7904         var ds = this.ds;
7905         switch(which){
7906             case "first":
7907                 ds.load({params:{start: 0, limit: this.pageSize}});
7908             break;
7909             case "prev":
7910                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7911             break;
7912             case "next":
7913                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7914             break;
7915             case "last":
7916                 var total = ds.getTotalCount();
7917                 var extra = total % this.pageSize;
7918                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7919                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7920             break;
7921             case "refresh":
7922                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7923             break;
7924         }
7925     },
7926
7927     /**
7928      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7929      * @param {Roo.data.Store} store The data store to unbind
7930      */
7931     unbind : function(ds){
7932         ds.un("beforeload", this.beforeLoad, this);
7933         ds.un("load", this.onLoad, this);
7934         ds.un("loadexception", this.onLoadError, this);
7935         ds.un("remove", this.updateInfo, this);
7936         ds.un("add", this.updateInfo, this);
7937         this.ds = undefined;
7938     },
7939
7940     /**
7941      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7942      * @param {Roo.data.Store} store The data store to bind
7943      */
7944     bind : function(ds){
7945         ds.on("beforeload", this.beforeLoad, this);
7946         ds.on("load", this.onLoad, this);
7947         ds.on("loadexception", this.onLoadError, this);
7948         ds.on("remove", this.updateInfo, this);
7949         ds.on("add", this.updateInfo, this);
7950         this.ds = ds;
7951     }
7952 });/*
7953  * Based on:
7954  * Ext JS Library 1.1.1
7955  * Copyright(c) 2006-2007, Ext JS, LLC.
7956  *
7957  * Originally Released Under LGPL - original licence link has changed is not relivant.
7958  *
7959  * Fork - LGPL
7960  * <script type="text/javascript">
7961  */
7962
7963 /**
7964  * @class Roo.Resizable
7965  * @extends Roo.util.Observable
7966  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7967  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7968  * 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
7969  * the element will be wrapped for you automatically.</p>
7970  * <p>Here is the list of valid resize handles:</p>
7971  * <pre>
7972 Value   Description
7973 ------  -------------------
7974  'n'     north
7975  's'     south
7976  'e'     east
7977  'w'     west
7978  'nw'    northwest
7979  'sw'    southwest
7980  'se'    southeast
7981  'ne'    northeast
7982  'hd'    horizontal drag
7983  'all'   all
7984 </pre>
7985  * <p>Here's an example showing the creation of a typical Resizable:</p>
7986  * <pre><code>
7987 var resizer = new Roo.Resizable("element-id", {
7988     handles: 'all',
7989     minWidth: 200,
7990     minHeight: 100,
7991     maxWidth: 500,
7992     maxHeight: 400,
7993     pinned: true
7994 });
7995 resizer.on("resize", myHandler);
7996 </code></pre>
7997  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7998  * resizer.east.setDisplayed(false);</p>
7999  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8000  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8001  * resize operation's new size (defaults to [0, 0])
8002  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8003  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8004  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8005  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8006  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8007  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8008  * @cfg {Number} width The width of the element in pixels (defaults to null)
8009  * @cfg {Number} height The height of the element in pixels (defaults to null)
8010  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8011  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8012  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8013  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8014  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8015  * in favor of the handles config option (defaults to false)
8016  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8017  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8018  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8019  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8020  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8021  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8022  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8023  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8024  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8025  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8026  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8027  * @constructor
8028  * Create a new resizable component
8029  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8030  * @param {Object} config configuration options
8031   */
8032 Roo.Resizable = function(el, config)
8033 {
8034     this.el = Roo.get(el);
8035
8036     if(config && config.wrap){
8037         config.resizeChild = this.el;
8038         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8039         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8040         this.el.setStyle("overflow", "hidden");
8041         this.el.setPositioning(config.resizeChild.getPositioning());
8042         config.resizeChild.clearPositioning();
8043         if(!config.width || !config.height){
8044             var csize = config.resizeChild.getSize();
8045             this.el.setSize(csize.width, csize.height);
8046         }
8047         if(config.pinned && !config.adjustments){
8048             config.adjustments = "auto";
8049         }
8050     }
8051
8052     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8053     this.proxy.unselectable();
8054     this.proxy.enableDisplayMode('block');
8055
8056     Roo.apply(this, config);
8057
8058     if(this.pinned){
8059         this.disableTrackOver = true;
8060         this.el.addClass("x-resizable-pinned");
8061     }
8062     // if the element isn't positioned, make it relative
8063     var position = this.el.getStyle("position");
8064     if(position != "absolute" && position != "fixed"){
8065         this.el.setStyle("position", "relative");
8066     }
8067     if(!this.handles){ // no handles passed, must be legacy style
8068         this.handles = 's,e,se';
8069         if(this.multiDirectional){
8070             this.handles += ',n,w';
8071         }
8072     }
8073     if(this.handles == "all"){
8074         this.handles = "n s e w ne nw se sw";
8075     }
8076     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8077     var ps = Roo.Resizable.positions;
8078     for(var i = 0, len = hs.length; i < len; i++){
8079         if(hs[i] && ps[hs[i]]){
8080             var pos = ps[hs[i]];
8081             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8082         }
8083     }
8084     // legacy
8085     this.corner = this.southeast;
8086     
8087     // updateBox = the box can move..
8088     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8089         this.updateBox = true;
8090     }
8091
8092     this.activeHandle = null;
8093
8094     if(this.resizeChild){
8095         if(typeof this.resizeChild == "boolean"){
8096             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8097         }else{
8098             this.resizeChild = Roo.get(this.resizeChild, true);
8099         }
8100     }
8101     
8102     if(this.adjustments == "auto"){
8103         var rc = this.resizeChild;
8104         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8105         if(rc && (hw || hn)){
8106             rc.position("relative");
8107             rc.setLeft(hw ? hw.el.getWidth() : 0);
8108             rc.setTop(hn ? hn.el.getHeight() : 0);
8109         }
8110         this.adjustments = [
8111             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8112             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8113         ];
8114     }
8115
8116     if(this.draggable){
8117         this.dd = this.dynamic ?
8118             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8119         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8120     }
8121
8122     // public events
8123     this.addEvents({
8124         /**
8125          * @event beforeresize
8126          * Fired before resize is allowed. Set enabled to false to cancel resize.
8127          * @param {Roo.Resizable} this
8128          * @param {Roo.EventObject} e The mousedown event
8129          */
8130         "beforeresize" : true,
8131         /**
8132          * @event resizing
8133          * Fired a resizing.
8134          * @param {Roo.Resizable} this
8135          * @param {Number} x The new x position
8136          * @param {Number} y The new y position
8137          * @param {Number} w The new w width
8138          * @param {Number} h The new h hight
8139          * @param {Roo.EventObject} e The mouseup event
8140          */
8141         "resizing" : true,
8142         /**
8143          * @event resize
8144          * Fired after a resize.
8145          * @param {Roo.Resizable} this
8146          * @param {Number} width The new width
8147          * @param {Number} height The new height
8148          * @param {Roo.EventObject} e The mouseup event
8149          */
8150         "resize" : true
8151     });
8152
8153     if(this.width !== null && this.height !== null){
8154         this.resizeTo(this.width, this.height);
8155     }else{
8156         this.updateChildSize();
8157     }
8158     if(Roo.isIE){
8159         this.el.dom.style.zoom = 1;
8160     }
8161     Roo.Resizable.superclass.constructor.call(this);
8162 };
8163
8164 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8165         resizeChild : false,
8166         adjustments : [0, 0],
8167         minWidth : 5,
8168         minHeight : 5,
8169         maxWidth : 10000,
8170         maxHeight : 10000,
8171         enabled : true,
8172         animate : false,
8173         duration : .35,
8174         dynamic : false,
8175         handles : false,
8176         multiDirectional : false,
8177         disableTrackOver : false,
8178         easing : 'easeOutStrong',
8179         widthIncrement : 0,
8180         heightIncrement : 0,
8181         pinned : false,
8182         width : null,
8183         height : null,
8184         preserveRatio : false,
8185         transparent: false,
8186         minX: 0,
8187         minY: 0,
8188         draggable: false,
8189
8190         /**
8191          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8192          */
8193         constrainTo: undefined,
8194         /**
8195          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8196          */
8197         resizeRegion: undefined,
8198
8199
8200     /**
8201      * Perform a manual resize
8202      * @param {Number} width
8203      * @param {Number} height
8204      */
8205     resizeTo : function(width, height){
8206         this.el.setSize(width, height);
8207         this.updateChildSize();
8208         this.fireEvent("resize", this, width, height, null);
8209     },
8210
8211     // private
8212     startSizing : function(e, handle){
8213         this.fireEvent("beforeresize", this, e);
8214         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8215
8216             if(!this.overlay){
8217                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8218                 this.overlay.unselectable();
8219                 this.overlay.enableDisplayMode("block");
8220                 this.overlay.on("mousemove", this.onMouseMove, this);
8221                 this.overlay.on("mouseup", this.onMouseUp, this);
8222             }
8223             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8224
8225             this.resizing = true;
8226             this.startBox = this.el.getBox();
8227             this.startPoint = e.getXY();
8228             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8229                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8230
8231             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8232             this.overlay.show();
8233
8234             if(this.constrainTo) {
8235                 var ct = Roo.get(this.constrainTo);
8236                 this.resizeRegion = ct.getRegion().adjust(
8237                     ct.getFrameWidth('t'),
8238                     ct.getFrameWidth('l'),
8239                     -ct.getFrameWidth('b'),
8240                     -ct.getFrameWidth('r')
8241                 );
8242             }
8243
8244             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8245             this.proxy.show();
8246             this.proxy.setBox(this.startBox);
8247             if(!this.dynamic){
8248                 this.proxy.setStyle('visibility', 'visible');
8249             }
8250         }
8251     },
8252
8253     // private
8254     onMouseDown : function(handle, e){
8255         if(this.enabled){
8256             e.stopEvent();
8257             this.activeHandle = handle;
8258             this.startSizing(e, handle);
8259         }
8260     },
8261
8262     // private
8263     onMouseUp : function(e){
8264         var size = this.resizeElement();
8265         this.resizing = false;
8266         this.handleOut();
8267         this.overlay.hide();
8268         this.proxy.hide();
8269         this.fireEvent("resize", this, size.width, size.height, e);
8270     },
8271
8272     // private
8273     updateChildSize : function(){
8274         
8275         if(this.resizeChild){
8276             var el = this.el;
8277             var child = this.resizeChild;
8278             var adj = this.adjustments;
8279             if(el.dom.offsetWidth){
8280                 var b = el.getSize(true);
8281                 child.setSize(b.width+adj[0], b.height+adj[1]);
8282             }
8283             // Second call here for IE
8284             // The first call enables instant resizing and
8285             // the second call corrects scroll bars if they
8286             // exist
8287             if(Roo.isIE){
8288                 setTimeout(function(){
8289                     if(el.dom.offsetWidth){
8290                         var b = el.getSize(true);
8291                         child.setSize(b.width+adj[0], b.height+adj[1]);
8292                     }
8293                 }, 10);
8294             }
8295         }
8296     },
8297
8298     // private
8299     snap : function(value, inc, min){
8300         if(!inc || !value) {
8301             return value;
8302         }
8303         var newValue = value;
8304         var m = value % inc;
8305         if(m > 0){
8306             if(m > (inc/2)){
8307                 newValue = value + (inc-m);
8308             }else{
8309                 newValue = value - m;
8310             }
8311         }
8312         return Math.max(min, newValue);
8313     },
8314
8315     // private
8316     resizeElement : function(){
8317         var box = this.proxy.getBox();
8318         if(this.updateBox){
8319             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8320         }else{
8321             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8322         }
8323         this.updateChildSize();
8324         if(!this.dynamic){
8325             this.proxy.hide();
8326         }
8327         return box;
8328     },
8329
8330     // private
8331     constrain : function(v, diff, m, mx){
8332         if(v - diff < m){
8333             diff = v - m;
8334         }else if(v - diff > mx){
8335             diff = mx - v;
8336         }
8337         return diff;
8338     },
8339
8340     // private
8341     onMouseMove : function(e){
8342         
8343         if(this.enabled){
8344             try{// try catch so if something goes wrong the user doesn't get hung
8345
8346             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8347                 return;
8348             }
8349
8350             //var curXY = this.startPoint;
8351             var curSize = this.curSize || this.startBox;
8352             var x = this.startBox.x, y = this.startBox.y;
8353             var ox = x, oy = y;
8354             var w = curSize.width, h = curSize.height;
8355             var ow = w, oh = h;
8356             var mw = this.minWidth, mh = this.minHeight;
8357             var mxw = this.maxWidth, mxh = this.maxHeight;
8358             var wi = this.widthIncrement;
8359             var hi = this.heightIncrement;
8360
8361             var eventXY = e.getXY();
8362             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8363             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8364
8365             var pos = this.activeHandle.position;
8366
8367             switch(pos){
8368                 case "east":
8369                     w += diffX;
8370                     w = Math.min(Math.max(mw, w), mxw);
8371                     break;
8372              
8373                 case "south":
8374                     h += diffY;
8375                     h = Math.min(Math.max(mh, h), mxh);
8376                     break;
8377                 case "southeast":
8378                     w += diffX;
8379                     h += diffY;
8380                     w = Math.min(Math.max(mw, w), mxw);
8381                     h = Math.min(Math.max(mh, h), mxh);
8382                     break;
8383                 case "north":
8384                     diffY = this.constrain(h, diffY, mh, mxh);
8385                     y += diffY;
8386                     h -= diffY;
8387                     break;
8388                 case "hdrag":
8389                     
8390                     if (wi) {
8391                         var adiffX = Math.abs(diffX);
8392                         var sub = (adiffX % wi); // how much 
8393                         if (sub > (wi/2)) { // far enough to snap
8394                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8395                         } else {
8396                             // remove difference.. 
8397                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8398                         }
8399                     }
8400                     x += diffX;
8401                     x = Math.max(this.minX, x);
8402                     break;
8403                 case "west":
8404                     diffX = this.constrain(w, diffX, mw, mxw);
8405                     x += diffX;
8406                     w -= diffX;
8407                     break;
8408                 case "northeast":
8409                     w += diffX;
8410                     w = Math.min(Math.max(mw, w), mxw);
8411                     diffY = this.constrain(h, diffY, mh, mxh);
8412                     y += diffY;
8413                     h -= diffY;
8414                     break;
8415                 case "northwest":
8416                     diffX = this.constrain(w, diffX, mw, mxw);
8417                     diffY = this.constrain(h, diffY, mh, mxh);
8418                     y += diffY;
8419                     h -= diffY;
8420                     x += diffX;
8421                     w -= diffX;
8422                     break;
8423                case "southwest":
8424                     diffX = this.constrain(w, diffX, mw, mxw);
8425                     h += diffY;
8426                     h = Math.min(Math.max(mh, h), mxh);
8427                     x += diffX;
8428                     w -= diffX;
8429                     break;
8430             }
8431
8432             var sw = this.snap(w, wi, mw);
8433             var sh = this.snap(h, hi, mh);
8434             if(sw != w || sh != h){
8435                 switch(pos){
8436                     case "northeast":
8437                         y -= sh - h;
8438                     break;
8439                     case "north":
8440                         y -= sh - h;
8441                         break;
8442                     case "southwest":
8443                         x -= sw - w;
8444                     break;
8445                     case "west":
8446                         x -= sw - w;
8447                         break;
8448                     case "northwest":
8449                         x -= sw - w;
8450                         y -= sh - h;
8451                     break;
8452                 }
8453                 w = sw;
8454                 h = sh;
8455             }
8456
8457             if(this.preserveRatio){
8458                 switch(pos){
8459                     case "southeast":
8460                     case "east":
8461                         h = oh * (w/ow);
8462                         h = Math.min(Math.max(mh, h), mxh);
8463                         w = ow * (h/oh);
8464                        break;
8465                     case "south":
8466                         w = ow * (h/oh);
8467                         w = Math.min(Math.max(mw, w), mxw);
8468                         h = oh * (w/ow);
8469                         break;
8470                     case "northeast":
8471                         w = ow * (h/oh);
8472                         w = Math.min(Math.max(mw, w), mxw);
8473                         h = oh * (w/ow);
8474                     break;
8475                     case "north":
8476                         var tw = w;
8477                         w = ow * (h/oh);
8478                         w = Math.min(Math.max(mw, w), mxw);
8479                         h = oh * (w/ow);
8480                         x += (tw - w) / 2;
8481                         break;
8482                     case "southwest":
8483                         h = oh * (w/ow);
8484                         h = Math.min(Math.max(mh, h), mxh);
8485                         var tw = w;
8486                         w = ow * (h/oh);
8487                         x += tw - w;
8488                         break;
8489                     case "west":
8490                         var th = h;
8491                         h = oh * (w/ow);
8492                         h = Math.min(Math.max(mh, h), mxh);
8493                         y += (th - h) / 2;
8494                         var tw = w;
8495                         w = ow * (h/oh);
8496                         x += tw - w;
8497                        break;
8498                     case "northwest":
8499                         var tw = w;
8500                         var th = h;
8501                         h = oh * (w/ow);
8502                         h = Math.min(Math.max(mh, h), mxh);
8503                         w = ow * (h/oh);
8504                         y += th - h;
8505                         x += tw - w;
8506                        break;
8507
8508                 }
8509             }
8510             if (pos == 'hdrag') {
8511                 w = ow;
8512             }
8513             this.proxy.setBounds(x, y, w, h);
8514             if(this.dynamic){
8515                 this.resizeElement();
8516             }
8517             }catch(e){}
8518         }
8519         this.fireEvent("resizing", this, x, y, w, h, e);
8520     },
8521
8522     // private
8523     handleOver : function(){
8524         if(this.enabled){
8525             this.el.addClass("x-resizable-over");
8526         }
8527     },
8528
8529     // private
8530     handleOut : function(){
8531         if(!this.resizing){
8532             this.el.removeClass("x-resizable-over");
8533         }
8534     },
8535
8536     /**
8537      * Returns the element this component is bound to.
8538      * @return {Roo.Element}
8539      */
8540     getEl : function(){
8541         return this.el;
8542     },
8543
8544     /**
8545      * Returns the resizeChild element (or null).
8546      * @return {Roo.Element}
8547      */
8548     getResizeChild : function(){
8549         return this.resizeChild;
8550     },
8551     groupHandler : function()
8552     {
8553         
8554     },
8555     /**
8556      * Destroys this resizable. If the element was wrapped and
8557      * removeEl is not true then the element remains.
8558      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8559      */
8560     destroy : function(removeEl){
8561         this.proxy.remove();
8562         if(this.overlay){
8563             this.overlay.removeAllListeners();
8564             this.overlay.remove();
8565         }
8566         var ps = Roo.Resizable.positions;
8567         for(var k in ps){
8568             if(typeof ps[k] != "function" && this[ps[k]]){
8569                 var h = this[ps[k]];
8570                 h.el.removeAllListeners();
8571                 h.el.remove();
8572             }
8573         }
8574         if(removeEl){
8575             this.el.update("");
8576             this.el.remove();
8577         }
8578     }
8579 });
8580
8581 // private
8582 // hash to map config positions to true positions
8583 Roo.Resizable.positions = {
8584     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8585     hd: "hdrag"
8586 };
8587
8588 // private
8589 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8590     if(!this.tpl){
8591         // only initialize the template if resizable is used
8592         var tpl = Roo.DomHelper.createTemplate(
8593             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8594         );
8595         tpl.compile();
8596         Roo.Resizable.Handle.prototype.tpl = tpl;
8597     }
8598     this.position = pos;
8599     this.rz = rz;
8600     // show north drag fro topdra
8601     var handlepos = pos == 'hdrag' ? 'north' : pos;
8602     
8603     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8604     if (pos == 'hdrag') {
8605         this.el.setStyle('cursor', 'pointer');
8606     }
8607     this.el.unselectable();
8608     if(transparent){
8609         this.el.setOpacity(0);
8610     }
8611     this.el.on("mousedown", this.onMouseDown, this);
8612     if(!disableTrackOver){
8613         this.el.on("mouseover", this.onMouseOver, this);
8614         this.el.on("mouseout", this.onMouseOut, this);
8615     }
8616 };
8617
8618 // private
8619 Roo.Resizable.Handle.prototype = {
8620     afterResize : function(rz){
8621         Roo.log('after?');
8622         // do nothing
8623     },
8624     // private
8625     onMouseDown : function(e){
8626         this.rz.onMouseDown(this, e);
8627     },
8628     // private
8629     onMouseOver : function(e){
8630         this.rz.handleOver(this, e);
8631     },
8632     // private
8633     onMouseOut : function(e){
8634         this.rz.handleOut(this, e);
8635     }
8636 };/*
8637  * Based on:
8638  * Ext JS Library 1.1.1
8639  * Copyright(c) 2006-2007, Ext JS, LLC.
8640  *
8641  * Originally Released Under LGPL - original licence link has changed is not relivant.
8642  *
8643  * Fork - LGPL
8644  * <script type="text/javascript">
8645  */
8646
8647 /**
8648  * @class Roo.Editor
8649  * @extends Roo.Component
8650  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8651  * @constructor
8652  * Create a new Editor
8653  * @param {Roo.form.Field} field The Field object (or descendant)
8654  * @param {Object} config The config object
8655  */
8656 Roo.Editor = function(field, config){
8657     Roo.Editor.superclass.constructor.call(this, config);
8658     this.field = field;
8659     this.addEvents({
8660         /**
8661              * @event beforestartedit
8662              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8663              * false from the handler of this event.
8664              * @param {Editor} this
8665              * @param {Roo.Element} boundEl The underlying element bound to this editor
8666              * @param {Mixed} value The field value being set
8667              */
8668         "beforestartedit" : true,
8669         /**
8670              * @event startedit
8671              * Fires when this editor is displayed
8672              * @param {Roo.Element} boundEl The underlying element bound to this editor
8673              * @param {Mixed} value The starting field value
8674              */
8675         "startedit" : true,
8676         /**
8677              * @event beforecomplete
8678              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8679              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8680              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8681              * event will not fire since no edit actually occurred.
8682              * @param {Editor} this
8683              * @param {Mixed} value The current field value
8684              * @param {Mixed} startValue The original field value
8685              */
8686         "beforecomplete" : true,
8687         /**
8688              * @event complete
8689              * Fires after editing is complete and any changed value has been written to the underlying field.
8690              * @param {Editor} this
8691              * @param {Mixed} value The current field value
8692              * @param {Mixed} startValue The original field value
8693              */
8694         "complete" : true,
8695         /**
8696          * @event specialkey
8697          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8698          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8699          * @param {Roo.form.Field} this
8700          * @param {Roo.EventObject} e The event object
8701          */
8702         "specialkey" : true
8703     });
8704 };
8705
8706 Roo.extend(Roo.Editor, Roo.Component, {
8707     /**
8708      * @cfg {Boolean/String} autosize
8709      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8710      * or "height" to adopt the height only (defaults to false)
8711      */
8712     /**
8713      * @cfg {Boolean} revertInvalid
8714      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8715      * validation fails (defaults to true)
8716      */
8717     /**
8718      * @cfg {Boolean} ignoreNoChange
8719      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8720      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8721      * will never be ignored.
8722      */
8723     /**
8724      * @cfg {Boolean} hideEl
8725      * False to keep the bound element visible while the editor is displayed (defaults to true)
8726      */
8727     /**
8728      * @cfg {Mixed} value
8729      * The data value of the underlying field (defaults to "")
8730      */
8731     value : "",
8732     /**
8733      * @cfg {String} alignment
8734      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8735      */
8736     alignment: "c-c?",
8737     /**
8738      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8739      * for bottom-right shadow (defaults to "frame")
8740      */
8741     shadow : "frame",
8742     /**
8743      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8744      */
8745     constrain : false,
8746     /**
8747      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8748      */
8749     completeOnEnter : false,
8750     /**
8751      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8752      */
8753     cancelOnEsc : false,
8754     /**
8755      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8756      */
8757     updateEl : false,
8758
8759     // private
8760     onRender : function(ct, position){
8761         this.el = new Roo.Layer({
8762             shadow: this.shadow,
8763             cls: "x-editor",
8764             parentEl : ct,
8765             shim : this.shim,
8766             shadowOffset:4,
8767             id: this.id,
8768             constrain: this.constrain
8769         });
8770         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8771         if(this.field.msgTarget != 'title'){
8772             this.field.msgTarget = 'qtip';
8773         }
8774         this.field.render(this.el);
8775         if(Roo.isGecko){
8776             this.field.el.dom.setAttribute('autocomplete', 'off');
8777         }
8778         this.field.on("specialkey", this.onSpecialKey, this);
8779         if(this.swallowKeys){
8780             this.field.el.swallowEvent(['keydown','keypress']);
8781         }
8782         this.field.show();
8783         this.field.on("blur", this.onBlur, this);
8784         if(this.field.grow){
8785             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8786         }
8787     },
8788
8789     onSpecialKey : function(field, e)
8790     {
8791         //Roo.log('editor onSpecialKey');
8792         if(this.completeOnEnter && e.getKey() == e.ENTER){
8793             e.stopEvent();
8794             this.completeEdit();
8795             return;
8796         }
8797         // do not fire special key otherwise it might hide close the editor...
8798         if(e.getKey() == e.ENTER){    
8799             return;
8800         }
8801         if(this.cancelOnEsc && e.getKey() == e.ESC){
8802             this.cancelEdit();
8803             return;
8804         } 
8805         this.fireEvent('specialkey', field, e);
8806     
8807     },
8808
8809     /**
8810      * Starts the editing process and shows the editor.
8811      * @param {String/HTMLElement/Element} el The element to edit
8812      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8813       * to the innerHTML of el.
8814      */
8815     startEdit : function(el, value){
8816         if(this.editing){
8817             this.completeEdit();
8818         }
8819         this.boundEl = Roo.get(el);
8820         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8821         if(!this.rendered){
8822             this.render(this.parentEl || document.body);
8823         }
8824         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8825             return;
8826         }
8827         this.startValue = v;
8828         this.field.setValue(v);
8829         if(this.autoSize){
8830             var sz = this.boundEl.getSize();
8831             switch(this.autoSize){
8832                 case "width":
8833                 this.setSize(sz.width,  "");
8834                 break;
8835                 case "height":
8836                 this.setSize("",  sz.height);
8837                 break;
8838                 default:
8839                 this.setSize(sz.width,  sz.height);
8840             }
8841         }
8842         this.el.alignTo(this.boundEl, this.alignment);
8843         this.editing = true;
8844         if(Roo.QuickTips){
8845             Roo.QuickTips.disable();
8846         }
8847         this.show();
8848     },
8849
8850     /**
8851      * Sets the height and width of this editor.
8852      * @param {Number} width The new width
8853      * @param {Number} height The new height
8854      */
8855     setSize : function(w, h){
8856         this.field.setSize(w, h);
8857         if(this.el){
8858             this.el.sync();
8859         }
8860     },
8861
8862     /**
8863      * Realigns the editor to the bound field based on the current alignment config value.
8864      */
8865     realign : function(){
8866         this.el.alignTo(this.boundEl, this.alignment);
8867     },
8868
8869     /**
8870      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8871      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8872      */
8873     completeEdit : function(remainVisible){
8874         if(!this.editing){
8875             return;
8876         }
8877         var v = this.getValue();
8878         if(this.revertInvalid !== false && !this.field.isValid()){
8879             v = this.startValue;
8880             this.cancelEdit(true);
8881         }
8882         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8883             this.editing = false;
8884             this.hide();
8885             return;
8886         }
8887         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8888             this.editing = false;
8889             if(this.updateEl && this.boundEl){
8890                 this.boundEl.update(v);
8891             }
8892             if(remainVisible !== true){
8893                 this.hide();
8894             }
8895             this.fireEvent("complete", this, v, this.startValue);
8896         }
8897     },
8898
8899     // private
8900     onShow : function(){
8901         this.el.show();
8902         if(this.hideEl !== false){
8903             this.boundEl.hide();
8904         }
8905         this.field.show();
8906         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8907             this.fixIEFocus = true;
8908             this.deferredFocus.defer(50, this);
8909         }else{
8910             this.field.focus();
8911         }
8912         this.fireEvent("startedit", this.boundEl, this.startValue);
8913     },
8914
8915     deferredFocus : function(){
8916         if(this.editing){
8917             this.field.focus();
8918         }
8919     },
8920
8921     /**
8922      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8923      * reverted to the original starting value.
8924      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8925      * cancel (defaults to false)
8926      */
8927     cancelEdit : function(remainVisible){
8928         if(this.editing){
8929             this.setValue(this.startValue);
8930             if(remainVisible !== true){
8931                 this.hide();
8932             }
8933         }
8934     },
8935
8936     // private
8937     onBlur : function(){
8938         if(this.allowBlur !== true && this.editing){
8939             this.completeEdit();
8940         }
8941     },
8942
8943     // private
8944     onHide : function(){
8945         if(this.editing){
8946             this.completeEdit();
8947             return;
8948         }
8949         this.field.blur();
8950         if(this.field.collapse){
8951             this.field.collapse();
8952         }
8953         this.el.hide();
8954         if(this.hideEl !== false){
8955             this.boundEl.show();
8956         }
8957         if(Roo.QuickTips){
8958             Roo.QuickTips.enable();
8959         }
8960     },
8961
8962     /**
8963      * Sets the data value of the editor
8964      * @param {Mixed} value Any valid value supported by the underlying field
8965      */
8966     setValue : function(v){
8967         this.field.setValue(v);
8968     },
8969
8970     /**
8971      * Gets the data value of the editor
8972      * @return {Mixed} The data value
8973      */
8974     getValue : function(){
8975         return this.field.getValue();
8976     }
8977 });/*
8978  * Based on:
8979  * Ext JS Library 1.1.1
8980  * Copyright(c) 2006-2007, Ext JS, LLC.
8981  *
8982  * Originally Released Under LGPL - original licence link has changed is not relivant.
8983  *
8984  * Fork - LGPL
8985  * <script type="text/javascript">
8986  */
8987  
8988 /**
8989  * @class Roo.BasicDialog
8990  * @extends Roo.util.Observable
8991  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8992  * <pre><code>
8993 var dlg = new Roo.BasicDialog("my-dlg", {
8994     height: 200,
8995     width: 300,
8996     minHeight: 100,
8997     minWidth: 150,
8998     modal: true,
8999     proxyDrag: true,
9000     shadow: true
9001 });
9002 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9003 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9004 dlg.addButton('Cancel', dlg.hide, dlg);
9005 dlg.show();
9006 </code></pre>
9007   <b>A Dialog should always be a direct child of the body element.</b>
9008  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9009  * @cfg {String} title Default text to display in the title bar (defaults to null)
9010  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9011  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9012  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9013  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9014  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9015  * (defaults to null with no animation)
9016  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9017  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9018  * property for valid values (defaults to 'all')
9019  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9020  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9021  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9022  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9023  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9024  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9025  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9026  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9027  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9028  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9029  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9030  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9031  * draggable = true (defaults to false)
9032  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9033  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9034  * shadow (defaults to false)
9035  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9036  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9037  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9038  * @cfg {Array} buttons Array of buttons
9039  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9040  * @constructor
9041  * Create a new BasicDialog.
9042  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9043  * @param {Object} config Configuration options
9044  */
9045 Roo.BasicDialog = function(el, config){
9046     this.el = Roo.get(el);
9047     var dh = Roo.DomHelper;
9048     if(!this.el && config && config.autoCreate){
9049         if(typeof config.autoCreate == "object"){
9050             if(!config.autoCreate.id){
9051                 config.autoCreate.id = el;
9052             }
9053             this.el = dh.append(document.body,
9054                         config.autoCreate, true);
9055         }else{
9056             this.el = dh.append(document.body,
9057                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9058         }
9059     }
9060     el = this.el;
9061     el.setDisplayed(true);
9062     el.hide = this.hideAction;
9063     this.id = el.id;
9064     el.addClass("x-dlg");
9065
9066     Roo.apply(this, config);
9067
9068     this.proxy = el.createProxy("x-dlg-proxy");
9069     this.proxy.hide = this.hideAction;
9070     this.proxy.setOpacity(.5);
9071     this.proxy.hide();
9072
9073     if(config.width){
9074         el.setWidth(config.width);
9075     }
9076     if(config.height){
9077         el.setHeight(config.height);
9078     }
9079     this.size = el.getSize();
9080     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9081         this.xy = [config.x,config.y];
9082     }else{
9083         this.xy = el.getCenterXY(true);
9084     }
9085     /** The header element @type Roo.Element */
9086     this.header = el.child("> .x-dlg-hd");
9087     /** The body element @type Roo.Element */
9088     this.body = el.child("> .x-dlg-bd");
9089     /** The footer element @type Roo.Element */
9090     this.footer = el.child("> .x-dlg-ft");
9091
9092     if(!this.header){
9093         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9094     }
9095     if(!this.body){
9096         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9097     }
9098
9099     this.header.unselectable();
9100     if(this.title){
9101         this.header.update(this.title);
9102     }
9103     // this element allows the dialog to be focused for keyboard event
9104     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9105     this.focusEl.swallowEvent("click", true);
9106
9107     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9108
9109     // wrap the body and footer for special rendering
9110     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9111     if(this.footer){
9112         this.bwrap.dom.appendChild(this.footer.dom);
9113     }
9114
9115     this.bg = this.el.createChild({
9116         tag: "div", cls:"x-dlg-bg",
9117         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9118     });
9119     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9120
9121
9122     if(this.autoScroll !== false && !this.autoTabs){
9123         this.body.setStyle("overflow", "auto");
9124     }
9125
9126     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9127
9128     if(this.closable !== false){
9129         this.el.addClass("x-dlg-closable");
9130         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9131         this.close.on("click", this.closeClick, this);
9132         this.close.addClassOnOver("x-dlg-close-over");
9133     }
9134     if(this.collapsible !== false){
9135         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9136         this.collapseBtn.on("click", this.collapseClick, this);
9137         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9138         this.header.on("dblclick", this.collapseClick, this);
9139     }
9140     if(this.resizable !== false){
9141         this.el.addClass("x-dlg-resizable");
9142         this.resizer = new Roo.Resizable(el, {
9143             minWidth: this.minWidth || 80,
9144             minHeight:this.minHeight || 80,
9145             handles: this.resizeHandles || "all",
9146             pinned: true
9147         });
9148         this.resizer.on("beforeresize", this.beforeResize, this);
9149         this.resizer.on("resize", this.onResize, this);
9150     }
9151     if(this.draggable !== false){
9152         el.addClass("x-dlg-draggable");
9153         if (!this.proxyDrag) {
9154             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9155         }
9156         else {
9157             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9158         }
9159         dd.setHandleElId(this.header.id);
9160         dd.endDrag = this.endMove.createDelegate(this);
9161         dd.startDrag = this.startMove.createDelegate(this);
9162         dd.onDrag = this.onDrag.createDelegate(this);
9163         dd.scroll = false;
9164         this.dd = dd;
9165     }
9166     if(this.modal){
9167         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9168         this.mask.enableDisplayMode("block");
9169         this.mask.hide();
9170         this.el.addClass("x-dlg-modal");
9171     }
9172     if(this.shadow){
9173         this.shadow = new Roo.Shadow({
9174             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9175             offset : this.shadowOffset
9176         });
9177     }else{
9178         this.shadowOffset = 0;
9179     }
9180     if(Roo.useShims && this.shim !== false){
9181         this.shim = this.el.createShim();
9182         this.shim.hide = this.hideAction;
9183         this.shim.hide();
9184     }else{
9185         this.shim = false;
9186     }
9187     if(this.autoTabs){
9188         this.initTabs();
9189     }
9190     if (this.buttons) { 
9191         var bts= this.buttons;
9192         this.buttons = [];
9193         Roo.each(bts, function(b) {
9194             this.addButton(b);
9195         }, this);
9196     }
9197     
9198     
9199     this.addEvents({
9200         /**
9201          * @event keydown
9202          * Fires when a key is pressed
9203          * @param {Roo.BasicDialog} this
9204          * @param {Roo.EventObject} e
9205          */
9206         "keydown" : true,
9207         /**
9208          * @event move
9209          * Fires when this dialog is moved by the user.
9210          * @param {Roo.BasicDialog} this
9211          * @param {Number} x The new page X
9212          * @param {Number} y The new page Y
9213          */
9214         "move" : true,
9215         /**
9216          * @event resize
9217          * Fires when this dialog is resized by the user.
9218          * @param {Roo.BasicDialog} this
9219          * @param {Number} width The new width
9220          * @param {Number} height The new height
9221          */
9222         "resize" : true,
9223         /**
9224          * @event beforehide
9225          * Fires before this dialog is hidden.
9226          * @param {Roo.BasicDialog} this
9227          */
9228         "beforehide" : true,
9229         /**
9230          * @event hide
9231          * Fires when this dialog is hidden.
9232          * @param {Roo.BasicDialog} this
9233          */
9234         "hide" : true,
9235         /**
9236          * @event beforeshow
9237          * Fires before this dialog is shown.
9238          * @param {Roo.BasicDialog} this
9239          */
9240         "beforeshow" : true,
9241         /**
9242          * @event show
9243          * Fires when this dialog is shown.
9244          * @param {Roo.BasicDialog} this
9245          */
9246         "show" : true
9247     });
9248     el.on("keydown", this.onKeyDown, this);
9249     el.on("mousedown", this.toFront, this);
9250     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9251     this.el.hide();
9252     Roo.DialogManager.register(this);
9253     Roo.BasicDialog.superclass.constructor.call(this);
9254 };
9255
9256 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9257     shadowOffset: Roo.isIE ? 6 : 5,
9258     minHeight: 80,
9259     minWidth: 200,
9260     minButtonWidth: 75,
9261     defaultButton: null,
9262     buttonAlign: "right",
9263     tabTag: 'div',
9264     firstShow: true,
9265
9266     /**
9267      * Sets the dialog title text
9268      * @param {String} text The title text to display
9269      * @return {Roo.BasicDialog} this
9270      */
9271     setTitle : function(text){
9272         this.header.update(text);
9273         return this;
9274     },
9275
9276     // private
9277     closeClick : function(){
9278         this.hide();
9279     },
9280
9281     // private
9282     collapseClick : function(){
9283         this[this.collapsed ? "expand" : "collapse"]();
9284     },
9285
9286     /**
9287      * Collapses the dialog to its minimized state (only the title bar is visible).
9288      * Equivalent to the user clicking the collapse dialog button.
9289      */
9290     collapse : function(){
9291         if(!this.collapsed){
9292             this.collapsed = true;
9293             this.el.addClass("x-dlg-collapsed");
9294             this.restoreHeight = this.el.getHeight();
9295             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9296         }
9297     },
9298
9299     /**
9300      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9301      * clicking the expand dialog button.
9302      */
9303     expand : function(){
9304         if(this.collapsed){
9305             this.collapsed = false;
9306             this.el.removeClass("x-dlg-collapsed");
9307             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9308         }
9309     },
9310
9311     /**
9312      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9313      * @return {Roo.TabPanel} The tabs component
9314      */
9315     initTabs : function(){
9316         var tabs = this.getTabs();
9317         while(tabs.getTab(0)){
9318             tabs.removeTab(0);
9319         }
9320         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9321             var dom = el.dom;
9322             tabs.addTab(Roo.id(dom), dom.title);
9323             dom.title = "";
9324         });
9325         tabs.activate(0);
9326         return tabs;
9327     },
9328
9329     // private
9330     beforeResize : function(){
9331         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9332     },
9333
9334     // private
9335     onResize : function(){
9336         this.refreshSize();
9337         this.syncBodyHeight();
9338         this.adjustAssets();
9339         this.focus();
9340         this.fireEvent("resize", this, this.size.width, this.size.height);
9341     },
9342
9343     // private
9344     onKeyDown : function(e){
9345         if(this.isVisible()){
9346             this.fireEvent("keydown", this, e);
9347         }
9348     },
9349
9350     /**
9351      * Resizes the dialog.
9352      * @param {Number} width
9353      * @param {Number} height
9354      * @return {Roo.BasicDialog} this
9355      */
9356     resizeTo : function(width, height){
9357         this.el.setSize(width, height);
9358         this.size = {width: width, height: height};
9359         this.syncBodyHeight();
9360         if(this.fixedcenter){
9361             this.center();
9362         }
9363         if(this.isVisible()){
9364             this.constrainXY();
9365             this.adjustAssets();
9366         }
9367         this.fireEvent("resize", this, width, height);
9368         return this;
9369     },
9370
9371
9372     /**
9373      * Resizes the dialog to fit the specified content size.
9374      * @param {Number} width
9375      * @param {Number} height
9376      * @return {Roo.BasicDialog} this
9377      */
9378     setContentSize : function(w, h){
9379         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9380         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9381         //if(!this.el.isBorderBox()){
9382             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9383             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9384         //}
9385         if(this.tabs){
9386             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9387             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9388         }
9389         this.resizeTo(w, h);
9390         return this;
9391     },
9392
9393     /**
9394      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9395      * executed in response to a particular key being pressed while the dialog is active.
9396      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9397      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9398      * @param {Function} fn The function to call
9399      * @param {Object} scope (optional) The scope of the function
9400      * @return {Roo.BasicDialog} this
9401      */
9402     addKeyListener : function(key, fn, scope){
9403         var keyCode, shift, ctrl, alt;
9404         if(typeof key == "object" && !(key instanceof Array)){
9405             keyCode = key["key"];
9406             shift = key["shift"];
9407             ctrl = key["ctrl"];
9408             alt = key["alt"];
9409         }else{
9410             keyCode = key;
9411         }
9412         var handler = function(dlg, e){
9413             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9414                 var k = e.getKey();
9415                 if(keyCode instanceof Array){
9416                     for(var i = 0, len = keyCode.length; i < len; i++){
9417                         if(keyCode[i] == k){
9418                           fn.call(scope || window, dlg, k, e);
9419                           return;
9420                         }
9421                     }
9422                 }else{
9423                     if(k == keyCode){
9424                         fn.call(scope || window, dlg, k, e);
9425                     }
9426                 }
9427             }
9428         };
9429         this.on("keydown", handler);
9430         return this;
9431     },
9432
9433     /**
9434      * Returns the TabPanel component (creates it if it doesn't exist).
9435      * Note: If you wish to simply check for the existence of tabs without creating them,
9436      * check for a null 'tabs' property.
9437      * @return {Roo.TabPanel} The tabs component
9438      */
9439     getTabs : function(){
9440         if(!this.tabs){
9441             this.el.addClass("x-dlg-auto-tabs");
9442             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9443             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9444         }
9445         return this.tabs;
9446     },
9447
9448     /**
9449      * Adds a button to the footer section of the dialog.
9450      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9451      * object or a valid Roo.DomHelper element config
9452      * @param {Function} handler The function called when the button is clicked
9453      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9454      * @return {Roo.Button} The new button
9455      */
9456     addButton : function(config, handler, scope){
9457         var dh = Roo.DomHelper;
9458         if(!this.footer){
9459             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9460         }
9461         if(!this.btnContainer){
9462             var tb = this.footer.createChild({
9463
9464                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9465                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9466             }, null, true);
9467             this.btnContainer = tb.firstChild.firstChild.firstChild;
9468         }
9469         var bconfig = {
9470             handler: handler,
9471             scope: scope,
9472             minWidth: this.minButtonWidth,
9473             hideParent:true
9474         };
9475         if(typeof config == "string"){
9476             bconfig.text = config;
9477         }else{
9478             if(config.tag){
9479                 bconfig.dhconfig = config;
9480             }else{
9481                 Roo.apply(bconfig, config);
9482             }
9483         }
9484         var fc = false;
9485         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9486             bconfig.position = Math.max(0, bconfig.position);
9487             fc = this.btnContainer.childNodes[bconfig.position];
9488         }
9489          
9490         var btn = new Roo.Button(
9491             fc ? 
9492                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9493                 : this.btnContainer.appendChild(document.createElement("td")),
9494             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9495             bconfig
9496         );
9497         this.syncBodyHeight();
9498         if(!this.buttons){
9499             /**
9500              * Array of all the buttons that have been added to this dialog via addButton
9501              * @type Array
9502              */
9503             this.buttons = [];
9504         }
9505         this.buttons.push(btn);
9506         return btn;
9507     },
9508
9509     /**
9510      * Sets the default button to be focused when the dialog is displayed.
9511      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9512      * @return {Roo.BasicDialog} this
9513      */
9514     setDefaultButton : function(btn){
9515         this.defaultButton = btn;
9516         return this;
9517     },
9518
9519     // private
9520     getHeaderFooterHeight : function(safe){
9521         var height = 0;
9522         if(this.header){
9523            height += this.header.getHeight();
9524         }
9525         if(this.footer){
9526            var fm = this.footer.getMargins();
9527             height += (this.footer.getHeight()+fm.top+fm.bottom);
9528         }
9529         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9530         height += this.centerBg.getPadding("tb");
9531         return height;
9532     },
9533
9534     // private
9535     syncBodyHeight : function()
9536     {
9537         var bd = this.body, // the text
9538             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9539             bw = this.bwrap;
9540         var height = this.size.height - this.getHeaderFooterHeight(false);
9541         bd.setHeight(height-bd.getMargins("tb"));
9542         var hh = this.header.getHeight();
9543         var h = this.size.height-hh;
9544         cb.setHeight(h);
9545         
9546         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9547         bw.setHeight(h-cb.getPadding("tb"));
9548         
9549         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9550         bd.setWidth(bw.getWidth(true));
9551         if(this.tabs){
9552             this.tabs.syncHeight();
9553             if(Roo.isIE){
9554                 this.tabs.el.repaint();
9555             }
9556         }
9557     },
9558
9559     /**
9560      * Restores the previous state of the dialog if Roo.state is configured.
9561      * @return {Roo.BasicDialog} this
9562      */
9563     restoreState : function(){
9564         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9565         if(box && box.width){
9566             this.xy = [box.x, box.y];
9567             this.resizeTo(box.width, box.height);
9568         }
9569         return this;
9570     },
9571
9572     // private
9573     beforeShow : function(){
9574         this.expand();
9575         if(this.fixedcenter){
9576             this.xy = this.el.getCenterXY(true);
9577         }
9578         if(this.modal){
9579             Roo.get(document.body).addClass("x-body-masked");
9580             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9581             this.mask.show();
9582         }
9583         this.constrainXY();
9584     },
9585
9586     // private
9587     animShow : function(){
9588         var b = Roo.get(this.animateTarget).getBox();
9589         this.proxy.setSize(b.width, b.height);
9590         this.proxy.setLocation(b.x, b.y);
9591         this.proxy.show();
9592         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9593                     true, .35, this.showEl.createDelegate(this));
9594     },
9595
9596     /**
9597      * Shows the dialog.
9598      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9599      * @return {Roo.BasicDialog} this
9600      */
9601     show : function(animateTarget){
9602         if (this.fireEvent("beforeshow", this) === false){
9603             return;
9604         }
9605         if(this.syncHeightBeforeShow){
9606             this.syncBodyHeight();
9607         }else if(this.firstShow){
9608             this.firstShow = false;
9609             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9610         }
9611         this.animateTarget = animateTarget || this.animateTarget;
9612         if(!this.el.isVisible()){
9613             this.beforeShow();
9614             if(this.animateTarget && Roo.get(this.animateTarget)){
9615                 this.animShow();
9616             }else{
9617                 this.showEl();
9618             }
9619         }
9620         return this;
9621     },
9622
9623     // private
9624     showEl : function(){
9625         this.proxy.hide();
9626         this.el.setXY(this.xy);
9627         this.el.show();
9628         this.adjustAssets(true);
9629         this.toFront();
9630         this.focus();
9631         // IE peekaboo bug - fix found by Dave Fenwick
9632         if(Roo.isIE){
9633             this.el.repaint();
9634         }
9635         this.fireEvent("show", this);
9636     },
9637
9638     /**
9639      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9640      * dialog itself will receive focus.
9641      */
9642     focus : function(){
9643         if(this.defaultButton){
9644             this.defaultButton.focus();
9645         }else{
9646             this.focusEl.focus();
9647         }
9648     },
9649
9650     // private
9651     constrainXY : function(){
9652         if(this.constraintoviewport !== false){
9653             if(!this.viewSize){
9654                 if(this.container){
9655                     var s = this.container.getSize();
9656                     this.viewSize = [s.width, s.height];
9657                 }else{
9658                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9659                 }
9660             }
9661             var s = Roo.get(this.container||document).getScroll();
9662
9663             var x = this.xy[0], y = this.xy[1];
9664             var w = this.size.width, h = this.size.height;
9665             var vw = this.viewSize[0], vh = this.viewSize[1];
9666             // only move it if it needs it
9667             var moved = false;
9668             // first validate right/bottom
9669             if(x + w > vw+s.left){
9670                 x = vw - w;
9671                 moved = true;
9672             }
9673             if(y + h > vh+s.top){
9674                 y = vh - h;
9675                 moved = true;
9676             }
9677             // then make sure top/left isn't negative
9678             if(x < s.left){
9679                 x = s.left;
9680                 moved = true;
9681             }
9682             if(y < s.top){
9683                 y = s.top;
9684                 moved = true;
9685             }
9686             if(moved){
9687                 // cache xy
9688                 this.xy = [x, y];
9689                 if(this.isVisible()){
9690                     this.el.setLocation(x, y);
9691                     this.adjustAssets();
9692                 }
9693             }
9694         }
9695     },
9696
9697     // private
9698     onDrag : function(){
9699         if(!this.proxyDrag){
9700             this.xy = this.el.getXY();
9701             this.adjustAssets();
9702         }
9703     },
9704
9705     // private
9706     adjustAssets : function(doShow){
9707         var x = this.xy[0], y = this.xy[1];
9708         var w = this.size.width, h = this.size.height;
9709         if(doShow === true){
9710             if(this.shadow){
9711                 this.shadow.show(this.el);
9712             }
9713             if(this.shim){
9714                 this.shim.show();
9715             }
9716         }
9717         if(this.shadow && this.shadow.isVisible()){
9718             this.shadow.show(this.el);
9719         }
9720         if(this.shim && this.shim.isVisible()){
9721             this.shim.setBounds(x, y, w, h);
9722         }
9723     },
9724
9725     // private
9726     adjustViewport : function(w, h){
9727         if(!w || !h){
9728             w = Roo.lib.Dom.getViewWidth();
9729             h = Roo.lib.Dom.getViewHeight();
9730         }
9731         // cache the size
9732         this.viewSize = [w, h];
9733         if(this.modal && this.mask.isVisible()){
9734             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9735             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9736         }
9737         if(this.isVisible()){
9738             this.constrainXY();
9739         }
9740     },
9741
9742     /**
9743      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9744      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9745      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9746      */
9747     destroy : function(removeEl){
9748         if(this.isVisible()){
9749             this.animateTarget = null;
9750             this.hide();
9751         }
9752         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9753         if(this.tabs){
9754             this.tabs.destroy(removeEl);
9755         }
9756         Roo.destroy(
9757              this.shim,
9758              this.proxy,
9759              this.resizer,
9760              this.close,
9761              this.mask
9762         );
9763         if(this.dd){
9764             this.dd.unreg();
9765         }
9766         if(this.buttons){
9767            for(var i = 0, len = this.buttons.length; i < len; i++){
9768                this.buttons[i].destroy();
9769            }
9770         }
9771         this.el.removeAllListeners();
9772         if(removeEl === true){
9773             this.el.update("");
9774             this.el.remove();
9775         }
9776         Roo.DialogManager.unregister(this);
9777     },
9778
9779     // private
9780     startMove : function(){
9781         if(this.proxyDrag){
9782             this.proxy.show();
9783         }
9784         if(this.constraintoviewport !== false){
9785             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9786         }
9787     },
9788
9789     // private
9790     endMove : function(){
9791         if(!this.proxyDrag){
9792             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9793         }else{
9794             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9795             this.proxy.hide();
9796         }
9797         this.refreshSize();
9798         this.adjustAssets();
9799         this.focus();
9800         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9801     },
9802
9803     /**
9804      * Brings this dialog to the front of any other visible dialogs
9805      * @return {Roo.BasicDialog} this
9806      */
9807     toFront : function(){
9808         Roo.DialogManager.bringToFront(this);
9809         return this;
9810     },
9811
9812     /**
9813      * Sends this dialog to the back (under) of any other visible dialogs
9814      * @return {Roo.BasicDialog} this
9815      */
9816     toBack : function(){
9817         Roo.DialogManager.sendToBack(this);
9818         return this;
9819     },
9820
9821     /**
9822      * Centers this dialog in the viewport
9823      * @return {Roo.BasicDialog} this
9824      */
9825     center : function(){
9826         var xy = this.el.getCenterXY(true);
9827         this.moveTo(xy[0], xy[1]);
9828         return this;
9829     },
9830
9831     /**
9832      * Moves the dialog's top-left corner to the specified point
9833      * @param {Number} x
9834      * @param {Number} y
9835      * @return {Roo.BasicDialog} this
9836      */
9837     moveTo : function(x, y){
9838         this.xy = [x,y];
9839         if(this.isVisible()){
9840             this.el.setXY(this.xy);
9841             this.adjustAssets();
9842         }
9843         return this;
9844     },
9845
9846     /**
9847      * Aligns the dialog to the specified element
9848      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9849      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9850      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9851      * @return {Roo.BasicDialog} this
9852      */
9853     alignTo : function(element, position, offsets){
9854         this.xy = this.el.getAlignToXY(element, position, offsets);
9855         if(this.isVisible()){
9856             this.el.setXY(this.xy);
9857             this.adjustAssets();
9858         }
9859         return this;
9860     },
9861
9862     /**
9863      * Anchors an element to another element and realigns it when the window is resized.
9864      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9865      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9866      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9867      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9868      * is a number, it is used as the buffer delay (defaults to 50ms).
9869      * @return {Roo.BasicDialog} this
9870      */
9871     anchorTo : function(el, alignment, offsets, monitorScroll){
9872         var action = function(){
9873             this.alignTo(el, alignment, offsets);
9874         };
9875         Roo.EventManager.onWindowResize(action, this);
9876         var tm = typeof monitorScroll;
9877         if(tm != 'undefined'){
9878             Roo.EventManager.on(window, 'scroll', action, this,
9879                 {buffer: tm == 'number' ? monitorScroll : 50});
9880         }
9881         action.call(this);
9882         return this;
9883     },
9884
9885     /**
9886      * Returns true if the dialog is visible
9887      * @return {Boolean}
9888      */
9889     isVisible : function(){
9890         return this.el.isVisible();
9891     },
9892
9893     // private
9894     animHide : function(callback){
9895         var b = Roo.get(this.animateTarget).getBox();
9896         this.proxy.show();
9897         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9898         this.el.hide();
9899         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9900                     this.hideEl.createDelegate(this, [callback]));
9901     },
9902
9903     /**
9904      * Hides the dialog.
9905      * @param {Function} callback (optional) Function to call when the dialog is hidden
9906      * @return {Roo.BasicDialog} this
9907      */
9908     hide : function(callback){
9909         if (this.fireEvent("beforehide", this) === false){
9910             return;
9911         }
9912         if(this.shadow){
9913             this.shadow.hide();
9914         }
9915         if(this.shim) {
9916           this.shim.hide();
9917         }
9918         // sometimes animateTarget seems to get set.. causing problems...
9919         // this just double checks..
9920         if(this.animateTarget && Roo.get(this.animateTarget)) {
9921            this.animHide(callback);
9922         }else{
9923             this.el.hide();
9924             this.hideEl(callback);
9925         }
9926         return this;
9927     },
9928
9929     // private
9930     hideEl : function(callback){
9931         this.proxy.hide();
9932         if(this.modal){
9933             this.mask.hide();
9934             Roo.get(document.body).removeClass("x-body-masked");
9935         }
9936         this.fireEvent("hide", this);
9937         if(typeof callback == "function"){
9938             callback();
9939         }
9940     },
9941
9942     // private
9943     hideAction : function(){
9944         this.setLeft("-10000px");
9945         this.setTop("-10000px");
9946         this.setStyle("visibility", "hidden");
9947     },
9948
9949     // private
9950     refreshSize : function(){
9951         this.size = this.el.getSize();
9952         this.xy = this.el.getXY();
9953         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9954     },
9955
9956     // private
9957     // z-index is managed by the DialogManager and may be overwritten at any time
9958     setZIndex : function(index){
9959         if(this.modal){
9960             this.mask.setStyle("z-index", index);
9961         }
9962         if(this.shim){
9963             this.shim.setStyle("z-index", ++index);
9964         }
9965         if(this.shadow){
9966             this.shadow.setZIndex(++index);
9967         }
9968         this.el.setStyle("z-index", ++index);
9969         if(this.proxy){
9970             this.proxy.setStyle("z-index", ++index);
9971         }
9972         if(this.resizer){
9973             this.resizer.proxy.setStyle("z-index", ++index);
9974         }
9975
9976         this.lastZIndex = index;
9977     },
9978
9979     /**
9980      * Returns the element for this dialog
9981      * @return {Roo.Element} The underlying dialog Element
9982      */
9983     getEl : function(){
9984         return this.el;
9985     }
9986 });
9987
9988 /**
9989  * @class Roo.DialogManager
9990  * Provides global access to BasicDialogs that have been created and
9991  * support for z-indexing (layering) multiple open dialogs.
9992  */
9993 Roo.DialogManager = function(){
9994     var list = {};
9995     var accessList = [];
9996     var front = null;
9997
9998     // private
9999     var sortDialogs = function(d1, d2){
10000         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10001     };
10002
10003     // private
10004     var orderDialogs = function(){
10005         accessList.sort(sortDialogs);
10006         var seed = Roo.DialogManager.zseed;
10007         for(var i = 0, len = accessList.length; i < len; i++){
10008             var dlg = accessList[i];
10009             if(dlg){
10010                 dlg.setZIndex(seed + (i*10));
10011             }
10012         }
10013     };
10014
10015     return {
10016         /**
10017          * The starting z-index for BasicDialogs (defaults to 9000)
10018          * @type Number The z-index value
10019          */
10020         zseed : 9000,
10021
10022         // private
10023         register : function(dlg){
10024             list[dlg.id] = dlg;
10025             accessList.push(dlg);
10026         },
10027
10028         // private
10029         unregister : function(dlg){
10030             delete list[dlg.id];
10031             var i=0;
10032             var len=0;
10033             if(!accessList.indexOf){
10034                 for(  i = 0, len = accessList.length; i < len; i++){
10035                     if(accessList[i] == dlg){
10036                         accessList.splice(i, 1);
10037                         return;
10038                     }
10039                 }
10040             }else{
10041                  i = accessList.indexOf(dlg);
10042                 if(i != -1){
10043                     accessList.splice(i, 1);
10044                 }
10045             }
10046         },
10047
10048         /**
10049          * Gets a registered dialog by id
10050          * @param {String/Object} id The id of the dialog or a dialog
10051          * @return {Roo.BasicDialog} this
10052          */
10053         get : function(id){
10054             return typeof id == "object" ? id : list[id];
10055         },
10056
10057         /**
10058          * Brings the specified dialog to the front
10059          * @param {String/Object} dlg The id of the dialog or a dialog
10060          * @return {Roo.BasicDialog} this
10061          */
10062         bringToFront : function(dlg){
10063             dlg = this.get(dlg);
10064             if(dlg != front){
10065                 front = dlg;
10066                 dlg._lastAccess = new Date().getTime();
10067                 orderDialogs();
10068             }
10069             return dlg;
10070         },
10071
10072         /**
10073          * Sends the specified dialog to the back
10074          * @param {String/Object} dlg The id of the dialog or a dialog
10075          * @return {Roo.BasicDialog} this
10076          */
10077         sendToBack : function(dlg){
10078             dlg = this.get(dlg);
10079             dlg._lastAccess = -(new Date().getTime());
10080             orderDialogs();
10081             return dlg;
10082         },
10083
10084         /**
10085          * Hides all dialogs
10086          */
10087         hideAll : function(){
10088             for(var id in list){
10089                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10090                     list[id].hide();
10091                 }
10092             }
10093         }
10094     };
10095 }();
10096
10097 /**
10098  * @class Roo.LayoutDialog
10099  * @extends Roo.BasicDialog
10100  * @children Roo.ContentPanel
10101  * Dialog which provides adjustments for working with a layout in a Dialog.
10102  * Add your necessary layout config options to the dialog's config.<br>
10103  * Example usage (including a nested layout):
10104  * <pre><code>
10105 if(!dialog){
10106     dialog = new Roo.LayoutDialog("download-dlg", {
10107         modal: true,
10108         width:600,
10109         height:450,
10110         shadow:true,
10111         minWidth:500,
10112         minHeight:350,
10113         autoTabs:true,
10114         proxyDrag:true,
10115         // layout config merges with the dialog config
10116         center:{
10117             tabPosition: "top",
10118             alwaysShowTabs: true
10119         }
10120     });
10121     dialog.addKeyListener(27, dialog.hide, dialog);
10122     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10123     dialog.addButton("Build It!", this.getDownload, this);
10124
10125     // we can even add nested layouts
10126     var innerLayout = new Roo.BorderLayout("dl-inner", {
10127         east: {
10128             initialSize: 200,
10129             autoScroll:true,
10130             split:true
10131         },
10132         center: {
10133             autoScroll:true
10134         }
10135     });
10136     innerLayout.beginUpdate();
10137     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10138     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10139     innerLayout.endUpdate(true);
10140
10141     var layout = dialog.getLayout();
10142     layout.beginUpdate();
10143     layout.add("center", new Roo.ContentPanel("standard-panel",
10144                         {title: "Download the Source", fitToFrame:true}));
10145     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10146                {title: "Build your own roo.js"}));
10147     layout.getRegion("center").showPanel(sp);
10148     layout.endUpdate();
10149 }
10150 </code></pre>
10151     * @constructor
10152     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10153     * @param {Object} config configuration options
10154   */
10155 Roo.LayoutDialog = function(el, cfg){
10156     
10157     var config=  cfg;
10158     if (typeof(cfg) == 'undefined') {
10159         config = Roo.apply({}, el);
10160         // not sure why we use documentElement here.. - it should always be body.
10161         // IE7 borks horribly if we use documentElement.
10162         // webkit also does not like documentElement - it creates a body element...
10163         el = Roo.get( document.body || document.documentElement ).createChild();
10164         //config.autoCreate = true;
10165     }
10166     
10167     
10168     config.autoTabs = false;
10169     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10170     this.body.setStyle({overflow:"hidden", position:"relative"});
10171     this.layout = new Roo.BorderLayout(this.body.dom, config);
10172     this.layout.monitorWindowResize = false;
10173     this.el.addClass("x-dlg-auto-layout");
10174     // fix case when center region overwrites center function
10175     this.center = Roo.BasicDialog.prototype.center;
10176     this.on("show", this.layout.layout, this.layout, true);
10177     if (config.items) {
10178         var xitems = config.items;
10179         delete config.items;
10180         Roo.each(xitems, this.addxtype, this);
10181     }
10182     
10183     
10184 };
10185 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10186     
10187     
10188     /**
10189      * @cfg {Roo.LayoutRegion} east  
10190      */
10191     /**
10192      * @cfg {Roo.LayoutRegion} west
10193      */
10194     /**
10195      * @cfg {Roo.LayoutRegion} south
10196      */
10197     /**
10198      * @cfg {Roo.LayoutRegion} north
10199      */
10200     /**
10201      * @cfg {Roo.LayoutRegion} center
10202      */
10203     /**
10204      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10205      */
10206     
10207     
10208     /**
10209      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10210      * @deprecated
10211      */
10212     endUpdate : function(){
10213         this.layout.endUpdate();
10214     },
10215
10216     /**
10217      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10218      *  @deprecated
10219      */
10220     beginUpdate : function(){
10221         this.layout.beginUpdate();
10222     },
10223
10224     /**
10225      * Get the BorderLayout for this dialog
10226      * @return {Roo.BorderLayout}
10227      */
10228     getLayout : function(){
10229         return this.layout;
10230     },
10231
10232     showEl : function(){
10233         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10234         if(Roo.isIE7){
10235             this.layout.layout();
10236         }
10237     },
10238
10239     // private
10240     // Use the syncHeightBeforeShow config option to control this automatically
10241     syncBodyHeight : function(){
10242         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10243         if(this.layout){this.layout.layout();}
10244     },
10245     
10246       /**
10247      * Add an xtype element (actually adds to the layout.)
10248      * @return {Object} xdata xtype object data.
10249      */
10250     
10251     addxtype : function(c) {
10252         return this.layout.addxtype(c);
10253     }
10254 });/*
10255  * Based on:
10256  * Ext JS Library 1.1.1
10257  * Copyright(c) 2006-2007, Ext JS, LLC.
10258  *
10259  * Originally Released Under LGPL - original licence link has changed is not relivant.
10260  *
10261  * Fork - LGPL
10262  * <script type="text/javascript">
10263  */
10264  
10265 /**
10266  * @class Roo.MessageBox
10267  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10268  * Example usage:
10269  *<pre><code>
10270 // Basic alert:
10271 Roo.Msg.alert('Status', 'Changes saved successfully.');
10272
10273 // Prompt for user data:
10274 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10275     if (btn == 'ok'){
10276         // process text value...
10277     }
10278 });
10279
10280 // Show a dialog using config options:
10281 Roo.Msg.show({
10282    title:'Save Changes?',
10283    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10284    buttons: Roo.Msg.YESNOCANCEL,
10285    fn: processResult,
10286    animEl: 'elId'
10287 });
10288 </code></pre>
10289  * @singleton
10290  */
10291 Roo.MessageBox = function(){
10292     var dlg, opt, mask, waitTimer;
10293     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10294     var buttons, activeTextEl, bwidth;
10295
10296     // private
10297     var handleButton = function(button){
10298         dlg.hide();
10299         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10300     };
10301
10302     // private
10303     var handleHide = function(){
10304         if(opt && opt.cls){
10305             dlg.el.removeClass(opt.cls);
10306         }
10307         if(waitTimer){
10308             Roo.TaskMgr.stop(waitTimer);
10309             waitTimer = null;
10310         }
10311     };
10312
10313     // private
10314     var updateButtons = function(b){
10315         var width = 0;
10316         if(!b){
10317             buttons["ok"].hide();
10318             buttons["cancel"].hide();
10319             buttons["yes"].hide();
10320             buttons["no"].hide();
10321             dlg.footer.dom.style.display = 'none';
10322             return width;
10323         }
10324         dlg.footer.dom.style.display = '';
10325         for(var k in buttons){
10326             if(typeof buttons[k] != "function"){
10327                 if(b[k]){
10328                     buttons[k].show();
10329                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10330                     width += buttons[k].el.getWidth()+15;
10331                 }else{
10332                     buttons[k].hide();
10333                 }
10334             }
10335         }
10336         return width;
10337     };
10338
10339     // private
10340     var handleEsc = function(d, k, e){
10341         if(opt && opt.closable !== false){
10342             dlg.hide();
10343         }
10344         if(e){
10345             e.stopEvent();
10346         }
10347     };
10348
10349     return {
10350         /**
10351          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10352          * @return {Roo.BasicDialog} The BasicDialog element
10353          */
10354         getDialog : function(){
10355            if(!dlg){
10356                 dlg = new Roo.BasicDialog("x-msg-box", {
10357                     autoCreate : true,
10358                     shadow: true,
10359                     draggable: true,
10360                     resizable:false,
10361                     constraintoviewport:false,
10362                     fixedcenter:true,
10363                     collapsible : false,
10364                     shim:true,
10365                     modal: true,
10366                     width:400, height:100,
10367                     buttonAlign:"center",
10368                     closeClick : function(){
10369                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10370                             handleButton("no");
10371                         }else{
10372                             handleButton("cancel");
10373                         }
10374                     }
10375                 });
10376                 dlg.on("hide", handleHide);
10377                 mask = dlg.mask;
10378                 dlg.addKeyListener(27, handleEsc);
10379                 buttons = {};
10380                 var bt = this.buttonText;
10381                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10382                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10383                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10384                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10385                 bodyEl = dlg.body.createChild({
10386
10387                     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>'
10388                 });
10389                 msgEl = bodyEl.dom.firstChild;
10390                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10391                 textboxEl.enableDisplayMode();
10392                 textboxEl.addKeyListener([10,13], function(){
10393                     if(dlg.isVisible() && opt && opt.buttons){
10394                         if(opt.buttons.ok){
10395                             handleButton("ok");
10396                         }else if(opt.buttons.yes){
10397                             handleButton("yes");
10398                         }
10399                     }
10400                 });
10401                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10402                 textareaEl.enableDisplayMode();
10403                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10404                 progressEl.enableDisplayMode();
10405                 var pf = progressEl.dom.firstChild;
10406                 if (pf) {
10407                     pp = Roo.get(pf.firstChild);
10408                     pp.setHeight(pf.offsetHeight);
10409                 }
10410                 
10411             }
10412             return dlg;
10413         },
10414
10415         /**
10416          * Updates the message box body text
10417          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10418          * the XHTML-compliant non-breaking space character '&amp;#160;')
10419          * @return {Roo.MessageBox} This message box
10420          */
10421         updateText : function(text){
10422             if(!dlg.isVisible() && !opt.width){
10423                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10424             }
10425             msgEl.innerHTML = text || '&#160;';
10426       
10427             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10428             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10429             var w = Math.max(
10430                     Math.min(opt.width || cw , this.maxWidth), 
10431                     Math.max(opt.minWidth || this.minWidth, bwidth)
10432             );
10433             if(opt.prompt){
10434                 activeTextEl.setWidth(w);
10435             }
10436             if(dlg.isVisible()){
10437                 dlg.fixedcenter = false;
10438             }
10439             // to big, make it scroll. = But as usual stupid IE does not support
10440             // !important..
10441             
10442             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10443                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10444                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10445             } else {
10446                 bodyEl.dom.style.height = '';
10447                 bodyEl.dom.style.overflowY = '';
10448             }
10449             if (cw > w) {
10450                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10451             } else {
10452                 bodyEl.dom.style.overflowX = '';
10453             }
10454             
10455             dlg.setContentSize(w, bodyEl.getHeight());
10456             if(dlg.isVisible()){
10457                 dlg.fixedcenter = true;
10458             }
10459             return this;
10460         },
10461
10462         /**
10463          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10464          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10465          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10466          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10467          * @return {Roo.MessageBox} This message box
10468          */
10469         updateProgress : function(value, text){
10470             if(text){
10471                 this.updateText(text);
10472             }
10473             if (pp) { // weird bug on my firefox - for some reason this is not defined
10474                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10475             }
10476             return this;
10477         },        
10478
10479         /**
10480          * Returns true if the message box is currently displayed
10481          * @return {Boolean} True if the message box is visible, else false
10482          */
10483         isVisible : function(){
10484             return dlg && dlg.isVisible();  
10485         },
10486
10487         /**
10488          * Hides the message box if it is displayed
10489          */
10490         hide : function(){
10491             if(this.isVisible()){
10492                 dlg.hide();
10493             }  
10494         },
10495
10496         /**
10497          * Displays a new message box, or reinitializes an existing message box, based on the config options
10498          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10499          * The following config object properties are supported:
10500          * <pre>
10501 Property    Type             Description
10502 ----------  ---------------  ------------------------------------------------------------------------------------
10503 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10504                                    closes (defaults to undefined)
10505 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10506                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10507 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10508                                    progress and wait dialogs will ignore this property and always hide the
10509                                    close button as they can only be closed programmatically.
10510 cls               String           A custom CSS class to apply to the message box element
10511 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10512                                    displayed (defaults to 75)
10513 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10514                                    function will be btn (the name of the button that was clicked, if applicable,
10515                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10516                                    Progress and wait dialogs will ignore this option since they do not respond to
10517                                    user actions and can only be closed programmatically, so any required function
10518                                    should be called by the same code after it closes the dialog.
10519 icon              String           A CSS class that provides a background image to be used as an icon for
10520                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10521 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10522 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10523 modal             Boolean          False to allow user interaction with the page while the message box is
10524                                    displayed (defaults to true)
10525 msg               String           A string that will replace the existing message box body text (defaults
10526                                    to the XHTML-compliant non-breaking space character '&#160;')
10527 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10528 progress          Boolean          True to display a progress bar (defaults to false)
10529 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10530 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10531 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10532 title             String           The title text
10533 value             String           The string value to set into the active textbox element if displayed
10534 wait              Boolean          True to display a progress bar (defaults to false)
10535 width             Number           The width of the dialog in pixels
10536 </pre>
10537          *
10538          * Example usage:
10539          * <pre><code>
10540 Roo.Msg.show({
10541    title: 'Address',
10542    msg: 'Please enter your address:',
10543    width: 300,
10544    buttons: Roo.MessageBox.OKCANCEL,
10545    multiline: true,
10546    fn: saveAddress,
10547    animEl: 'addAddressBtn'
10548 });
10549 </code></pre>
10550          * @param {Object} config Configuration options
10551          * @return {Roo.MessageBox} This message box
10552          */
10553         show : function(options)
10554         {
10555             
10556             // this causes nightmares if you show one dialog after another
10557             // especially on callbacks..
10558              
10559             if(this.isVisible()){
10560                 
10561                 this.hide();
10562                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10563                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10564                 Roo.log("New Dialog Message:" +  options.msg )
10565                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10566                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10567                 
10568             }
10569             var d = this.getDialog();
10570             opt = options;
10571             d.setTitle(opt.title || "&#160;");
10572             d.close.setDisplayed(opt.closable !== false);
10573             activeTextEl = textboxEl;
10574             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10575             if(opt.prompt){
10576                 if(opt.multiline){
10577                     textboxEl.hide();
10578                     textareaEl.show();
10579                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10580                         opt.multiline : this.defaultTextHeight);
10581                     activeTextEl = textareaEl;
10582                 }else{
10583                     textboxEl.show();
10584                     textareaEl.hide();
10585                 }
10586             }else{
10587                 textboxEl.hide();
10588                 textareaEl.hide();
10589             }
10590             progressEl.setDisplayed(opt.progress === true);
10591             this.updateProgress(0);
10592             activeTextEl.dom.value = opt.value || "";
10593             if(opt.prompt){
10594                 dlg.setDefaultButton(activeTextEl);
10595             }else{
10596                 var bs = opt.buttons;
10597                 var db = null;
10598                 if(bs && bs.ok){
10599                     db = buttons["ok"];
10600                 }else if(bs && bs.yes){
10601                     db = buttons["yes"];
10602                 }
10603                 dlg.setDefaultButton(db);
10604             }
10605             bwidth = updateButtons(opt.buttons);
10606             this.updateText(opt.msg);
10607             if(opt.cls){
10608                 d.el.addClass(opt.cls);
10609             }
10610             d.proxyDrag = opt.proxyDrag === true;
10611             d.modal = opt.modal !== false;
10612             d.mask = opt.modal !== false ? mask : false;
10613             if(!d.isVisible()){
10614                 // force it to the end of the z-index stack so it gets a cursor in FF
10615                 document.body.appendChild(dlg.el.dom);
10616                 d.animateTarget = null;
10617                 d.show(options.animEl);
10618             }
10619             return this;
10620         },
10621
10622         /**
10623          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10624          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10625          * and closing the message box when the process is complete.
10626          * @param {String} title The title bar text
10627          * @param {String} msg The message box body text
10628          * @return {Roo.MessageBox} This message box
10629          */
10630         progress : function(title, msg){
10631             this.show({
10632                 title : title,
10633                 msg : msg,
10634                 buttons: false,
10635                 progress:true,
10636                 closable:false,
10637                 minWidth: this.minProgressWidth,
10638                 modal : true
10639             });
10640             return this;
10641         },
10642
10643         /**
10644          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10645          * If a callback function is passed it will be called after the user clicks the button, and the
10646          * id of the button that was clicked will be passed as the only parameter to the callback
10647          * (could also be the top-right close button).
10648          * @param {String} title The title bar text
10649          * @param {String} msg The message box body text
10650          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10651          * @param {Object} scope (optional) The scope of the callback function
10652          * @return {Roo.MessageBox} This message box
10653          */
10654         alert : function(title, msg, fn, scope){
10655             this.show({
10656                 title : title,
10657                 msg : msg,
10658                 buttons: this.OK,
10659                 fn: fn,
10660                 scope : scope,
10661                 modal : true
10662             });
10663             return this;
10664         },
10665
10666         /**
10667          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10668          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10669          * You are responsible for closing the message box when the process is complete.
10670          * @param {String} msg The message box body text
10671          * @param {String} title (optional) The title bar text
10672          * @return {Roo.MessageBox} This message box
10673          */
10674         wait : function(msg, title){
10675             this.show({
10676                 title : title,
10677                 msg : msg,
10678                 buttons: false,
10679                 closable:false,
10680                 progress:true,
10681                 modal:true,
10682                 width:300,
10683                 wait:true
10684             });
10685             waitTimer = Roo.TaskMgr.start({
10686                 run: function(i){
10687                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10688                 },
10689                 interval: 1000
10690             });
10691             return this;
10692         },
10693
10694         /**
10695          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10696          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10697          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10698          * @param {String} title The title bar text
10699          * @param {String} msg The message box body text
10700          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10701          * @param {Object} scope (optional) The scope of the callback function
10702          * @return {Roo.MessageBox} This message box
10703          */
10704         confirm : function(title, msg, fn, scope){
10705             this.show({
10706                 title : title,
10707                 msg : msg,
10708                 buttons: this.YESNO,
10709                 fn: fn,
10710                 scope : scope,
10711                 modal : true
10712             });
10713             return this;
10714         },
10715
10716         /**
10717          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10718          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10719          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10720          * (could also be the top-right close button) and the text that was entered will be passed as the two
10721          * parameters to the callback.
10722          * @param {String} title The title bar text
10723          * @param {String} msg The message box body text
10724          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10725          * @param {Object} scope (optional) The scope of the callback function
10726          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10727          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10728          * @return {Roo.MessageBox} This message box
10729          */
10730         prompt : function(title, msg, fn, scope, multiline){
10731             this.show({
10732                 title : title,
10733                 msg : msg,
10734                 buttons: this.OKCANCEL,
10735                 fn: fn,
10736                 minWidth:250,
10737                 scope : scope,
10738                 prompt:true,
10739                 multiline: multiline,
10740                 modal : true
10741             });
10742             return this;
10743         },
10744
10745         /**
10746          * Button config that displays a single OK button
10747          * @type Object
10748          */
10749         OK : {ok:true},
10750         /**
10751          * Button config that displays Yes and No buttons
10752          * @type Object
10753          */
10754         YESNO : {yes:true, no:true},
10755         /**
10756          * Button config that displays OK and Cancel buttons
10757          * @type Object
10758          */
10759         OKCANCEL : {ok:true, cancel:true},
10760         /**
10761          * Button config that displays Yes, No and Cancel buttons
10762          * @type Object
10763          */
10764         YESNOCANCEL : {yes:true, no:true, cancel:true},
10765
10766         /**
10767          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10768          * @type Number
10769          */
10770         defaultTextHeight : 75,
10771         /**
10772          * The maximum width in pixels of the message box (defaults to 600)
10773          * @type Number
10774          */
10775         maxWidth : 600,
10776         /**
10777          * The minimum width in pixels of the message box (defaults to 100)
10778          * @type Number
10779          */
10780         minWidth : 100,
10781         /**
10782          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10783          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10784          * @type Number
10785          */
10786         minProgressWidth : 250,
10787         /**
10788          * An object containing the default button text strings that can be overriden for localized language support.
10789          * Supported properties are: ok, cancel, yes and no.
10790          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10791          * @type Object
10792          */
10793         buttonText : {
10794             ok : "OK",
10795             cancel : "Cancel",
10796             yes : "Yes",
10797             no : "No"
10798         }
10799     };
10800 }();
10801
10802 /**
10803  * Shorthand for {@link Roo.MessageBox}
10804  */
10805 Roo.Msg = Roo.MessageBox;/*
10806  * Based on:
10807  * Ext JS Library 1.1.1
10808  * Copyright(c) 2006-2007, Ext JS, LLC.
10809  *
10810  * Originally Released Under LGPL - original licence link has changed is not relivant.
10811  *
10812  * Fork - LGPL
10813  * <script type="text/javascript">
10814  */
10815 /**
10816  * @class Roo.QuickTips
10817  * Provides attractive and customizable tooltips for any element.
10818  * @singleton
10819  */
10820 Roo.QuickTips = function(){
10821     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10822     var ce, bd, xy, dd;
10823     var visible = false, disabled = true, inited = false;
10824     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10825     
10826     var onOver = function(e){
10827         if(disabled){
10828             return;
10829         }
10830         var t = e.getTarget();
10831         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10832             return;
10833         }
10834         if(ce && t == ce.el){
10835             clearTimeout(hideProc);
10836             return;
10837         }
10838         if(t && tagEls[t.id]){
10839             tagEls[t.id].el = t;
10840             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10841             return;
10842         }
10843         var ttp, et = Roo.fly(t);
10844         var ns = cfg.namespace;
10845         if(tm.interceptTitles && t.title){
10846             ttp = t.title;
10847             t.qtip = ttp;
10848             t.removeAttribute("title");
10849             e.preventDefault();
10850         }else{
10851             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10852         }
10853         if(ttp){
10854             showProc = show.defer(tm.showDelay, tm, [{
10855                 el: t, 
10856                 text: ttp.replace(/\\n/g,'<br/>'),
10857                 width: et.getAttributeNS(ns, cfg.width),
10858                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10859                 title: et.getAttributeNS(ns, cfg.title),
10860                     cls: et.getAttributeNS(ns, cfg.cls)
10861             }]);
10862         }
10863     };
10864     
10865     var onOut = function(e){
10866         clearTimeout(showProc);
10867         var t = e.getTarget();
10868         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10869             hideProc = setTimeout(hide, tm.hideDelay);
10870         }
10871     };
10872     
10873     var onMove = function(e){
10874         if(disabled){
10875             return;
10876         }
10877         xy = e.getXY();
10878         xy[1] += 18;
10879         if(tm.trackMouse && ce){
10880             el.setXY(xy);
10881         }
10882     };
10883     
10884     var onDown = function(e){
10885         clearTimeout(showProc);
10886         clearTimeout(hideProc);
10887         if(!e.within(el)){
10888             if(tm.hideOnClick){
10889                 hide();
10890                 tm.disable();
10891                 tm.enable.defer(100, tm);
10892             }
10893         }
10894     };
10895     
10896     var getPad = function(){
10897         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10898     };
10899
10900     var show = function(o){
10901         if(disabled){
10902             return;
10903         }
10904         clearTimeout(dismissProc);
10905         ce = o;
10906         if(removeCls){ // in case manually hidden
10907             el.removeClass(removeCls);
10908             removeCls = null;
10909         }
10910         if(ce.cls){
10911             el.addClass(ce.cls);
10912             removeCls = ce.cls;
10913         }
10914         if(ce.title){
10915             tipTitle.update(ce.title);
10916             tipTitle.show();
10917         }else{
10918             tipTitle.update('');
10919             tipTitle.hide();
10920         }
10921         el.dom.style.width  = tm.maxWidth+'px';
10922         //tipBody.dom.style.width = '';
10923         tipBodyText.update(o.text);
10924         var p = getPad(), w = ce.width;
10925         if(!w){
10926             var td = tipBodyText.dom;
10927             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10928             if(aw > tm.maxWidth){
10929                 w = tm.maxWidth;
10930             }else if(aw < tm.minWidth){
10931                 w = tm.minWidth;
10932             }else{
10933                 w = aw;
10934             }
10935         }
10936         //tipBody.setWidth(w);
10937         el.setWidth(parseInt(w, 10) + p);
10938         if(ce.autoHide === false){
10939             close.setDisplayed(true);
10940             if(dd){
10941                 dd.unlock();
10942             }
10943         }else{
10944             close.setDisplayed(false);
10945             if(dd){
10946                 dd.lock();
10947             }
10948         }
10949         if(xy){
10950             el.avoidY = xy[1]-18;
10951             el.setXY(xy);
10952         }
10953         if(tm.animate){
10954             el.setOpacity(.1);
10955             el.setStyle("visibility", "visible");
10956             el.fadeIn({callback: afterShow});
10957         }else{
10958             afterShow();
10959         }
10960     };
10961     
10962     var afterShow = function(){
10963         if(ce){
10964             el.show();
10965             esc.enable();
10966             if(tm.autoDismiss && ce.autoHide !== false){
10967                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10968             }
10969         }
10970     };
10971     
10972     var hide = function(noanim){
10973         clearTimeout(dismissProc);
10974         clearTimeout(hideProc);
10975         ce = null;
10976         if(el.isVisible()){
10977             esc.disable();
10978             if(noanim !== true && tm.animate){
10979                 el.fadeOut({callback: afterHide});
10980             }else{
10981                 afterHide();
10982             } 
10983         }
10984     };
10985     
10986     var afterHide = function(){
10987         el.hide();
10988         if(removeCls){
10989             el.removeClass(removeCls);
10990             removeCls = null;
10991         }
10992     };
10993     
10994     return {
10995         /**
10996         * @cfg {Number} minWidth
10997         * The minimum width of the quick tip (defaults to 40)
10998         */
10999        minWidth : 40,
11000         /**
11001         * @cfg {Number} maxWidth
11002         * The maximum width of the quick tip (defaults to 300)
11003         */
11004        maxWidth : 300,
11005         /**
11006         * @cfg {Boolean} interceptTitles
11007         * True to automatically use the element's DOM title value if available (defaults to false)
11008         */
11009        interceptTitles : false,
11010         /**
11011         * @cfg {Boolean} trackMouse
11012         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11013         */
11014        trackMouse : false,
11015         /**
11016         * @cfg {Boolean} hideOnClick
11017         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11018         */
11019        hideOnClick : true,
11020         /**
11021         * @cfg {Number} showDelay
11022         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11023         */
11024        showDelay : 500,
11025         /**
11026         * @cfg {Number} hideDelay
11027         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11028         */
11029        hideDelay : 200,
11030         /**
11031         * @cfg {Boolean} autoHide
11032         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11033         * Used in conjunction with hideDelay.
11034         */
11035        autoHide : true,
11036         /**
11037         * @cfg {Boolean}
11038         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11039         * (defaults to true).  Used in conjunction with autoDismissDelay.
11040         */
11041        autoDismiss : true,
11042         /**
11043         * @cfg {Number}
11044         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11045         */
11046        autoDismissDelay : 5000,
11047        /**
11048         * @cfg {Boolean} animate
11049         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11050         */
11051        animate : false,
11052
11053        /**
11054         * @cfg {String} title
11055         * Title text to display (defaults to '').  This can be any valid HTML markup.
11056         */
11057         title: '',
11058        /**
11059         * @cfg {String} text
11060         * Body text to display (defaults to '').  This can be any valid HTML markup.
11061         */
11062         text : '',
11063        /**
11064         * @cfg {String} cls
11065         * A CSS class to apply to the base quick tip element (defaults to '').
11066         */
11067         cls : '',
11068        /**
11069         * @cfg {Number} width
11070         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11071         * minWidth or maxWidth.
11072         */
11073         width : null,
11074
11075     /**
11076      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11077      * or display QuickTips in a page.
11078      */
11079        init : function(){
11080           tm = Roo.QuickTips;
11081           cfg = tm.tagConfig;
11082           if(!inited){
11083               if(!Roo.isReady){ // allow calling of init() before onReady
11084                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11085                   return;
11086               }
11087               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11088               el.fxDefaults = {stopFx: true};
11089               // maximum custom styling
11090               //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>');
11091               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>');              
11092               tipTitle = el.child('h3');
11093               tipTitle.enableDisplayMode("block");
11094               tipBody = el.child('div.x-tip-bd');
11095               tipBodyText = el.child('div.x-tip-bd-inner');
11096               //bdLeft = el.child('div.x-tip-bd-left');
11097               //bdRight = el.child('div.x-tip-bd-right');
11098               close = el.child('div.x-tip-close');
11099               close.enableDisplayMode("block");
11100               close.on("click", hide);
11101               var d = Roo.get(document);
11102               d.on("mousedown", onDown);
11103               d.on("mouseover", onOver);
11104               d.on("mouseout", onOut);
11105               d.on("mousemove", onMove);
11106               esc = d.addKeyListener(27, hide);
11107               esc.disable();
11108               if(Roo.dd.DD){
11109                   dd = el.initDD("default", null, {
11110                       onDrag : function(){
11111                           el.sync();  
11112                       }
11113                   });
11114                   dd.setHandleElId(tipTitle.id);
11115                   dd.lock();
11116               }
11117               inited = true;
11118           }
11119           this.enable(); 
11120        },
11121
11122     /**
11123      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11124      * are supported:
11125      * <pre>
11126 Property    Type                   Description
11127 ----------  ---------------------  ------------------------------------------------------------------------
11128 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11129      * </ul>
11130      * @param {Object} config The config object
11131      */
11132        register : function(config){
11133            var cs = config instanceof Array ? config : arguments;
11134            for(var i = 0, len = cs.length; i < len; i++) {
11135                var c = cs[i];
11136                var target = c.target;
11137                if(target){
11138                    if(target instanceof Array){
11139                        for(var j = 0, jlen = target.length; j < jlen; j++){
11140                            tagEls[target[j]] = c;
11141                        }
11142                    }else{
11143                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11144                    }
11145                }
11146            }
11147        },
11148
11149     /**
11150      * Removes this quick tip from its element and destroys it.
11151      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11152      */
11153        unregister : function(el){
11154            delete tagEls[Roo.id(el)];
11155        },
11156
11157     /**
11158      * Enable this quick tip.
11159      */
11160        enable : function(){
11161            if(inited && disabled){
11162                locks.pop();
11163                if(locks.length < 1){
11164                    disabled = false;
11165                }
11166            }
11167        },
11168
11169     /**
11170      * Disable this quick tip.
11171      */
11172        disable : function(){
11173           disabled = true;
11174           clearTimeout(showProc);
11175           clearTimeout(hideProc);
11176           clearTimeout(dismissProc);
11177           if(ce){
11178               hide(true);
11179           }
11180           locks.push(1);
11181        },
11182
11183     /**
11184      * Returns true if the quick tip is enabled, else false.
11185      */
11186        isEnabled : function(){
11187             return !disabled;
11188        },
11189
11190         // private
11191        tagConfig : {
11192            namespace : "roo", // was ext?? this may break..
11193            alt_namespace : "ext",
11194            attribute : "qtip",
11195            width : "width",
11196            target : "target",
11197            title : "qtitle",
11198            hide : "hide",
11199            cls : "qclass"
11200        }
11201    };
11202 }();
11203
11204 // backwards compat
11205 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11206  * Based on:
11207  * Ext JS Library 1.1.1
11208  * Copyright(c) 2006-2007, Ext JS, LLC.
11209  *
11210  * Originally Released Under LGPL - original licence link has changed is not relivant.
11211  *
11212  * Fork - LGPL
11213  * <script type="text/javascript">
11214  */
11215  
11216
11217 /**
11218  * @class Roo.tree.TreePanel
11219  * @extends Roo.data.Tree
11220  * @cfg {Roo.tree.TreeNode} root The root node
11221  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11222  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11223  * @cfg {Boolean} enableDD true to enable drag and drop
11224  * @cfg {Boolean} enableDrag true to enable just drag
11225  * @cfg {Boolean} enableDrop true to enable just drop
11226  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11227  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11228  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11229  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11230  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11231  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11232  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11233  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11234  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11235  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11236  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11237  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11238  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11239  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11240  * @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>
11241  * @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>
11242  * 
11243  * @constructor
11244  * @param {String/HTMLElement/Element} el The container element
11245  * @param {Object} config
11246  */
11247 Roo.tree.TreePanel = function(el, config){
11248     var root = false;
11249     var loader = false;
11250     if (config.root) {
11251         root = config.root;
11252         delete config.root;
11253     }
11254     if (config.loader) {
11255         loader = config.loader;
11256         delete config.loader;
11257     }
11258     
11259     Roo.apply(this, config);
11260     Roo.tree.TreePanel.superclass.constructor.call(this);
11261     this.el = Roo.get(el);
11262     this.el.addClass('x-tree');
11263     //console.log(root);
11264     if (root) {
11265         this.setRootNode( Roo.factory(root, Roo.tree));
11266     }
11267     if (loader) {
11268         this.loader = Roo.factory(loader, Roo.tree);
11269     }
11270    /**
11271     * Read-only. The id of the container element becomes this TreePanel's id.
11272     */
11273     this.id = this.el.id;
11274     this.addEvents({
11275         /**
11276         * @event beforeload
11277         * Fires before a node is loaded, return false to cancel
11278         * @param {Node} node The node being loaded
11279         */
11280         "beforeload" : true,
11281         /**
11282         * @event load
11283         * Fires when a node is loaded
11284         * @param {Node} node The node that was loaded
11285         */
11286         "load" : true,
11287         /**
11288         * @event textchange
11289         * Fires when the text for a node is changed
11290         * @param {Node} node The node
11291         * @param {String} text The new text
11292         * @param {String} oldText The old text
11293         */
11294         "textchange" : true,
11295         /**
11296         * @event beforeexpand
11297         * Fires before a node is expanded, return false to cancel.
11298         * @param {Node} node The node
11299         * @param {Boolean} deep
11300         * @param {Boolean} anim
11301         */
11302         "beforeexpand" : true,
11303         /**
11304         * @event beforecollapse
11305         * Fires before a node is collapsed, return false to cancel.
11306         * @param {Node} node The node
11307         * @param {Boolean} deep
11308         * @param {Boolean} anim
11309         */
11310         "beforecollapse" : true,
11311         /**
11312         * @event expand
11313         * Fires when a node is expanded
11314         * @param {Node} node The node
11315         */
11316         "expand" : true,
11317         /**
11318         * @event disabledchange
11319         * Fires when the disabled status of a node changes
11320         * @param {Node} node The node
11321         * @param {Boolean} disabled
11322         */
11323         "disabledchange" : true,
11324         /**
11325         * @event collapse
11326         * Fires when a node is collapsed
11327         * @param {Node} node The node
11328         */
11329         "collapse" : true,
11330         /**
11331         * @event beforeclick
11332         * Fires before click processing on a node. Return false to cancel the default action.
11333         * @param {Node} node The node
11334         * @param {Roo.EventObject} e The event object
11335         */
11336         "beforeclick":true,
11337         /**
11338         * @event checkchange
11339         * Fires when a node with a checkbox's checked property changes
11340         * @param {Node} this This node
11341         * @param {Boolean} checked
11342         */
11343         "checkchange":true,
11344         /**
11345         * @event click
11346         * Fires when a node is clicked
11347         * @param {Node} node The node
11348         * @param {Roo.EventObject} e The event object
11349         */
11350         "click":true,
11351         /**
11352         * @event dblclick
11353         * Fires when a node is double clicked
11354         * @param {Node} node The node
11355         * @param {Roo.EventObject} e The event object
11356         */
11357         "dblclick":true,
11358         /**
11359         * @event contextmenu
11360         * Fires when a node is right clicked
11361         * @param {Node} node The node
11362         * @param {Roo.EventObject} e The event object
11363         */
11364         "contextmenu":true,
11365         /**
11366         * @event beforechildrenrendered
11367         * Fires right before the child nodes for a node are rendered
11368         * @param {Node} node The node
11369         */
11370         "beforechildrenrendered":true,
11371         /**
11372         * @event startdrag
11373         * Fires when a node starts being dragged
11374         * @param {Roo.tree.TreePanel} this
11375         * @param {Roo.tree.TreeNode} node
11376         * @param {event} e The raw browser event
11377         */ 
11378        "startdrag" : true,
11379        /**
11380         * @event enddrag
11381         * Fires when a drag operation is complete
11382         * @param {Roo.tree.TreePanel} this
11383         * @param {Roo.tree.TreeNode} node
11384         * @param {event} e The raw browser event
11385         */
11386        "enddrag" : true,
11387        /**
11388         * @event dragdrop
11389         * Fires when a dragged node is dropped on a valid DD target
11390         * @param {Roo.tree.TreePanel} this
11391         * @param {Roo.tree.TreeNode} node
11392         * @param {DD} dd The dd it was dropped on
11393         * @param {event} e The raw browser event
11394         */
11395        "dragdrop" : true,
11396        /**
11397         * @event beforenodedrop
11398         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11399         * passed to handlers has the following properties:<br />
11400         * <ul style="padding:5px;padding-left:16px;">
11401         * <li>tree - The TreePanel</li>
11402         * <li>target - The node being targeted for the drop</li>
11403         * <li>data - The drag data from the drag source</li>
11404         * <li>point - The point of the drop - append, above or below</li>
11405         * <li>source - The drag source</li>
11406         * <li>rawEvent - Raw mouse event</li>
11407         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11408         * to be inserted by setting them on this object.</li>
11409         * <li>cancel - Set this to true to cancel the drop.</li>
11410         * </ul>
11411         * @param {Object} dropEvent
11412         */
11413        "beforenodedrop" : true,
11414        /**
11415         * @event nodedrop
11416         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11417         * passed to handlers has the following properties:<br />
11418         * <ul style="padding:5px;padding-left:16px;">
11419         * <li>tree - The TreePanel</li>
11420         * <li>target - The node being targeted for the drop</li>
11421         * <li>data - The drag data from the drag source</li>
11422         * <li>point - The point of the drop - append, above or below</li>
11423         * <li>source - The drag source</li>
11424         * <li>rawEvent - Raw mouse event</li>
11425         * <li>dropNode - Dropped node(s).</li>
11426         * </ul>
11427         * @param {Object} dropEvent
11428         */
11429        "nodedrop" : true,
11430         /**
11431         * @event nodedragover
11432         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11433         * passed to handlers has the following properties:<br />
11434         * <ul style="padding:5px;padding-left:16px;">
11435         * <li>tree - The TreePanel</li>
11436         * <li>target - The node being targeted for the drop</li>
11437         * <li>data - The drag data from the drag source</li>
11438         * <li>point - The point of the drop - append, above or below</li>
11439         * <li>source - The drag source</li>
11440         * <li>rawEvent - Raw mouse event</li>
11441         * <li>dropNode - Drop node(s) provided by the source.</li>
11442         * <li>cancel - Set this to true to signal drop not allowed.</li>
11443         * </ul>
11444         * @param {Object} dragOverEvent
11445         */
11446        "nodedragover" : true,
11447        /**
11448         * @event appendnode
11449         * Fires when append node to the tree
11450         * @param {Roo.tree.TreePanel} this
11451         * @param {Roo.tree.TreeNode} node
11452         * @param {Number} index The index of the newly appended node
11453         */
11454        "appendnode" : true
11455         
11456     });
11457     if(this.singleExpand){
11458        this.on("beforeexpand", this.restrictExpand, this);
11459     }
11460     if (this.editor) {
11461         this.editor.tree = this;
11462         this.editor = Roo.factory(this.editor, Roo.tree);
11463     }
11464     
11465     if (this.selModel) {
11466         this.selModel = Roo.factory(this.selModel, Roo.tree);
11467     }
11468    
11469 };
11470 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11471     rootVisible : true,
11472     animate: Roo.enableFx,
11473     lines : true,
11474     enableDD : false,
11475     hlDrop : Roo.enableFx,
11476   
11477     renderer: false,
11478     
11479     rendererTip: false,
11480     // private
11481     restrictExpand : function(node){
11482         var p = node.parentNode;
11483         if(p){
11484             if(p.expandedChild && p.expandedChild.parentNode == p){
11485                 p.expandedChild.collapse();
11486             }
11487             p.expandedChild = node;
11488         }
11489     },
11490
11491     // private override
11492     setRootNode : function(node){
11493         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11494         if(!this.rootVisible){
11495             node.ui = new Roo.tree.RootTreeNodeUI(node);
11496         }
11497         return node;
11498     },
11499
11500     /**
11501      * Returns the container element for this TreePanel
11502      */
11503     getEl : function(){
11504         return this.el;
11505     },
11506
11507     /**
11508      * Returns the default TreeLoader for this TreePanel
11509      */
11510     getLoader : function(){
11511         return this.loader;
11512     },
11513
11514     /**
11515      * Expand all nodes
11516      */
11517     expandAll : function(){
11518         this.root.expand(true);
11519     },
11520
11521     /**
11522      * Collapse all nodes
11523      */
11524     collapseAll : function(){
11525         this.root.collapse(true);
11526     },
11527
11528     /**
11529      * Returns the selection model used by this TreePanel
11530      */
11531     getSelectionModel : function(){
11532         if(!this.selModel){
11533             this.selModel = new Roo.tree.DefaultSelectionModel();
11534         }
11535         return this.selModel;
11536     },
11537
11538     /**
11539      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11540      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11541      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11542      * @return {Array}
11543      */
11544     getChecked : function(a, startNode){
11545         startNode = startNode || this.root;
11546         var r = [];
11547         var f = function(){
11548             if(this.attributes.checked){
11549                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11550             }
11551         }
11552         startNode.cascade(f);
11553         return r;
11554     },
11555
11556     /**
11557      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11558      * @param {String} path
11559      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11560      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11561      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11562      */
11563     expandPath : function(path, attr, callback){
11564         attr = attr || "id";
11565         var keys = path.split(this.pathSeparator);
11566         var curNode = this.root;
11567         if(curNode.attributes[attr] != keys[1]){ // invalid root
11568             if(callback){
11569                 callback(false, null);
11570             }
11571             return;
11572         }
11573         var index = 1;
11574         var f = function(){
11575             if(++index == keys.length){
11576                 if(callback){
11577                     callback(true, curNode);
11578                 }
11579                 return;
11580             }
11581             var c = curNode.findChild(attr, keys[index]);
11582             if(!c){
11583                 if(callback){
11584                     callback(false, curNode);
11585                 }
11586                 return;
11587             }
11588             curNode = c;
11589             c.expand(false, false, f);
11590         };
11591         curNode.expand(false, false, f);
11592     },
11593
11594     /**
11595      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11596      * @param {String} path
11597      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11598      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11599      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11600      */
11601     selectPath : function(path, attr, callback){
11602         attr = attr || "id";
11603         var keys = path.split(this.pathSeparator);
11604         var v = keys.pop();
11605         if(keys.length > 0){
11606             var f = function(success, node){
11607                 if(success && node){
11608                     var n = node.findChild(attr, v);
11609                     if(n){
11610                         n.select();
11611                         if(callback){
11612                             callback(true, n);
11613                         }
11614                     }else if(callback){
11615                         callback(false, n);
11616                     }
11617                 }else{
11618                     if(callback){
11619                         callback(false, n);
11620                     }
11621                 }
11622             };
11623             this.expandPath(keys.join(this.pathSeparator), attr, f);
11624         }else{
11625             this.root.select();
11626             if(callback){
11627                 callback(true, this.root);
11628             }
11629         }
11630     },
11631
11632     getTreeEl : function(){
11633         return this.el;
11634     },
11635
11636     /**
11637      * Trigger rendering of this TreePanel
11638      */
11639     render : function(){
11640         if (this.innerCt) {
11641             return this; // stop it rendering more than once!!
11642         }
11643         
11644         this.innerCt = this.el.createChild({tag:"ul",
11645                cls:"x-tree-root-ct " +
11646                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11647
11648         if(this.containerScroll){
11649             Roo.dd.ScrollManager.register(this.el);
11650         }
11651         if((this.enableDD || this.enableDrop) && !this.dropZone){
11652            /**
11653             * The dropZone used by this tree if drop is enabled
11654             * @type Roo.tree.TreeDropZone
11655             */
11656              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11657                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11658            });
11659         }
11660         if((this.enableDD || this.enableDrag) && !this.dragZone){
11661            /**
11662             * The dragZone used by this tree if drag is enabled
11663             * @type Roo.tree.TreeDragZone
11664             */
11665             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11666                ddGroup: this.ddGroup || "TreeDD",
11667                scroll: this.ddScroll
11668            });
11669         }
11670         this.getSelectionModel().init(this);
11671         if (!this.root) {
11672             Roo.log("ROOT not set in tree");
11673             return this;
11674         }
11675         this.root.render();
11676         if(!this.rootVisible){
11677             this.root.renderChildren();
11678         }
11679         return this;
11680     }
11681 });/*
11682  * Based on:
11683  * Ext JS Library 1.1.1
11684  * Copyright(c) 2006-2007, Ext JS, LLC.
11685  *
11686  * Originally Released Under LGPL - original licence link has changed is not relivant.
11687  *
11688  * Fork - LGPL
11689  * <script type="text/javascript">
11690  */
11691  
11692
11693 /**
11694  * @class Roo.tree.DefaultSelectionModel
11695  * @extends Roo.util.Observable
11696  * The default single selection for a TreePanel.
11697  * @param {Object} cfg Configuration
11698  */
11699 Roo.tree.DefaultSelectionModel = function(cfg){
11700    this.selNode = null;
11701    
11702    
11703    
11704    this.addEvents({
11705        /**
11706         * @event selectionchange
11707         * Fires when the selected node changes
11708         * @param {DefaultSelectionModel} this
11709         * @param {TreeNode} node the new selection
11710         */
11711        "selectionchange" : true,
11712
11713        /**
11714         * @event beforeselect
11715         * Fires before the selected node changes, return false to cancel the change
11716         * @param {DefaultSelectionModel} this
11717         * @param {TreeNode} node the new selection
11718         * @param {TreeNode} node the old selection
11719         */
11720        "beforeselect" : true
11721    });
11722    
11723     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11724 };
11725
11726 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11727     init : function(tree){
11728         this.tree = tree;
11729         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11730         tree.on("click", this.onNodeClick, this);
11731     },
11732     
11733     onNodeClick : function(node, e){
11734         if (e.ctrlKey && this.selNode == node)  {
11735             this.unselect(node);
11736             return;
11737         }
11738         this.select(node);
11739     },
11740     
11741     /**
11742      * Select a node.
11743      * @param {TreeNode} node The node to select
11744      * @return {TreeNode} The selected node
11745      */
11746     select : function(node){
11747         var last = this.selNode;
11748         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11749             if(last){
11750                 last.ui.onSelectedChange(false);
11751             }
11752             this.selNode = node;
11753             node.ui.onSelectedChange(true);
11754             this.fireEvent("selectionchange", this, node, last);
11755         }
11756         return node;
11757     },
11758     
11759     /**
11760      * Deselect a node.
11761      * @param {TreeNode} node The node to unselect
11762      */
11763     unselect : function(node){
11764         if(this.selNode == node){
11765             this.clearSelections();
11766         }    
11767     },
11768     
11769     /**
11770      * Clear all selections
11771      */
11772     clearSelections : function(){
11773         var n = this.selNode;
11774         if(n){
11775             n.ui.onSelectedChange(false);
11776             this.selNode = null;
11777             this.fireEvent("selectionchange", this, null);
11778         }
11779         return n;
11780     },
11781     
11782     /**
11783      * Get the selected node
11784      * @return {TreeNode} The selected node
11785      */
11786     getSelectedNode : function(){
11787         return this.selNode;    
11788     },
11789     
11790     /**
11791      * Returns true if the node is selected
11792      * @param {TreeNode} node The node to check
11793      * @return {Boolean}
11794      */
11795     isSelected : function(node){
11796         return this.selNode == node;  
11797     },
11798
11799     /**
11800      * Selects the node above the selected node in the tree, intelligently walking the nodes
11801      * @return TreeNode The new selection
11802      */
11803     selectPrevious : function(){
11804         var s = this.selNode || this.lastSelNode;
11805         if(!s){
11806             return null;
11807         }
11808         var ps = s.previousSibling;
11809         if(ps){
11810             if(!ps.isExpanded() || ps.childNodes.length < 1){
11811                 return this.select(ps);
11812             } else{
11813                 var lc = ps.lastChild;
11814                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11815                     lc = lc.lastChild;
11816                 }
11817                 return this.select(lc);
11818             }
11819         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11820             return this.select(s.parentNode);
11821         }
11822         return null;
11823     },
11824
11825     /**
11826      * Selects the node above the selected node in the tree, intelligently walking the nodes
11827      * @return TreeNode The new selection
11828      */
11829     selectNext : function(){
11830         var s = this.selNode || this.lastSelNode;
11831         if(!s){
11832             return null;
11833         }
11834         if(s.firstChild && s.isExpanded()){
11835              return this.select(s.firstChild);
11836          }else if(s.nextSibling){
11837              return this.select(s.nextSibling);
11838          }else if(s.parentNode){
11839             var newS = null;
11840             s.parentNode.bubble(function(){
11841                 if(this.nextSibling){
11842                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11843                     return false;
11844                 }
11845             });
11846             return newS;
11847          }
11848         return null;
11849     },
11850
11851     onKeyDown : function(e){
11852         var s = this.selNode || this.lastSelNode;
11853         // undesirable, but required
11854         var sm = this;
11855         if(!s){
11856             return;
11857         }
11858         var k = e.getKey();
11859         switch(k){
11860              case e.DOWN:
11861                  e.stopEvent();
11862                  this.selectNext();
11863              break;
11864              case e.UP:
11865                  e.stopEvent();
11866                  this.selectPrevious();
11867              break;
11868              case e.RIGHT:
11869                  e.preventDefault();
11870                  if(s.hasChildNodes()){
11871                      if(!s.isExpanded()){
11872                          s.expand();
11873                      }else if(s.firstChild){
11874                          this.select(s.firstChild, e);
11875                      }
11876                  }
11877              break;
11878              case e.LEFT:
11879                  e.preventDefault();
11880                  if(s.hasChildNodes() && s.isExpanded()){
11881                      s.collapse();
11882                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11883                      this.select(s.parentNode, e);
11884                  }
11885              break;
11886         };
11887     }
11888 });
11889
11890 /**
11891  * @class Roo.tree.MultiSelectionModel
11892  * @extends Roo.util.Observable
11893  * Multi selection for a TreePanel.
11894  * @param {Object} cfg Configuration
11895  */
11896 Roo.tree.MultiSelectionModel = function(){
11897    this.selNodes = [];
11898    this.selMap = {};
11899    this.addEvents({
11900        /**
11901         * @event selectionchange
11902         * Fires when the selected nodes change
11903         * @param {MultiSelectionModel} this
11904         * @param {Array} nodes Array of the selected nodes
11905         */
11906        "selectionchange" : true
11907    });
11908    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11909    
11910 };
11911
11912 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11913     init : function(tree){
11914         this.tree = tree;
11915         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11916         tree.on("click", this.onNodeClick, this);
11917     },
11918     
11919     onNodeClick : function(node, e){
11920         this.select(node, e, e.ctrlKey);
11921     },
11922     
11923     /**
11924      * Select a node.
11925      * @param {TreeNode} node The node to select
11926      * @param {EventObject} e (optional) An event associated with the selection
11927      * @param {Boolean} keepExisting True to retain existing selections
11928      * @return {TreeNode} The selected node
11929      */
11930     select : function(node, e, keepExisting){
11931         if(keepExisting !== true){
11932             this.clearSelections(true);
11933         }
11934         if(this.isSelected(node)){
11935             this.lastSelNode = node;
11936             return node;
11937         }
11938         this.selNodes.push(node);
11939         this.selMap[node.id] = node;
11940         this.lastSelNode = node;
11941         node.ui.onSelectedChange(true);
11942         this.fireEvent("selectionchange", this, this.selNodes);
11943         return node;
11944     },
11945     
11946     /**
11947      * Deselect a node.
11948      * @param {TreeNode} node The node to unselect
11949      */
11950     unselect : function(node){
11951         if(this.selMap[node.id]){
11952             node.ui.onSelectedChange(false);
11953             var sn = this.selNodes;
11954             var index = -1;
11955             if(sn.indexOf){
11956                 index = sn.indexOf(node);
11957             }else{
11958                 for(var i = 0, len = sn.length; i < len; i++){
11959                     if(sn[i] == node){
11960                         index = i;
11961                         break;
11962                     }
11963                 }
11964             }
11965             if(index != -1){
11966                 this.selNodes.splice(index, 1);
11967             }
11968             delete this.selMap[node.id];
11969             this.fireEvent("selectionchange", this, this.selNodes);
11970         }
11971     },
11972     
11973     /**
11974      * Clear all selections
11975      */
11976     clearSelections : function(suppressEvent){
11977         var sn = this.selNodes;
11978         if(sn.length > 0){
11979             for(var i = 0, len = sn.length; i < len; i++){
11980                 sn[i].ui.onSelectedChange(false);
11981             }
11982             this.selNodes = [];
11983             this.selMap = {};
11984             if(suppressEvent !== true){
11985                 this.fireEvent("selectionchange", this, this.selNodes);
11986             }
11987         }
11988     },
11989     
11990     /**
11991      * Returns true if the node is selected
11992      * @param {TreeNode} node The node to check
11993      * @return {Boolean}
11994      */
11995     isSelected : function(node){
11996         return this.selMap[node.id] ? true : false;  
11997     },
11998     
11999     /**
12000      * Returns an array of the selected nodes
12001      * @return {Array}
12002      */
12003     getSelectedNodes : function(){
12004         return this.selNodes;    
12005     },
12006
12007     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12008
12009     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12010
12011     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12012 });/*
12013  * Based on:
12014  * Ext JS Library 1.1.1
12015  * Copyright(c) 2006-2007, Ext JS, LLC.
12016  *
12017  * Originally Released Under LGPL - original licence link has changed is not relivant.
12018  *
12019  * Fork - LGPL
12020  * <script type="text/javascript">
12021  */
12022  
12023 /**
12024  * @class Roo.tree.TreeNode
12025  * @extends Roo.data.Node
12026  * @cfg {String} text The text for this node
12027  * @cfg {Boolean} expanded true to start the node expanded
12028  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12029  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12030  * @cfg {Boolean} disabled true to start the node disabled
12031  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12032  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12033  * @cfg {String} cls A css class to be added to the node
12034  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12035  * @cfg {String} href URL of the link used for the node (defaults to #)
12036  * @cfg {String} hrefTarget target frame for the link
12037  * @cfg {String} qtip An Ext QuickTip for the node
12038  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12039  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12040  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12041  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12042  * (defaults to undefined with no checkbox rendered)
12043  * @constructor
12044  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12045  */
12046 Roo.tree.TreeNode = function(attributes){
12047     attributes = attributes || {};
12048     if(typeof attributes == "string"){
12049         attributes = {text: attributes};
12050     }
12051     this.childrenRendered = false;
12052     this.rendered = false;
12053     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12054     this.expanded = attributes.expanded === true;
12055     this.isTarget = attributes.isTarget !== false;
12056     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12057     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12058
12059     /**
12060      * Read-only. The text for this node. To change it use setText().
12061      * @type String
12062      */
12063     this.text = attributes.text;
12064     /**
12065      * True if this node is disabled.
12066      * @type Boolean
12067      */
12068     this.disabled = attributes.disabled === true;
12069
12070     this.addEvents({
12071         /**
12072         * @event textchange
12073         * Fires when the text for this node is changed
12074         * @param {Node} this This node
12075         * @param {String} text The new text
12076         * @param {String} oldText The old text
12077         */
12078         "textchange" : true,
12079         /**
12080         * @event beforeexpand
12081         * Fires before this node is expanded, return false to cancel.
12082         * @param {Node} this This node
12083         * @param {Boolean} deep
12084         * @param {Boolean} anim
12085         */
12086         "beforeexpand" : true,
12087         /**
12088         * @event beforecollapse
12089         * Fires before this node is collapsed, return false to cancel.
12090         * @param {Node} this This node
12091         * @param {Boolean} deep
12092         * @param {Boolean} anim
12093         */
12094         "beforecollapse" : true,
12095         /**
12096         * @event expand
12097         * Fires when this node is expanded
12098         * @param {Node} this This node
12099         */
12100         "expand" : true,
12101         /**
12102         * @event disabledchange
12103         * Fires when the disabled status of this node changes
12104         * @param {Node} this This node
12105         * @param {Boolean} disabled
12106         */
12107         "disabledchange" : true,
12108         /**
12109         * @event collapse
12110         * Fires when this node is collapsed
12111         * @param {Node} this This node
12112         */
12113         "collapse" : true,
12114         /**
12115         * @event beforeclick
12116         * Fires before click processing. Return false to cancel the default action.
12117         * @param {Node} this This node
12118         * @param {Roo.EventObject} e The event object
12119         */
12120         "beforeclick":true,
12121         /**
12122         * @event checkchange
12123         * Fires when a node with a checkbox's checked property changes
12124         * @param {Node} this This node
12125         * @param {Boolean} checked
12126         */
12127         "checkchange":true,
12128         /**
12129         * @event click
12130         * Fires when this node is clicked
12131         * @param {Node} this This node
12132         * @param {Roo.EventObject} e The event object
12133         */
12134         "click":true,
12135         /**
12136         * @event dblclick
12137         * Fires when this node is double clicked
12138         * @param {Node} this This node
12139         * @param {Roo.EventObject} e The event object
12140         */
12141         "dblclick":true,
12142         /**
12143         * @event contextmenu
12144         * Fires when this node is right clicked
12145         * @param {Node} this This node
12146         * @param {Roo.EventObject} e The event object
12147         */
12148         "contextmenu":true,
12149         /**
12150         * @event beforechildrenrendered
12151         * Fires right before the child nodes for this node are rendered
12152         * @param {Node} this This node
12153         */
12154         "beforechildrenrendered":true
12155     });
12156
12157     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12158
12159     /**
12160      * Read-only. The UI for this node
12161      * @type TreeNodeUI
12162      */
12163     this.ui = new uiClass(this);
12164     
12165     // finally support items[]
12166     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12167         return;
12168     }
12169     
12170     
12171     Roo.each(this.attributes.items, function(c) {
12172         this.appendChild(Roo.factory(c,Roo.Tree));
12173     }, this);
12174     delete this.attributes.items;
12175     
12176     
12177     
12178 };
12179 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12180     preventHScroll: true,
12181     /**
12182      * Returns true if this node is expanded
12183      * @return {Boolean}
12184      */
12185     isExpanded : function(){
12186         return this.expanded;
12187     },
12188
12189     /**
12190      * Returns the UI object for this node
12191      * @return {TreeNodeUI}
12192      */
12193     getUI : function(){
12194         return this.ui;
12195     },
12196
12197     // private override
12198     setFirstChild : function(node){
12199         var of = this.firstChild;
12200         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12201         if(this.childrenRendered && of && node != of){
12202             of.renderIndent(true, true);
12203         }
12204         if(this.rendered){
12205             this.renderIndent(true, true);
12206         }
12207     },
12208
12209     // private override
12210     setLastChild : function(node){
12211         var ol = this.lastChild;
12212         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12213         if(this.childrenRendered && ol && node != ol){
12214             ol.renderIndent(true, true);
12215         }
12216         if(this.rendered){
12217             this.renderIndent(true, true);
12218         }
12219     },
12220
12221     // these methods are overridden to provide lazy rendering support
12222     // private override
12223     appendChild : function()
12224     {
12225         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12226         if(node && this.childrenRendered){
12227             node.render();
12228         }
12229         this.ui.updateExpandIcon();
12230         return node;
12231     },
12232
12233     // private override
12234     removeChild : function(node){
12235         this.ownerTree.getSelectionModel().unselect(node);
12236         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12237         // if it's been rendered remove dom node
12238         if(this.childrenRendered){
12239             node.ui.remove();
12240         }
12241         if(this.childNodes.length < 1){
12242             this.collapse(false, false);
12243         }else{
12244             this.ui.updateExpandIcon();
12245         }
12246         if(!this.firstChild) {
12247             this.childrenRendered = false;
12248         }
12249         return node;
12250     },
12251
12252     // private override
12253     insertBefore : function(node, refNode){
12254         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12255         if(newNode && refNode && this.childrenRendered){
12256             node.render();
12257         }
12258         this.ui.updateExpandIcon();
12259         return newNode;
12260     },
12261
12262     /**
12263      * Sets the text for this node
12264      * @param {String} text
12265      */
12266     setText : function(text){
12267         var oldText = this.text;
12268         this.text = text;
12269         this.attributes.text = text;
12270         if(this.rendered){ // event without subscribing
12271             this.ui.onTextChange(this, text, oldText);
12272         }
12273         this.fireEvent("textchange", this, text, oldText);
12274     },
12275
12276     /**
12277      * Triggers selection of this node
12278      */
12279     select : function(){
12280         this.getOwnerTree().getSelectionModel().select(this);
12281     },
12282
12283     /**
12284      * Triggers deselection of this node
12285      */
12286     unselect : function(){
12287         this.getOwnerTree().getSelectionModel().unselect(this);
12288     },
12289
12290     /**
12291      * Returns true if this node is selected
12292      * @return {Boolean}
12293      */
12294     isSelected : function(){
12295         return this.getOwnerTree().getSelectionModel().isSelected(this);
12296     },
12297
12298     /**
12299      * Expand this node.
12300      * @param {Boolean} deep (optional) True to expand all children as well
12301      * @param {Boolean} anim (optional) false to cancel the default animation
12302      * @param {Function} callback (optional) A callback to be called when
12303      * expanding this node completes (does not wait for deep expand to complete).
12304      * Called with 1 parameter, this node.
12305      */
12306     expand : function(deep, anim, callback){
12307         if(!this.expanded){
12308             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12309                 return;
12310             }
12311             if(!this.childrenRendered){
12312                 this.renderChildren();
12313             }
12314             this.expanded = true;
12315             
12316             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12317                 this.ui.animExpand(function(){
12318                     this.fireEvent("expand", this);
12319                     if(typeof callback == "function"){
12320                         callback(this);
12321                     }
12322                     if(deep === true){
12323                         this.expandChildNodes(true);
12324                     }
12325                 }.createDelegate(this));
12326                 return;
12327             }else{
12328                 this.ui.expand();
12329                 this.fireEvent("expand", this);
12330                 if(typeof callback == "function"){
12331                     callback(this);
12332                 }
12333             }
12334         }else{
12335            if(typeof callback == "function"){
12336                callback(this);
12337            }
12338         }
12339         if(deep === true){
12340             this.expandChildNodes(true);
12341         }
12342     },
12343
12344     isHiddenRoot : function(){
12345         return this.isRoot && !this.getOwnerTree().rootVisible;
12346     },
12347
12348     /**
12349      * Collapse this node.
12350      * @param {Boolean} deep (optional) True to collapse all children as well
12351      * @param {Boolean} anim (optional) false to cancel the default animation
12352      */
12353     collapse : function(deep, anim){
12354         if(this.expanded && !this.isHiddenRoot()){
12355             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12356                 return;
12357             }
12358             this.expanded = false;
12359             if((this.getOwnerTree().animate && anim !== false) || anim){
12360                 this.ui.animCollapse(function(){
12361                     this.fireEvent("collapse", this);
12362                     if(deep === true){
12363                         this.collapseChildNodes(true);
12364                     }
12365                 }.createDelegate(this));
12366                 return;
12367             }else{
12368                 this.ui.collapse();
12369                 this.fireEvent("collapse", this);
12370             }
12371         }
12372         if(deep === true){
12373             var cs = this.childNodes;
12374             for(var i = 0, len = cs.length; i < len; i++) {
12375                 cs[i].collapse(true, false);
12376             }
12377         }
12378     },
12379
12380     // private
12381     delayedExpand : function(delay){
12382         if(!this.expandProcId){
12383             this.expandProcId = this.expand.defer(delay, this);
12384         }
12385     },
12386
12387     // private
12388     cancelExpand : function(){
12389         if(this.expandProcId){
12390             clearTimeout(this.expandProcId);
12391         }
12392         this.expandProcId = false;
12393     },
12394
12395     /**
12396      * Toggles expanded/collapsed state of the node
12397      */
12398     toggle : function(){
12399         if(this.expanded){
12400             this.collapse();
12401         }else{
12402             this.expand();
12403         }
12404     },
12405
12406     /**
12407      * Ensures all parent nodes are expanded
12408      */
12409     ensureVisible : function(callback){
12410         var tree = this.getOwnerTree();
12411         tree.expandPath(this.parentNode.getPath(), false, function(){
12412             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12413             Roo.callback(callback);
12414         }.createDelegate(this));
12415     },
12416
12417     /**
12418      * Expand all child nodes
12419      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12420      */
12421     expandChildNodes : function(deep){
12422         var cs = this.childNodes;
12423         for(var i = 0, len = cs.length; i < len; i++) {
12424                 cs[i].expand(deep);
12425         }
12426     },
12427
12428     /**
12429      * Collapse all child nodes
12430      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12431      */
12432     collapseChildNodes : function(deep){
12433         var cs = this.childNodes;
12434         for(var i = 0, len = cs.length; i < len; i++) {
12435                 cs[i].collapse(deep);
12436         }
12437     },
12438
12439     /**
12440      * Disables this node
12441      */
12442     disable : function(){
12443         this.disabled = true;
12444         this.unselect();
12445         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12446             this.ui.onDisableChange(this, true);
12447         }
12448         this.fireEvent("disabledchange", this, true);
12449     },
12450
12451     /**
12452      * Enables this node
12453      */
12454     enable : function(){
12455         this.disabled = false;
12456         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12457             this.ui.onDisableChange(this, false);
12458         }
12459         this.fireEvent("disabledchange", this, false);
12460     },
12461
12462     // private
12463     renderChildren : function(suppressEvent){
12464         if(suppressEvent !== false){
12465             this.fireEvent("beforechildrenrendered", this);
12466         }
12467         var cs = this.childNodes;
12468         for(var i = 0, len = cs.length; i < len; i++){
12469             cs[i].render(true);
12470         }
12471         this.childrenRendered = true;
12472     },
12473
12474     // private
12475     sort : function(fn, scope){
12476         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12477         if(this.childrenRendered){
12478             var cs = this.childNodes;
12479             for(var i = 0, len = cs.length; i < len; i++){
12480                 cs[i].render(true);
12481             }
12482         }
12483     },
12484
12485     // private
12486     render : function(bulkRender){
12487         this.ui.render(bulkRender);
12488         if(!this.rendered){
12489             this.rendered = true;
12490             if(this.expanded){
12491                 this.expanded = false;
12492                 this.expand(false, false);
12493             }
12494         }
12495     },
12496
12497     // private
12498     renderIndent : function(deep, refresh){
12499         if(refresh){
12500             this.ui.childIndent = null;
12501         }
12502         this.ui.renderIndent();
12503         if(deep === true && this.childrenRendered){
12504             var cs = this.childNodes;
12505             for(var i = 0, len = cs.length; i < len; i++){
12506                 cs[i].renderIndent(true, refresh);
12507             }
12508         }
12509     }
12510 });/*
12511  * Based on:
12512  * Ext JS Library 1.1.1
12513  * Copyright(c) 2006-2007, Ext JS, LLC.
12514  *
12515  * Originally Released Under LGPL - original licence link has changed is not relivant.
12516  *
12517  * Fork - LGPL
12518  * <script type="text/javascript">
12519  */
12520  
12521 /**
12522  * @class Roo.tree.AsyncTreeNode
12523  * @extends Roo.tree.TreeNode
12524  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12525  * @constructor
12526  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12527  */
12528  Roo.tree.AsyncTreeNode = function(config){
12529     this.loaded = false;
12530     this.loading = false;
12531     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12532     /**
12533     * @event beforeload
12534     * Fires before this node is loaded, return false to cancel
12535     * @param {Node} this This node
12536     */
12537     this.addEvents({'beforeload':true, 'load': true});
12538     /**
12539     * @event load
12540     * Fires when this node is loaded
12541     * @param {Node} this This node
12542     */
12543     /**
12544      * The loader used by this node (defaults to using the tree's defined loader)
12545      * @type TreeLoader
12546      * @property loader
12547      */
12548 };
12549 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12550     expand : function(deep, anim, callback){
12551         if(this.loading){ // if an async load is already running, waiting til it's done
12552             var timer;
12553             var f = function(){
12554                 if(!this.loading){ // done loading
12555                     clearInterval(timer);
12556                     this.expand(deep, anim, callback);
12557                 }
12558             }.createDelegate(this);
12559             timer = setInterval(f, 200);
12560             return;
12561         }
12562         if(!this.loaded){
12563             if(this.fireEvent("beforeload", this) === false){
12564                 return;
12565             }
12566             this.loading = true;
12567             this.ui.beforeLoad(this);
12568             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12569             if(loader){
12570                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12571                 return;
12572             }
12573         }
12574         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12575     },
12576     
12577     /**
12578      * Returns true if this node is currently loading
12579      * @return {Boolean}
12580      */
12581     isLoading : function(){
12582         return this.loading;  
12583     },
12584     
12585     loadComplete : function(deep, anim, callback){
12586         this.loading = false;
12587         this.loaded = true;
12588         this.ui.afterLoad(this);
12589         this.fireEvent("load", this);
12590         this.expand(deep, anim, callback);
12591     },
12592     
12593     /**
12594      * Returns true if this node has been loaded
12595      * @return {Boolean}
12596      */
12597     isLoaded : function(){
12598         return this.loaded;
12599     },
12600     
12601     hasChildNodes : function(){
12602         if(!this.isLeaf() && !this.loaded){
12603             return true;
12604         }else{
12605             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12606         }
12607     },
12608
12609     /**
12610      * Trigger a reload for this node
12611      * @param {Function} callback
12612      */
12613     reload : function(callback){
12614         this.collapse(false, false);
12615         while(this.firstChild){
12616             this.removeChild(this.firstChild);
12617         }
12618         this.childrenRendered = false;
12619         this.loaded = false;
12620         if(this.isHiddenRoot()){
12621             this.expanded = false;
12622         }
12623         this.expand(false, false, callback);
12624     }
12625 });/*
12626  * Based on:
12627  * Ext JS Library 1.1.1
12628  * Copyright(c) 2006-2007, Ext JS, LLC.
12629  *
12630  * Originally Released Under LGPL - original licence link has changed is not relivant.
12631  *
12632  * Fork - LGPL
12633  * <script type="text/javascript">
12634  */
12635  
12636 /**
12637  * @class Roo.tree.TreeNodeUI
12638  * @constructor
12639  * @param {Object} node The node to render
12640  * The TreeNode UI implementation is separate from the
12641  * tree implementation. Unless you are customizing the tree UI,
12642  * you should never have to use this directly.
12643  */
12644 Roo.tree.TreeNodeUI = function(node){
12645     this.node = node;
12646     this.rendered = false;
12647     this.animating = false;
12648     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12649 };
12650
12651 Roo.tree.TreeNodeUI.prototype = {
12652     removeChild : function(node){
12653         if(this.rendered){
12654             this.ctNode.removeChild(node.ui.getEl());
12655         }
12656     },
12657
12658     beforeLoad : function(){
12659          this.addClass("x-tree-node-loading");
12660     },
12661
12662     afterLoad : function(){
12663          this.removeClass("x-tree-node-loading");
12664     },
12665
12666     onTextChange : function(node, text, oldText){
12667         if(this.rendered){
12668             this.textNode.innerHTML = text;
12669         }
12670     },
12671
12672     onDisableChange : function(node, state){
12673         this.disabled = state;
12674         if(state){
12675             this.addClass("x-tree-node-disabled");
12676         }else{
12677             this.removeClass("x-tree-node-disabled");
12678         }
12679     },
12680
12681     onSelectedChange : function(state){
12682         if(state){
12683             this.focus();
12684             this.addClass("x-tree-selected");
12685         }else{
12686             //this.blur();
12687             this.removeClass("x-tree-selected");
12688         }
12689     },
12690
12691     onMove : function(tree, node, oldParent, newParent, index, refNode){
12692         this.childIndent = null;
12693         if(this.rendered){
12694             var targetNode = newParent.ui.getContainer();
12695             if(!targetNode){//target not rendered
12696                 this.holder = document.createElement("div");
12697                 this.holder.appendChild(this.wrap);
12698                 return;
12699             }
12700             var insertBefore = refNode ? refNode.ui.getEl() : null;
12701             if(insertBefore){
12702                 targetNode.insertBefore(this.wrap, insertBefore);
12703             }else{
12704                 targetNode.appendChild(this.wrap);
12705             }
12706             this.node.renderIndent(true);
12707         }
12708     },
12709
12710     addClass : function(cls){
12711         if(this.elNode){
12712             Roo.fly(this.elNode).addClass(cls);
12713         }
12714     },
12715
12716     removeClass : function(cls){
12717         if(this.elNode){
12718             Roo.fly(this.elNode).removeClass(cls);
12719         }
12720     },
12721
12722     remove : function(){
12723         if(this.rendered){
12724             this.holder = document.createElement("div");
12725             this.holder.appendChild(this.wrap);
12726         }
12727     },
12728
12729     fireEvent : function(){
12730         return this.node.fireEvent.apply(this.node, arguments);
12731     },
12732
12733     initEvents : function(){
12734         this.node.on("move", this.onMove, this);
12735         var E = Roo.EventManager;
12736         var a = this.anchor;
12737
12738         var el = Roo.fly(a, '_treeui');
12739
12740         if(Roo.isOpera){ // opera render bug ignores the CSS
12741             el.setStyle("text-decoration", "none");
12742         }
12743
12744         el.on("click", this.onClick, this);
12745         el.on("dblclick", this.onDblClick, this);
12746
12747         if(this.checkbox){
12748             Roo.EventManager.on(this.checkbox,
12749                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12750         }
12751
12752         el.on("contextmenu", this.onContextMenu, this);
12753
12754         var icon = Roo.fly(this.iconNode);
12755         icon.on("click", this.onClick, this);
12756         icon.on("dblclick", this.onDblClick, this);
12757         icon.on("contextmenu", this.onContextMenu, this);
12758         E.on(this.ecNode, "click", this.ecClick, this, true);
12759
12760         if(this.node.disabled){
12761             this.addClass("x-tree-node-disabled");
12762         }
12763         if(this.node.hidden){
12764             this.addClass("x-tree-node-disabled");
12765         }
12766         var ot = this.node.getOwnerTree();
12767         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12768         if(dd && (!this.node.isRoot || ot.rootVisible)){
12769             Roo.dd.Registry.register(this.elNode, {
12770                 node: this.node,
12771                 handles: this.getDDHandles(),
12772                 isHandle: false
12773             });
12774         }
12775     },
12776
12777     getDDHandles : function(){
12778         return [this.iconNode, this.textNode];
12779     },
12780
12781     hide : function(){
12782         if(this.rendered){
12783             this.wrap.style.display = "none";
12784         }
12785     },
12786
12787     show : function(){
12788         if(this.rendered){
12789             this.wrap.style.display = "";
12790         }
12791     },
12792
12793     onContextMenu : function(e){
12794         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12795             e.preventDefault();
12796             this.focus();
12797             this.fireEvent("contextmenu", this.node, e);
12798         }
12799     },
12800
12801     onClick : function(e){
12802         if(this.dropping){
12803             e.stopEvent();
12804             return;
12805         }
12806         if(this.fireEvent("beforeclick", this.node, e) !== false){
12807             if(!this.disabled && this.node.attributes.href){
12808                 this.fireEvent("click", this.node, e);
12809                 return;
12810             }
12811             e.preventDefault();
12812             if(this.disabled){
12813                 return;
12814             }
12815
12816             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12817                 this.node.toggle();
12818             }
12819
12820             this.fireEvent("click", this.node, e);
12821         }else{
12822             e.stopEvent();
12823         }
12824     },
12825
12826     onDblClick : function(e){
12827         e.preventDefault();
12828         if(this.disabled){
12829             return;
12830         }
12831         if(this.checkbox){
12832             this.toggleCheck();
12833         }
12834         if(!this.animating && this.node.hasChildNodes()){
12835             this.node.toggle();
12836         }
12837         this.fireEvent("dblclick", this.node, e);
12838     },
12839
12840     onCheckChange : function(){
12841         var checked = this.checkbox.checked;
12842         this.node.attributes.checked = checked;
12843         this.fireEvent('checkchange', this.node, checked);
12844     },
12845
12846     ecClick : function(e){
12847         if(!this.animating && this.node.hasChildNodes()){
12848             this.node.toggle();
12849         }
12850     },
12851
12852     startDrop : function(){
12853         this.dropping = true;
12854     },
12855
12856     // delayed drop so the click event doesn't get fired on a drop
12857     endDrop : function(){
12858        setTimeout(function(){
12859            this.dropping = false;
12860        }.createDelegate(this), 50);
12861     },
12862
12863     expand : function(){
12864         this.updateExpandIcon();
12865         this.ctNode.style.display = "";
12866     },
12867
12868     focus : function(){
12869         if(!this.node.preventHScroll){
12870             try{this.anchor.focus();
12871             }catch(e){}
12872         }else if(!Roo.isIE){
12873             try{
12874                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12875                 var l = noscroll.scrollLeft;
12876                 this.anchor.focus();
12877                 noscroll.scrollLeft = l;
12878             }catch(e){}
12879         }
12880     },
12881
12882     toggleCheck : function(value){
12883         var cb = this.checkbox;
12884         if(cb){
12885             cb.checked = (value === undefined ? !cb.checked : value);
12886         }
12887     },
12888
12889     blur : function(){
12890         try{
12891             this.anchor.blur();
12892         }catch(e){}
12893     },
12894
12895     animExpand : function(callback){
12896         var ct = Roo.get(this.ctNode);
12897         ct.stopFx();
12898         if(!this.node.hasChildNodes()){
12899             this.updateExpandIcon();
12900             this.ctNode.style.display = "";
12901             Roo.callback(callback);
12902             return;
12903         }
12904         this.animating = true;
12905         this.updateExpandIcon();
12906
12907         ct.slideIn('t', {
12908            callback : function(){
12909                this.animating = false;
12910                Roo.callback(callback);
12911             },
12912             scope: this,
12913             duration: this.node.ownerTree.duration || .25
12914         });
12915     },
12916
12917     highlight : function(){
12918         var tree = this.node.getOwnerTree();
12919         Roo.fly(this.wrap).highlight(
12920             tree.hlColor || "C3DAF9",
12921             {endColor: tree.hlBaseColor}
12922         );
12923     },
12924
12925     collapse : function(){
12926         this.updateExpandIcon();
12927         this.ctNode.style.display = "none";
12928     },
12929
12930     animCollapse : function(callback){
12931         var ct = Roo.get(this.ctNode);
12932         ct.enableDisplayMode('block');
12933         ct.stopFx();
12934
12935         this.animating = true;
12936         this.updateExpandIcon();
12937
12938         ct.slideOut('t', {
12939             callback : function(){
12940                this.animating = false;
12941                Roo.callback(callback);
12942             },
12943             scope: this,
12944             duration: this.node.ownerTree.duration || .25
12945         });
12946     },
12947
12948     getContainer : function(){
12949         return this.ctNode;
12950     },
12951
12952     getEl : function(){
12953         return this.wrap;
12954     },
12955
12956     appendDDGhost : function(ghostNode){
12957         ghostNode.appendChild(this.elNode.cloneNode(true));
12958     },
12959
12960     getDDRepairXY : function(){
12961         return Roo.lib.Dom.getXY(this.iconNode);
12962     },
12963
12964     onRender : function(){
12965         this.render();
12966     },
12967
12968     render : function(bulkRender){
12969         var n = this.node, a = n.attributes;
12970         var targetNode = n.parentNode ?
12971               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12972
12973         if(!this.rendered){
12974             this.rendered = true;
12975
12976             this.renderElements(n, a, targetNode, bulkRender);
12977
12978             if(a.qtip){
12979                if(this.textNode.setAttributeNS){
12980                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12981                    if(a.qtipTitle){
12982                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12983                    }
12984                }else{
12985                    this.textNode.setAttribute("ext:qtip", a.qtip);
12986                    if(a.qtipTitle){
12987                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12988                    }
12989                }
12990             }else if(a.qtipCfg){
12991                 a.qtipCfg.target = Roo.id(this.textNode);
12992                 Roo.QuickTips.register(a.qtipCfg);
12993             }
12994             this.initEvents();
12995             if(!this.node.expanded){
12996                 this.updateExpandIcon();
12997             }
12998         }else{
12999             if(bulkRender === true) {
13000                 targetNode.appendChild(this.wrap);
13001             }
13002         }
13003     },
13004
13005     renderElements : function(n, a, targetNode, bulkRender)
13006     {
13007         // add some indent caching, this helps performance when rendering a large tree
13008         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13009         var t = n.getOwnerTree();
13010         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13011         if (typeof(n.attributes.html) != 'undefined') {
13012             txt = n.attributes.html;
13013         }
13014         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13015         var cb = typeof a.checked == 'boolean';
13016         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13017         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13018             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13019             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13020             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13021             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13022             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13023              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13024                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13025             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13026             "</li>"];
13027
13028         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13029             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13030                                 n.nextSibling.ui.getEl(), buf.join(""));
13031         }else{
13032             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13033         }
13034
13035         this.elNode = this.wrap.childNodes[0];
13036         this.ctNode = this.wrap.childNodes[1];
13037         var cs = this.elNode.childNodes;
13038         this.indentNode = cs[0];
13039         this.ecNode = cs[1];
13040         this.iconNode = cs[2];
13041         var index = 3;
13042         if(cb){
13043             this.checkbox = cs[3];
13044             index++;
13045         }
13046         this.anchor = cs[index];
13047         this.textNode = cs[index].firstChild;
13048     },
13049
13050     getAnchor : function(){
13051         return this.anchor;
13052     },
13053
13054     getTextEl : function(){
13055         return this.textNode;
13056     },
13057
13058     getIconEl : function(){
13059         return this.iconNode;
13060     },
13061
13062     isChecked : function(){
13063         return this.checkbox ? this.checkbox.checked : false;
13064     },
13065
13066     updateExpandIcon : function(){
13067         if(this.rendered){
13068             var n = this.node, c1, c2;
13069             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13070             var hasChild = n.hasChildNodes();
13071             if(hasChild){
13072                 if(n.expanded){
13073                     cls += "-minus";
13074                     c1 = "x-tree-node-collapsed";
13075                     c2 = "x-tree-node-expanded";
13076                 }else{
13077                     cls += "-plus";
13078                     c1 = "x-tree-node-expanded";
13079                     c2 = "x-tree-node-collapsed";
13080                 }
13081                 if(this.wasLeaf){
13082                     this.removeClass("x-tree-node-leaf");
13083                     this.wasLeaf = false;
13084                 }
13085                 if(this.c1 != c1 || this.c2 != c2){
13086                     Roo.fly(this.elNode).replaceClass(c1, c2);
13087                     this.c1 = c1; this.c2 = c2;
13088                 }
13089             }else{
13090                 // this changes non-leafs into leafs if they have no children.
13091                 // it's not very rational behaviour..
13092                 
13093                 if(!this.wasLeaf && this.node.leaf){
13094                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13095                     delete this.c1;
13096                     delete this.c2;
13097                     this.wasLeaf = true;
13098                 }
13099             }
13100             var ecc = "x-tree-ec-icon "+cls;
13101             if(this.ecc != ecc){
13102                 this.ecNode.className = ecc;
13103                 this.ecc = ecc;
13104             }
13105         }
13106     },
13107
13108     getChildIndent : function(){
13109         if(!this.childIndent){
13110             var buf = [];
13111             var p = this.node;
13112             while(p){
13113                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13114                     if(!p.isLast()) {
13115                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13116                     } else {
13117                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13118                     }
13119                 }
13120                 p = p.parentNode;
13121             }
13122             this.childIndent = buf.join("");
13123         }
13124         return this.childIndent;
13125     },
13126
13127     renderIndent : function(){
13128         if(this.rendered){
13129             var indent = "";
13130             var p = this.node.parentNode;
13131             if(p){
13132                 indent = p.ui.getChildIndent();
13133             }
13134             if(this.indentMarkup != indent){ // don't rerender if not required
13135                 this.indentNode.innerHTML = indent;
13136                 this.indentMarkup = indent;
13137             }
13138             this.updateExpandIcon();
13139         }
13140     }
13141 };
13142
13143 Roo.tree.RootTreeNodeUI = function(){
13144     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13145 };
13146 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13147     render : function(){
13148         if(!this.rendered){
13149             var targetNode = this.node.ownerTree.innerCt.dom;
13150             this.node.expanded = true;
13151             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13152             this.wrap = this.ctNode = targetNode.firstChild;
13153         }
13154     },
13155     collapse : function(){
13156     },
13157     expand : function(){
13158     }
13159 });/*
13160  * Based on:
13161  * Ext JS Library 1.1.1
13162  * Copyright(c) 2006-2007, Ext JS, LLC.
13163  *
13164  * Originally Released Under LGPL - original licence link has changed is not relivant.
13165  *
13166  * Fork - LGPL
13167  * <script type="text/javascript">
13168  */
13169 /**
13170  * @class Roo.tree.TreeLoader
13171  * @extends Roo.util.Observable
13172  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13173  * nodes from a specified URL. The response must be a javascript Array definition
13174  * who's elements are node definition objects. eg:
13175  * <pre><code>
13176 {  success : true,
13177    data :      [
13178    
13179     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13180     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13181     ]
13182 }
13183
13184
13185 </code></pre>
13186  * <br><br>
13187  * The old style respose with just an array is still supported, but not recommended.
13188  * <br><br>
13189  *
13190  * A server request is sent, and child nodes are loaded only when a node is expanded.
13191  * The loading node's id is passed to the server under the parameter name "node" to
13192  * enable the server to produce the correct child nodes.
13193  * <br><br>
13194  * To pass extra parameters, an event handler may be attached to the "beforeload"
13195  * event, and the parameters specified in the TreeLoader's baseParams property:
13196  * <pre><code>
13197     myTreeLoader.on("beforeload", function(treeLoader, node) {
13198         this.baseParams.category = node.attributes.category;
13199     }, this);
13200     
13201 </code></pre>
13202  *
13203  * This would pass an HTTP parameter called "category" to the server containing
13204  * the value of the Node's "category" attribute.
13205  * @constructor
13206  * Creates a new Treeloader.
13207  * @param {Object} config A config object containing config properties.
13208  */
13209 Roo.tree.TreeLoader = function(config){
13210     this.baseParams = {};
13211     this.requestMethod = "POST";
13212     Roo.apply(this, config);
13213
13214     this.addEvents({
13215     
13216         /**
13217          * @event beforeload
13218          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13219          * @param {Object} This TreeLoader object.
13220          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13221          * @param {Object} callback The callback function specified in the {@link #load} call.
13222          */
13223         beforeload : true,
13224         /**
13225          * @event load
13226          * Fires when the node has been successfuly loaded.
13227          * @param {Object} This TreeLoader object.
13228          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13229          * @param {Object} response The response object containing the data from the server.
13230          */
13231         load : true,
13232         /**
13233          * @event loadexception
13234          * Fires if the network request failed.
13235          * @param {Object} This TreeLoader object.
13236          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13237          * @param {Object} response The response object containing the data from the server.
13238          */
13239         loadexception : true,
13240         /**
13241          * @event create
13242          * Fires before a node is created, enabling you to return custom Node types 
13243          * @param {Object} This TreeLoader object.
13244          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13245          */
13246         create : true
13247     });
13248
13249     Roo.tree.TreeLoader.superclass.constructor.call(this);
13250 };
13251
13252 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13253     /**
13254     * @cfg {String} dataUrl The URL from which to request a Json string which
13255     * specifies an array of node definition object representing the child nodes
13256     * to be loaded.
13257     */
13258     /**
13259     * @cfg {String} requestMethod either GET or POST
13260     * defaults to POST (due to BC)
13261     * to be loaded.
13262     */
13263     /**
13264     * @cfg {Object} baseParams (optional) An object containing properties which
13265     * specify HTTP parameters to be passed to each request for child nodes.
13266     */
13267     /**
13268     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13269     * created by this loader. If the attributes sent by the server have an attribute in this object,
13270     * they take priority.
13271     */
13272     /**
13273     * @cfg {Object} uiProviders (optional) An object containing properties which
13274     * 
13275     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13276     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13277     * <i>uiProvider</i> attribute of a returned child node is a string rather
13278     * than a reference to a TreeNodeUI implementation, this that string value
13279     * is used as a property name in the uiProviders object. You can define the provider named
13280     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13281     */
13282     uiProviders : {},
13283
13284     /**
13285     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13286     * child nodes before loading.
13287     */
13288     clearOnLoad : true,
13289
13290     /**
13291     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13292     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13293     * Grid query { data : [ .....] }
13294     */
13295     
13296     root : false,
13297      /**
13298     * @cfg {String} queryParam (optional) 
13299     * Name of the query as it will be passed on the querystring (defaults to 'node')
13300     * eg. the request will be ?node=[id]
13301     */
13302     
13303     
13304     queryParam: false,
13305     
13306     /**
13307      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13308      * This is called automatically when a node is expanded, but may be used to reload
13309      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13310      * @param {Roo.tree.TreeNode} node
13311      * @param {Function} callback
13312      */
13313     load : function(node, callback){
13314         if(this.clearOnLoad){
13315             while(node.firstChild){
13316                 node.removeChild(node.firstChild);
13317             }
13318         }
13319         if(node.attributes.children){ // preloaded json children
13320             var cs = node.attributes.children;
13321             for(var i = 0, len = cs.length; i < len; i++){
13322                 node.appendChild(this.createNode(cs[i]));
13323             }
13324             if(typeof callback == "function"){
13325                 callback();
13326             }
13327         }else if(this.dataUrl){
13328             this.requestData(node, callback);
13329         }
13330     },
13331
13332     getParams: function(node){
13333         var buf = [], bp = this.baseParams;
13334         for(var key in bp){
13335             if(typeof bp[key] != "function"){
13336                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13337             }
13338         }
13339         var n = this.queryParam === false ? 'node' : this.queryParam;
13340         buf.push(n + "=", encodeURIComponent(node.id));
13341         return buf.join("");
13342     },
13343
13344     requestData : function(node, callback){
13345         if(this.fireEvent("beforeload", this, node, callback) !== false){
13346             this.transId = Roo.Ajax.request({
13347                 method:this.requestMethod,
13348                 url: this.dataUrl||this.url,
13349                 success: this.handleResponse,
13350                 failure: this.handleFailure,
13351                 scope: this,
13352                 argument: {callback: callback, node: node},
13353                 params: this.getParams(node)
13354             });
13355         }else{
13356             // if the load is cancelled, make sure we notify
13357             // the node that we are done
13358             if(typeof callback == "function"){
13359                 callback();
13360             }
13361         }
13362     },
13363
13364     isLoading : function(){
13365         return this.transId ? true : false;
13366     },
13367
13368     abort : function(){
13369         if(this.isLoading()){
13370             Roo.Ajax.abort(this.transId);
13371         }
13372     },
13373
13374     // private
13375     createNode : function(attr)
13376     {
13377         // apply baseAttrs, nice idea Corey!
13378         if(this.baseAttrs){
13379             Roo.applyIf(attr, this.baseAttrs);
13380         }
13381         if(this.applyLoader !== false){
13382             attr.loader = this;
13383         }
13384         // uiProvider = depreciated..
13385         
13386         if(typeof(attr.uiProvider) == 'string'){
13387            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13388                 /**  eval:var:attr */ eval(attr.uiProvider);
13389         }
13390         if(typeof(this.uiProviders['default']) != 'undefined') {
13391             attr.uiProvider = this.uiProviders['default'];
13392         }
13393         
13394         this.fireEvent('create', this, attr);
13395         
13396         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13397         return(attr.leaf ?
13398                         new Roo.tree.TreeNode(attr) :
13399                         new Roo.tree.AsyncTreeNode(attr));
13400     },
13401
13402     processResponse : function(response, node, callback)
13403     {
13404         var json = response.responseText;
13405         try {
13406             
13407             var o = Roo.decode(json);
13408             
13409             if (this.root === false && typeof(o.success) != undefined) {
13410                 this.root = 'data'; // the default behaviour for list like data..
13411                 }
13412                 
13413             if (this.root !== false &&  !o.success) {
13414                 // it's a failure condition.
13415                 var a = response.argument;
13416                 this.fireEvent("loadexception", this, a.node, response);
13417                 Roo.log("Load failed - should have a handler really");
13418                 return;
13419             }
13420             
13421             
13422             
13423             if (this.root !== false) {
13424                  o = o[this.root];
13425             }
13426             
13427             for(var i = 0, len = o.length; i < len; i++){
13428                 var n = this.createNode(o[i]);
13429                 if(n){
13430                     node.appendChild(n);
13431                 }
13432             }
13433             if(typeof callback == "function"){
13434                 callback(this, node);
13435             }
13436         }catch(e){
13437             this.handleFailure(response);
13438         }
13439     },
13440
13441     handleResponse : function(response){
13442         this.transId = false;
13443         var a = response.argument;
13444         this.processResponse(response, a.node, a.callback);
13445         this.fireEvent("load", this, a.node, response);
13446     },
13447
13448     handleFailure : function(response)
13449     {
13450         // should handle failure better..
13451         this.transId = false;
13452         var a = response.argument;
13453         this.fireEvent("loadexception", this, a.node, response);
13454         if(typeof a.callback == "function"){
13455             a.callback(this, a.node);
13456         }
13457     }
13458 });/*
13459  * Based on:
13460  * Ext JS Library 1.1.1
13461  * Copyright(c) 2006-2007, Ext JS, LLC.
13462  *
13463  * Originally Released Under LGPL - original licence link has changed is not relivant.
13464  *
13465  * Fork - LGPL
13466  * <script type="text/javascript">
13467  */
13468
13469 /**
13470 * @class Roo.tree.TreeFilter
13471 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13472 * @param {TreePanel} tree
13473 * @param {Object} config (optional)
13474  */
13475 Roo.tree.TreeFilter = function(tree, config){
13476     this.tree = tree;
13477     this.filtered = {};
13478     Roo.apply(this, config);
13479 };
13480
13481 Roo.tree.TreeFilter.prototype = {
13482     clearBlank:false,
13483     reverse:false,
13484     autoClear:false,
13485     remove:false,
13486
13487      /**
13488      * Filter the data by a specific attribute.
13489      * @param {String/RegExp} value Either string that the attribute value
13490      * should start with or a RegExp to test against the attribute
13491      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13492      * @param {TreeNode} startNode (optional) The node to start the filter at.
13493      */
13494     filter : function(value, attr, startNode){
13495         attr = attr || "text";
13496         var f;
13497         if(typeof value == "string"){
13498             var vlen = value.length;
13499             // auto clear empty filter
13500             if(vlen == 0 && this.clearBlank){
13501                 this.clear();
13502                 return;
13503             }
13504             value = value.toLowerCase();
13505             f = function(n){
13506                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13507             };
13508         }else if(value.exec){ // regex?
13509             f = function(n){
13510                 return value.test(n.attributes[attr]);
13511             };
13512         }else{
13513             throw 'Illegal filter type, must be string or regex';
13514         }
13515         this.filterBy(f, null, startNode);
13516         },
13517
13518     /**
13519      * Filter by a function. The passed function will be called with each
13520      * node in the tree (or from the startNode). If the function returns true, the node is kept
13521      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13522      * @param {Function} fn The filter function
13523      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13524      */
13525     filterBy : function(fn, scope, startNode){
13526         startNode = startNode || this.tree.root;
13527         if(this.autoClear){
13528             this.clear();
13529         }
13530         var af = this.filtered, rv = this.reverse;
13531         var f = function(n){
13532             if(n == startNode){
13533                 return true;
13534             }
13535             if(af[n.id]){
13536                 return false;
13537             }
13538             var m = fn.call(scope || n, n);
13539             if(!m || rv){
13540                 af[n.id] = n;
13541                 n.ui.hide();
13542                 return false;
13543             }
13544             return true;
13545         };
13546         startNode.cascade(f);
13547         if(this.remove){
13548            for(var id in af){
13549                if(typeof id != "function"){
13550                    var n = af[id];
13551                    if(n && n.parentNode){
13552                        n.parentNode.removeChild(n);
13553                    }
13554                }
13555            }
13556         }
13557     },
13558
13559     /**
13560      * Clears the current filter. Note: with the "remove" option
13561      * set a filter cannot be cleared.
13562      */
13563     clear : function(){
13564         var t = this.tree;
13565         var af = this.filtered;
13566         for(var id in af){
13567             if(typeof id != "function"){
13568                 var n = af[id];
13569                 if(n){
13570                     n.ui.show();
13571                 }
13572             }
13573         }
13574         this.filtered = {};
13575     }
13576 };
13577 /*
13578  * Based on:
13579  * Ext JS Library 1.1.1
13580  * Copyright(c) 2006-2007, Ext JS, LLC.
13581  *
13582  * Originally Released Under LGPL - original licence link has changed is not relivant.
13583  *
13584  * Fork - LGPL
13585  * <script type="text/javascript">
13586  */
13587  
13588
13589 /**
13590  * @class Roo.tree.TreeSorter
13591  * Provides sorting of nodes in a TreePanel
13592  * 
13593  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13594  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13595  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13596  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13597  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13598  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13599  * @constructor
13600  * @param {TreePanel} tree
13601  * @param {Object} config
13602  */
13603 Roo.tree.TreeSorter = function(tree, config){
13604     Roo.apply(this, config);
13605     tree.on("beforechildrenrendered", this.doSort, this);
13606     tree.on("append", this.updateSort, this);
13607     tree.on("insert", this.updateSort, this);
13608     
13609     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13610     var p = this.property || "text";
13611     var sortType = this.sortType;
13612     var fs = this.folderSort;
13613     var cs = this.caseSensitive === true;
13614     var leafAttr = this.leafAttr || 'leaf';
13615
13616     this.sortFn = function(n1, n2){
13617         if(fs){
13618             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13619                 return 1;
13620             }
13621             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13622                 return -1;
13623             }
13624         }
13625         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13626         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13627         if(v1 < v2){
13628                         return dsc ? +1 : -1;
13629                 }else if(v1 > v2){
13630                         return dsc ? -1 : +1;
13631         }else{
13632                 return 0;
13633         }
13634     };
13635 };
13636
13637 Roo.tree.TreeSorter.prototype = {
13638     doSort : function(node){
13639         node.sort(this.sortFn);
13640     },
13641     
13642     compareNodes : function(n1, n2){
13643         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13644     },
13645     
13646     updateSort : function(tree, node){
13647         if(node.childrenRendered){
13648             this.doSort.defer(1, this, [node]);
13649         }
13650     }
13651 };/*
13652  * Based on:
13653  * Ext JS Library 1.1.1
13654  * Copyright(c) 2006-2007, Ext JS, LLC.
13655  *
13656  * Originally Released Under LGPL - original licence link has changed is not relivant.
13657  *
13658  * Fork - LGPL
13659  * <script type="text/javascript">
13660  */
13661
13662 if(Roo.dd.DropZone){
13663     
13664 Roo.tree.TreeDropZone = function(tree, config){
13665     this.allowParentInsert = false;
13666     this.allowContainerDrop = false;
13667     this.appendOnly = false;
13668     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13669     this.tree = tree;
13670     this.lastInsertClass = "x-tree-no-status";
13671     this.dragOverData = {};
13672 };
13673
13674 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13675     ddGroup : "TreeDD",
13676     scroll:  true,
13677     
13678     expandDelay : 1000,
13679     
13680     expandNode : function(node){
13681         if(node.hasChildNodes() && !node.isExpanded()){
13682             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13683         }
13684     },
13685     
13686     queueExpand : function(node){
13687         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13688     },
13689     
13690     cancelExpand : function(){
13691         if(this.expandProcId){
13692             clearTimeout(this.expandProcId);
13693             this.expandProcId = false;
13694         }
13695     },
13696     
13697     isValidDropPoint : function(n, pt, dd, e, data){
13698         if(!n || !data){ return false; }
13699         var targetNode = n.node;
13700         var dropNode = data.node;
13701         // default drop rules
13702         if(!(targetNode && targetNode.isTarget && pt)){
13703             return false;
13704         }
13705         if(pt == "append" && targetNode.allowChildren === false){
13706             return false;
13707         }
13708         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13709             return false;
13710         }
13711         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13712             return false;
13713         }
13714         // reuse the object
13715         var overEvent = this.dragOverData;
13716         overEvent.tree = this.tree;
13717         overEvent.target = targetNode;
13718         overEvent.data = data;
13719         overEvent.point = pt;
13720         overEvent.source = dd;
13721         overEvent.rawEvent = e;
13722         overEvent.dropNode = dropNode;
13723         overEvent.cancel = false;  
13724         var result = this.tree.fireEvent("nodedragover", overEvent);
13725         return overEvent.cancel === false && result !== false;
13726     },
13727     
13728     getDropPoint : function(e, n, dd)
13729     {
13730         var tn = n.node;
13731         if(tn.isRoot){
13732             return tn.allowChildren !== false ? "append" : false; // always append for root
13733         }
13734         var dragEl = n.ddel;
13735         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13736         var y = Roo.lib.Event.getPageY(e);
13737         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13738         
13739         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13740         var noAppend = tn.allowChildren === false;
13741         if(this.appendOnly || tn.parentNode.allowChildren === false){
13742             return noAppend ? false : "append";
13743         }
13744         var noBelow = false;
13745         if(!this.allowParentInsert){
13746             noBelow = tn.hasChildNodes() && tn.isExpanded();
13747         }
13748         var q = (b - t) / (noAppend ? 2 : 3);
13749         if(y >= t && y < (t + q)){
13750             return "above";
13751         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13752             return "below";
13753         }else{
13754             return "append";
13755         }
13756     },
13757     
13758     onNodeEnter : function(n, dd, e, data)
13759     {
13760         this.cancelExpand();
13761     },
13762     
13763     onNodeOver : function(n, dd, e, data)
13764     {
13765        
13766         var pt = this.getDropPoint(e, n, dd);
13767         var node = n.node;
13768         
13769         // auto node expand check
13770         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13771             this.queueExpand(node);
13772         }else if(pt != "append"){
13773             this.cancelExpand();
13774         }
13775         
13776         // set the insert point style on the target node
13777         var returnCls = this.dropNotAllowed;
13778         if(this.isValidDropPoint(n, pt, dd, e, data)){
13779            if(pt){
13780                var el = n.ddel;
13781                var cls;
13782                if(pt == "above"){
13783                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13784                    cls = "x-tree-drag-insert-above";
13785                }else if(pt == "below"){
13786                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13787                    cls = "x-tree-drag-insert-below";
13788                }else{
13789                    returnCls = "x-tree-drop-ok-append";
13790                    cls = "x-tree-drag-append";
13791                }
13792                if(this.lastInsertClass != cls){
13793                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13794                    this.lastInsertClass = cls;
13795                }
13796            }
13797        }
13798        return returnCls;
13799     },
13800     
13801     onNodeOut : function(n, dd, e, data){
13802         
13803         this.cancelExpand();
13804         this.removeDropIndicators(n);
13805     },
13806     
13807     onNodeDrop : function(n, dd, e, data){
13808         var point = this.getDropPoint(e, n, dd);
13809         var targetNode = n.node;
13810         targetNode.ui.startDrop();
13811         if(!this.isValidDropPoint(n, point, dd, e, data)){
13812             targetNode.ui.endDrop();
13813             return false;
13814         }
13815         // first try to find the drop node
13816         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13817         var dropEvent = {
13818             tree : this.tree,
13819             target: targetNode,
13820             data: data,
13821             point: point,
13822             source: dd,
13823             rawEvent: e,
13824             dropNode: dropNode,
13825             cancel: !dropNode   
13826         };
13827         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13828         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13829             targetNode.ui.endDrop();
13830             return false;
13831         }
13832         // allow target changing
13833         targetNode = dropEvent.target;
13834         if(point == "append" && !targetNode.isExpanded()){
13835             targetNode.expand(false, null, function(){
13836                 this.completeDrop(dropEvent);
13837             }.createDelegate(this));
13838         }else{
13839             this.completeDrop(dropEvent);
13840         }
13841         return true;
13842     },
13843     
13844     completeDrop : function(de){
13845         var ns = de.dropNode, p = de.point, t = de.target;
13846         if(!(ns instanceof Array)){
13847             ns = [ns];
13848         }
13849         var n;
13850         for(var i = 0, len = ns.length; i < len; i++){
13851             n = ns[i];
13852             if(p == "above"){
13853                 t.parentNode.insertBefore(n, t);
13854             }else if(p == "below"){
13855                 t.parentNode.insertBefore(n, t.nextSibling);
13856             }else{
13857                 t.appendChild(n);
13858             }
13859         }
13860         n.ui.focus();
13861         if(this.tree.hlDrop){
13862             n.ui.highlight();
13863         }
13864         t.ui.endDrop();
13865         this.tree.fireEvent("nodedrop", de);
13866     },
13867     
13868     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13869         if(this.tree.hlDrop){
13870             dropNode.ui.focus();
13871             dropNode.ui.highlight();
13872         }
13873         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13874     },
13875     
13876     getTree : function(){
13877         return this.tree;
13878     },
13879     
13880     removeDropIndicators : function(n){
13881         if(n && n.ddel){
13882             var el = n.ddel;
13883             Roo.fly(el).removeClass([
13884                     "x-tree-drag-insert-above",
13885                     "x-tree-drag-insert-below",
13886                     "x-tree-drag-append"]);
13887             this.lastInsertClass = "_noclass";
13888         }
13889     },
13890     
13891     beforeDragDrop : function(target, e, id){
13892         this.cancelExpand();
13893         return true;
13894     },
13895     
13896     afterRepair : function(data){
13897         if(data && Roo.enableFx){
13898             data.node.ui.highlight();
13899         }
13900         this.hideProxy();
13901     } 
13902     
13903 });
13904
13905 }
13906 /*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916  
13917
13918 if(Roo.dd.DragZone){
13919 Roo.tree.TreeDragZone = function(tree, config){
13920     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13921     this.tree = tree;
13922 };
13923
13924 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13925     ddGroup : "TreeDD",
13926    
13927     onBeforeDrag : function(data, e){
13928         var n = data.node;
13929         return n && n.draggable && !n.disabled;
13930     },
13931      
13932     
13933     onInitDrag : function(e){
13934         var data = this.dragData;
13935         this.tree.getSelectionModel().select(data.node);
13936         this.proxy.update("");
13937         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13938         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13939     },
13940     
13941     getRepairXY : function(e, data){
13942         return data.node.ui.getDDRepairXY();
13943     },
13944     
13945     onEndDrag : function(data, e){
13946         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13947         
13948         
13949     },
13950     
13951     onValidDrop : function(dd, e, id){
13952         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13953         this.hideProxy();
13954     },
13955     
13956     beforeInvalidDrop : function(e, id){
13957         // this scrolls the original position back into view
13958         var sm = this.tree.getSelectionModel();
13959         sm.clearSelections();
13960         sm.select(this.dragData.node);
13961     }
13962 });
13963 }/*
13964  * Based on:
13965  * Ext JS Library 1.1.1
13966  * Copyright(c) 2006-2007, Ext JS, LLC.
13967  *
13968  * Originally Released Under LGPL - original licence link has changed is not relivant.
13969  *
13970  * Fork - LGPL
13971  * <script type="text/javascript">
13972  */
13973 /**
13974  * @class Roo.tree.TreeEditor
13975  * @extends Roo.Editor
13976  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13977  * as the editor field.
13978  * @constructor
13979  * @param {Object} config (used to be the tree panel.)
13980  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13981  * 
13982  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13983  * @cfg {Roo.form.TextField} field [required] The field configuration
13984  *
13985  * 
13986  */
13987 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13988     var tree = config;
13989     var field;
13990     if (oldconfig) { // old style..
13991         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13992     } else {
13993         // new style..
13994         tree = config.tree;
13995         config.field = config.field  || {};
13996         config.field.xtype = 'TextField';
13997         field = Roo.factory(config.field, Roo.form);
13998     }
13999     config = config || {};
14000     
14001     
14002     this.addEvents({
14003         /**
14004          * @event beforenodeedit
14005          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14006          * false from the handler of this event.
14007          * @param {Editor} this
14008          * @param {Roo.tree.Node} node 
14009          */
14010         "beforenodeedit" : true
14011     });
14012     
14013     //Roo.log(config);
14014     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14015
14016     this.tree = tree;
14017
14018     tree.on('beforeclick', this.beforeNodeClick, this);
14019     tree.getTreeEl().on('mousedown', this.hide, this);
14020     this.on('complete', this.updateNode, this);
14021     this.on('beforestartedit', this.fitToTree, this);
14022     this.on('startedit', this.bindScroll, this, {delay:10});
14023     this.on('specialkey', this.onSpecialKey, this);
14024 };
14025
14026 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14027     /**
14028      * @cfg {String} alignment
14029      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14030      */
14031     alignment: "l-l",
14032     // inherit
14033     autoSize: false,
14034     /**
14035      * @cfg {Boolean} hideEl
14036      * True to hide the bound element while the editor is displayed (defaults to false)
14037      */
14038     hideEl : false,
14039     /**
14040      * @cfg {String} cls
14041      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14042      */
14043     cls: "x-small-editor x-tree-editor",
14044     /**
14045      * @cfg {Boolean} shim
14046      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14047      */
14048     shim:false,
14049     // inherit
14050     shadow:"frame",
14051     /**
14052      * @cfg {Number} maxWidth
14053      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14054      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14055      * scroll and client offsets into account prior to each edit.
14056      */
14057     maxWidth: 250,
14058
14059     editDelay : 350,
14060
14061     // private
14062     fitToTree : function(ed, el){
14063         var td = this.tree.getTreeEl().dom, nd = el.dom;
14064         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14065             td.scrollLeft = nd.offsetLeft;
14066         }
14067         var w = Math.min(
14068                 this.maxWidth,
14069                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14070         this.setSize(w, '');
14071         
14072         return this.fireEvent('beforenodeedit', this, this.editNode);
14073         
14074     },
14075
14076     // private
14077     triggerEdit : function(node){
14078         this.completeEdit();
14079         this.editNode = node;
14080         this.startEdit(node.ui.textNode, node.text);
14081     },
14082
14083     // private
14084     bindScroll : function(){
14085         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14086     },
14087
14088     // private
14089     beforeNodeClick : function(node, e){
14090         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14091         this.lastClick = new Date();
14092         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14093             e.stopEvent();
14094             this.triggerEdit(node);
14095             return false;
14096         }
14097         return true;
14098     },
14099
14100     // private
14101     updateNode : function(ed, value){
14102         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14103         this.editNode.setText(value);
14104     },
14105
14106     // private
14107     onHide : function(){
14108         Roo.tree.TreeEditor.superclass.onHide.call(this);
14109         if(this.editNode){
14110             this.editNode.ui.focus();
14111         }
14112     },
14113
14114     // private
14115     onSpecialKey : function(field, e){
14116         var k = e.getKey();
14117         if(k == e.ESC){
14118             e.stopEvent();
14119             this.cancelEdit();
14120         }else if(k == e.ENTER && !e.hasModifier()){
14121             e.stopEvent();
14122             this.completeEdit();
14123         }
14124     }
14125 });//<Script type="text/javascript">
14126 /*
14127  * Based on:
14128  * Ext JS Library 1.1.1
14129  * Copyright(c) 2006-2007, Ext JS, LLC.
14130  *
14131  * Originally Released Under LGPL - original licence link has changed is not relivant.
14132  *
14133  * Fork - LGPL
14134  * <script type="text/javascript">
14135  */
14136  
14137 /**
14138  * Not documented??? - probably should be...
14139  */
14140
14141 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14142     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14143     
14144     renderElements : function(n, a, targetNode, bulkRender){
14145         //consel.log("renderElements?");
14146         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14147
14148         var t = n.getOwnerTree();
14149         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14150         
14151         var cols = t.columns;
14152         var bw = t.borderWidth;
14153         var c = cols[0];
14154         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14155          var cb = typeof a.checked == "boolean";
14156         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14157         var colcls = 'x-t-' + tid + '-c0';
14158         var buf = [
14159             '<li class="x-tree-node">',
14160             
14161                 
14162                 '<div class="x-tree-node-el ', a.cls,'">',
14163                     // extran...
14164                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14165                 
14166                 
14167                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14168                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14169                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14170                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14171                            (a.iconCls ? ' '+a.iconCls : ''),
14172                            '" unselectable="on" />',
14173                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14174                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14175                              
14176                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14177                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14178                             '<span unselectable="on" qtip="' + tx + '">',
14179                              tx,
14180                              '</span></a>' ,
14181                     '</div>',
14182                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14183                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14184                  ];
14185         for(var i = 1, len = cols.length; i < len; i++){
14186             c = cols[i];
14187             colcls = 'x-t-' + tid + '-c' +i;
14188             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14189             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14190                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14191                       "</div>");
14192          }
14193          
14194          buf.push(
14195             '</a>',
14196             '<div class="x-clear"></div></div>',
14197             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14198             "</li>");
14199         
14200         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14201             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14202                                 n.nextSibling.ui.getEl(), buf.join(""));
14203         }else{
14204             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14205         }
14206         var el = this.wrap.firstChild;
14207         this.elRow = el;
14208         this.elNode = el.firstChild;
14209         this.ranchor = el.childNodes[1];
14210         this.ctNode = this.wrap.childNodes[1];
14211         var cs = el.firstChild.childNodes;
14212         this.indentNode = cs[0];
14213         this.ecNode = cs[1];
14214         this.iconNode = cs[2];
14215         var index = 3;
14216         if(cb){
14217             this.checkbox = cs[3];
14218             index++;
14219         }
14220         this.anchor = cs[index];
14221         
14222         this.textNode = cs[index].firstChild;
14223         
14224         //el.on("click", this.onClick, this);
14225         //el.on("dblclick", this.onDblClick, this);
14226         
14227         
14228        // console.log(this);
14229     },
14230     initEvents : function(){
14231         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14232         
14233             
14234         var a = this.ranchor;
14235
14236         var el = Roo.get(a);
14237
14238         if(Roo.isOpera){ // opera render bug ignores the CSS
14239             el.setStyle("text-decoration", "none");
14240         }
14241
14242         el.on("click", this.onClick, this);
14243         el.on("dblclick", this.onDblClick, this);
14244         el.on("contextmenu", this.onContextMenu, this);
14245         
14246     },
14247     
14248     /*onSelectedChange : function(state){
14249         if(state){
14250             this.focus();
14251             this.addClass("x-tree-selected");
14252         }else{
14253             //this.blur();
14254             this.removeClass("x-tree-selected");
14255         }
14256     },*/
14257     addClass : function(cls){
14258         if(this.elRow){
14259             Roo.fly(this.elRow).addClass(cls);
14260         }
14261         
14262     },
14263     
14264     
14265     removeClass : function(cls){
14266         if(this.elRow){
14267             Roo.fly(this.elRow).removeClass(cls);
14268         }
14269     }
14270
14271     
14272     
14273 });//<Script type="text/javascript">
14274
14275 /*
14276  * Based on:
14277  * Ext JS Library 1.1.1
14278  * Copyright(c) 2006-2007, Ext JS, LLC.
14279  *
14280  * Originally Released Under LGPL - original licence link has changed is not relivant.
14281  *
14282  * Fork - LGPL
14283  * <script type="text/javascript">
14284  */
14285  
14286
14287 /**
14288  * @class Roo.tree.ColumnTree
14289  * @extends Roo.data.TreePanel
14290  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14291  * @cfg {int} borderWidth  compined right/left border allowance
14292  * @constructor
14293  * @param {String/HTMLElement/Element} el The container element
14294  * @param {Object} config
14295  */
14296 Roo.tree.ColumnTree =  function(el, config)
14297 {
14298    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14299    this.addEvents({
14300         /**
14301         * @event resize
14302         * Fire this event on a container when it resizes
14303         * @param {int} w Width
14304         * @param {int} h Height
14305         */
14306        "resize" : true
14307     });
14308     this.on('resize', this.onResize, this);
14309 };
14310
14311 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14312     //lines:false,
14313     
14314     
14315     borderWidth: Roo.isBorderBox ? 0 : 2, 
14316     headEls : false,
14317     
14318     render : function(){
14319         // add the header.....
14320        
14321         Roo.tree.ColumnTree.superclass.render.apply(this);
14322         
14323         this.el.addClass('x-column-tree');
14324         
14325         this.headers = this.el.createChild(
14326             {cls:'x-tree-headers'},this.innerCt.dom);
14327    
14328         var cols = this.columns, c;
14329         var totalWidth = 0;
14330         this.headEls = [];
14331         var  len = cols.length;
14332         for(var i = 0; i < len; i++){
14333              c = cols[i];
14334              totalWidth += c.width;
14335             this.headEls.push(this.headers.createChild({
14336                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14337                  cn: {
14338                      cls:'x-tree-hd-text',
14339                      html: c.header
14340                  },
14341                  style:'width:'+(c.width-this.borderWidth)+'px;'
14342              }));
14343         }
14344         this.headers.createChild({cls:'x-clear'});
14345         // prevent floats from wrapping when clipped
14346         this.headers.setWidth(totalWidth);
14347         //this.innerCt.setWidth(totalWidth);
14348         this.innerCt.setStyle({ overflow: 'auto' });
14349         this.onResize(this.width, this.height);
14350              
14351         
14352     },
14353     onResize : function(w,h)
14354     {
14355         this.height = h;
14356         this.width = w;
14357         // resize cols..
14358         this.innerCt.setWidth(this.width);
14359         this.innerCt.setHeight(this.height-20);
14360         
14361         // headers...
14362         var cols = this.columns, c;
14363         var totalWidth = 0;
14364         var expEl = false;
14365         var len = cols.length;
14366         for(var i = 0; i < len; i++){
14367             c = cols[i];
14368             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14369                 // it's the expander..
14370                 expEl  = this.headEls[i];
14371                 continue;
14372             }
14373             totalWidth += c.width;
14374             
14375         }
14376         if (expEl) {
14377             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14378         }
14379         this.headers.setWidth(w-20);
14380
14381         
14382         
14383         
14384     }
14385 });
14386 /*
14387  * Based on:
14388  * Ext JS Library 1.1.1
14389  * Copyright(c) 2006-2007, Ext JS, LLC.
14390  *
14391  * Originally Released Under LGPL - original licence link has changed is not relivant.
14392  *
14393  * Fork - LGPL
14394  * <script type="text/javascript">
14395  */
14396  
14397 /**
14398  * @class Roo.menu.Menu
14399  * @extends Roo.util.Observable
14400  * @children Roo.menu.BaseItem
14401  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14402  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14403  * @constructor
14404  * Creates a new Menu
14405  * @param {Object} config Configuration options
14406  */
14407 Roo.menu.Menu = function(config){
14408     
14409     Roo.menu.Menu.superclass.constructor.call(this, config);
14410     
14411     this.id = this.id || Roo.id();
14412     this.addEvents({
14413         /**
14414          * @event beforeshow
14415          * Fires before this menu is displayed
14416          * @param {Roo.menu.Menu} this
14417          */
14418         beforeshow : true,
14419         /**
14420          * @event beforehide
14421          * Fires before this menu is hidden
14422          * @param {Roo.menu.Menu} this
14423          */
14424         beforehide : true,
14425         /**
14426          * @event show
14427          * Fires after this menu is displayed
14428          * @param {Roo.menu.Menu} this
14429          */
14430         show : true,
14431         /**
14432          * @event hide
14433          * Fires after this menu is hidden
14434          * @param {Roo.menu.Menu} this
14435          */
14436         hide : true,
14437         /**
14438          * @event click
14439          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14440          * @param {Roo.menu.Menu} this
14441          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14442          * @param {Roo.EventObject} e
14443          */
14444         click : true,
14445         /**
14446          * @event mouseover
14447          * Fires when the mouse is hovering over this menu
14448          * @param {Roo.menu.Menu} this
14449          * @param {Roo.EventObject} e
14450          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14451          */
14452         mouseover : true,
14453         /**
14454          * @event mouseout
14455          * Fires when the mouse exits this menu
14456          * @param {Roo.menu.Menu} this
14457          * @param {Roo.EventObject} e
14458          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14459          */
14460         mouseout : true,
14461         /**
14462          * @event itemclick
14463          * Fires when a menu item contained in this menu is clicked
14464          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14465          * @param {Roo.EventObject} e
14466          */
14467         itemclick: true
14468     });
14469     if (this.registerMenu) {
14470         Roo.menu.MenuMgr.register(this);
14471     }
14472     
14473     var mis = this.items;
14474     this.items = new Roo.util.MixedCollection();
14475     if(mis){
14476         this.add.apply(this, mis);
14477     }
14478 };
14479
14480 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14481     /**
14482      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14483      */
14484     minWidth : 120,
14485     /**
14486      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14487      * for bottom-right shadow (defaults to "sides")
14488      */
14489     shadow : "sides",
14490     /**
14491      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14492      * this menu (defaults to "tl-tr?")
14493      */
14494     subMenuAlign : "tl-tr?",
14495     /**
14496      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14497      * relative to its element of origin (defaults to "tl-bl?")
14498      */
14499     defaultAlign : "tl-bl?",
14500     /**
14501      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14502      */
14503     allowOtherMenus : false,
14504     /**
14505      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14506      */
14507     registerMenu : true,
14508
14509     hidden:true,
14510
14511     // private
14512     render : function(){
14513         if(this.el){
14514             return;
14515         }
14516         var el = this.el = new Roo.Layer({
14517             cls: "x-menu",
14518             shadow:this.shadow,
14519             constrain: false,
14520             parentEl: this.parentEl || document.body,
14521             zindex:15000
14522         });
14523
14524         this.keyNav = new Roo.menu.MenuNav(this);
14525
14526         if(this.plain){
14527             el.addClass("x-menu-plain");
14528         }
14529         if(this.cls){
14530             el.addClass(this.cls);
14531         }
14532         // generic focus element
14533         this.focusEl = el.createChild({
14534             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14535         });
14536         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14537         //disabling touch- as it's causing issues ..
14538         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14539         ul.on('click'   , this.onClick, this);
14540         
14541         
14542         ul.on("mouseover", this.onMouseOver, this);
14543         ul.on("mouseout", this.onMouseOut, this);
14544         this.items.each(function(item){
14545             if (item.hidden) {
14546                 return;
14547             }
14548             
14549             var li = document.createElement("li");
14550             li.className = "x-menu-list-item";
14551             ul.dom.appendChild(li);
14552             item.render(li, this);
14553         }, this);
14554         this.ul = ul;
14555         this.autoWidth();
14556     },
14557
14558     // private
14559     autoWidth : function(){
14560         var el = this.el, ul = this.ul;
14561         if(!el){
14562             return;
14563         }
14564         var w = this.width;
14565         if(w){
14566             el.setWidth(w);
14567         }else if(Roo.isIE){
14568             el.setWidth(this.minWidth);
14569             var t = el.dom.offsetWidth; // force recalc
14570             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14571         }
14572     },
14573
14574     // private
14575     delayAutoWidth : function(){
14576         if(this.rendered){
14577             if(!this.awTask){
14578                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14579             }
14580             this.awTask.delay(20);
14581         }
14582     },
14583
14584     // private
14585     findTargetItem : function(e){
14586         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14587         if(t && t.menuItemId){
14588             return this.items.get(t.menuItemId);
14589         }
14590     },
14591
14592     // private
14593     onClick : function(e){
14594         Roo.log("menu.onClick");
14595         var t = this.findTargetItem(e);
14596         if(!t){
14597             return;
14598         }
14599         Roo.log(e);
14600         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14601             if(t == this.activeItem && t.shouldDeactivate(e)){
14602                 this.activeItem.deactivate();
14603                 delete this.activeItem;
14604                 return;
14605             }
14606             if(t.canActivate){
14607                 this.setActiveItem(t, true);
14608             }
14609             return;
14610             
14611             
14612         }
14613         
14614         t.onClick(e);
14615         this.fireEvent("click", this, t, e);
14616     },
14617
14618     // private
14619     setActiveItem : function(item, autoExpand){
14620         if(item != this.activeItem){
14621             if(this.activeItem){
14622                 this.activeItem.deactivate();
14623             }
14624             this.activeItem = item;
14625             item.activate(autoExpand);
14626         }else if(autoExpand){
14627             item.expandMenu();
14628         }
14629     },
14630
14631     // private
14632     tryActivate : function(start, step){
14633         var items = this.items;
14634         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14635             var item = items.get(i);
14636             if(!item.disabled && item.canActivate){
14637                 this.setActiveItem(item, false);
14638                 return item;
14639             }
14640         }
14641         return false;
14642     },
14643
14644     // private
14645     onMouseOver : function(e){
14646         var t;
14647         if(t = this.findTargetItem(e)){
14648             if(t.canActivate && !t.disabled){
14649                 this.setActiveItem(t, true);
14650             }
14651         }
14652         this.fireEvent("mouseover", this, e, t);
14653     },
14654
14655     // private
14656     onMouseOut : function(e){
14657         var t;
14658         if(t = this.findTargetItem(e)){
14659             if(t == this.activeItem && t.shouldDeactivate(e)){
14660                 this.activeItem.deactivate();
14661                 delete this.activeItem;
14662             }
14663         }
14664         this.fireEvent("mouseout", this, e, t);
14665     },
14666
14667     /**
14668      * Read-only.  Returns true if the menu is currently displayed, else false.
14669      * @type Boolean
14670      */
14671     isVisible : function(){
14672         return this.el && !this.hidden;
14673     },
14674
14675     /**
14676      * Displays this menu relative to another element
14677      * @param {String/HTMLElement/Roo.Element} element The element to align to
14678      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14679      * the element (defaults to this.defaultAlign)
14680      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14681      */
14682     show : function(el, pos, parentMenu){
14683         this.parentMenu = parentMenu;
14684         if(!this.el){
14685             this.render();
14686         }
14687         this.fireEvent("beforeshow", this);
14688         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14689     },
14690
14691     /**
14692      * Displays this menu at a specific xy position
14693      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14694      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14695      */
14696     showAt : function(xy, parentMenu, /* private: */_e){
14697         this.parentMenu = parentMenu;
14698         if(!this.el){
14699             this.render();
14700         }
14701         if(_e !== false){
14702             this.fireEvent("beforeshow", this);
14703             xy = this.el.adjustForConstraints(xy);
14704         }
14705         this.el.setXY(xy);
14706         this.el.show();
14707         this.hidden = false;
14708         this.focus();
14709         this.fireEvent("show", this);
14710     },
14711
14712     focus : function(){
14713         if(!this.hidden){
14714             this.doFocus.defer(50, this);
14715         }
14716     },
14717
14718     doFocus : function(){
14719         if(!this.hidden){
14720             this.focusEl.focus();
14721         }
14722     },
14723
14724     /**
14725      * Hides this menu and optionally all parent menus
14726      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14727      */
14728     hide : function(deep){
14729         if(this.el && this.isVisible()){
14730             this.fireEvent("beforehide", this);
14731             if(this.activeItem){
14732                 this.activeItem.deactivate();
14733                 this.activeItem = null;
14734             }
14735             this.el.hide();
14736             this.hidden = true;
14737             this.fireEvent("hide", this);
14738         }
14739         if(deep === true && this.parentMenu){
14740             this.parentMenu.hide(true);
14741         }
14742     },
14743
14744     /**
14745      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14746      * Any of the following are valid:
14747      * <ul>
14748      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14749      * <li>An HTMLElement object which will be converted to a menu item</li>
14750      * <li>A menu item config object that will be created as a new menu item</li>
14751      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14752      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14753      * </ul>
14754      * Usage:
14755      * <pre><code>
14756 // Create the menu
14757 var menu = new Roo.menu.Menu();
14758
14759 // Create a menu item to add by reference
14760 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14761
14762 // Add a bunch of items at once using different methods.
14763 // Only the last item added will be returned.
14764 var item = menu.add(
14765     menuItem,                // add existing item by ref
14766     'Dynamic Item',          // new TextItem
14767     '-',                     // new separator
14768     { text: 'Config Item' }  // new item by config
14769 );
14770 </code></pre>
14771      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14772      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14773      */
14774     add : function(){
14775         var a = arguments, l = a.length, item;
14776         for(var i = 0; i < l; i++){
14777             var el = a[i];
14778             if ((typeof(el) == "object") && el.xtype && el.xns) {
14779                 el = Roo.factory(el, Roo.menu);
14780             }
14781             
14782             if(el.render){ // some kind of Item
14783                 item = this.addItem(el);
14784             }else if(typeof el == "string"){ // string
14785                 if(el == "separator" || el == "-"){
14786                     item = this.addSeparator();
14787                 }else{
14788                     item = this.addText(el);
14789                 }
14790             }else if(el.tagName || el.el){ // element
14791                 item = this.addElement(el);
14792             }else if(typeof el == "object"){ // must be menu item config?
14793                 item = this.addMenuItem(el);
14794             }
14795         }
14796         return item;
14797     },
14798
14799     /**
14800      * Returns this menu's underlying {@link Roo.Element} object
14801      * @return {Roo.Element} The element
14802      */
14803     getEl : function(){
14804         if(!this.el){
14805             this.render();
14806         }
14807         return this.el;
14808     },
14809
14810     /**
14811      * Adds a separator bar to the menu
14812      * @return {Roo.menu.Item} The menu item that was added
14813      */
14814     addSeparator : function(){
14815         return this.addItem(new Roo.menu.Separator());
14816     },
14817
14818     /**
14819      * Adds an {@link Roo.Element} object to the menu
14820      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14821      * @return {Roo.menu.Item} The menu item that was added
14822      */
14823     addElement : function(el){
14824         return this.addItem(new Roo.menu.BaseItem(el));
14825     },
14826
14827     /**
14828      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14829      * @param {Roo.menu.Item} item The menu item to add
14830      * @return {Roo.menu.Item} The menu item that was added
14831      */
14832     addItem : function(item){
14833         this.items.add(item);
14834         if(this.ul){
14835             var li = document.createElement("li");
14836             li.className = "x-menu-list-item";
14837             this.ul.dom.appendChild(li);
14838             item.render(li, this);
14839             this.delayAutoWidth();
14840         }
14841         return item;
14842     },
14843
14844     /**
14845      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14846      * @param {Object} config A MenuItem config object
14847      * @return {Roo.menu.Item} The menu item that was added
14848      */
14849     addMenuItem : function(config){
14850         if(!(config instanceof Roo.menu.Item)){
14851             if(typeof config.checked == "boolean"){ // must be check menu item config?
14852                 config = new Roo.menu.CheckItem(config);
14853             }else{
14854                 config = new Roo.menu.Item(config);
14855             }
14856         }
14857         return this.addItem(config);
14858     },
14859
14860     /**
14861      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14862      * @param {String} text The text to display in the menu item
14863      * @return {Roo.menu.Item} The menu item that was added
14864      */
14865     addText : function(text){
14866         return this.addItem(new Roo.menu.TextItem({ text : text }));
14867     },
14868
14869     /**
14870      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14871      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14872      * @param {Roo.menu.Item} item The menu item to add
14873      * @return {Roo.menu.Item} The menu item that was added
14874      */
14875     insert : function(index, item){
14876         this.items.insert(index, item);
14877         if(this.ul){
14878             var li = document.createElement("li");
14879             li.className = "x-menu-list-item";
14880             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14881             item.render(li, this);
14882             this.delayAutoWidth();
14883         }
14884         return item;
14885     },
14886
14887     /**
14888      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14889      * @param {Roo.menu.Item} item The menu item to remove
14890      */
14891     remove : function(item){
14892         this.items.removeKey(item.id);
14893         item.destroy();
14894     },
14895
14896     /**
14897      * Removes and destroys all items in the menu
14898      */
14899     removeAll : function(){
14900         var f;
14901         while(f = this.items.first()){
14902             this.remove(f);
14903         }
14904     }
14905 });
14906
14907 // MenuNav is a private utility class used internally by the Menu
14908 Roo.menu.MenuNav = function(menu){
14909     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14910     this.scope = this.menu = menu;
14911 };
14912
14913 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14914     doRelay : function(e, h){
14915         var k = e.getKey();
14916         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14917             this.menu.tryActivate(0, 1);
14918             return false;
14919         }
14920         return h.call(this.scope || this, e, this.menu);
14921     },
14922
14923     up : function(e, m){
14924         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14925             m.tryActivate(m.items.length-1, -1);
14926         }
14927     },
14928
14929     down : function(e, m){
14930         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14931             m.tryActivate(0, 1);
14932         }
14933     },
14934
14935     right : function(e, m){
14936         if(m.activeItem){
14937             m.activeItem.expandMenu(true);
14938         }
14939     },
14940
14941     left : function(e, m){
14942         m.hide();
14943         if(m.parentMenu && m.parentMenu.activeItem){
14944             m.parentMenu.activeItem.activate();
14945         }
14946     },
14947
14948     enter : function(e, m){
14949         if(m.activeItem){
14950             e.stopPropagation();
14951             m.activeItem.onClick(e);
14952             m.fireEvent("click", this, m.activeItem);
14953             return true;
14954         }
14955     }
14956 });/*
14957  * Based on:
14958  * Ext JS Library 1.1.1
14959  * Copyright(c) 2006-2007, Ext JS, LLC.
14960  *
14961  * Originally Released Under LGPL - original licence link has changed is not relivant.
14962  *
14963  * Fork - LGPL
14964  * <script type="text/javascript">
14965  */
14966  
14967 /**
14968  * @class Roo.menu.MenuMgr
14969  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14970  * @singleton
14971  */
14972 Roo.menu.MenuMgr = function(){
14973    var menus, active, groups = {}, attached = false, lastShow = new Date();
14974
14975    // private - called when first menu is created
14976    function init(){
14977        menus = {};
14978        active = new Roo.util.MixedCollection();
14979        Roo.get(document).addKeyListener(27, function(){
14980            if(active.length > 0){
14981                hideAll();
14982            }
14983        });
14984    }
14985
14986    // private
14987    function hideAll(){
14988        if(active && active.length > 0){
14989            var c = active.clone();
14990            c.each(function(m){
14991                m.hide();
14992            });
14993        }
14994    }
14995
14996    // private
14997    function onHide(m){
14998        active.remove(m);
14999        if(active.length < 1){
15000            Roo.get(document).un("mousedown", onMouseDown);
15001            attached = false;
15002        }
15003    }
15004
15005    // private
15006    function onShow(m){
15007        var last = active.last();
15008        lastShow = new Date();
15009        active.add(m);
15010        if(!attached){
15011            Roo.get(document).on("mousedown", onMouseDown);
15012            attached = true;
15013        }
15014        if(m.parentMenu){
15015           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15016           m.parentMenu.activeChild = m;
15017        }else if(last && last.isVisible()){
15018           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15019        }
15020    }
15021
15022    // private
15023    function onBeforeHide(m){
15024        if(m.activeChild){
15025            m.activeChild.hide();
15026        }
15027        if(m.autoHideTimer){
15028            clearTimeout(m.autoHideTimer);
15029            delete m.autoHideTimer;
15030        }
15031    }
15032
15033    // private
15034    function onBeforeShow(m){
15035        var pm = m.parentMenu;
15036        if(!pm && !m.allowOtherMenus){
15037            hideAll();
15038        }else if(pm && pm.activeChild && active != m){
15039            pm.activeChild.hide();
15040        }
15041    }
15042
15043    // private
15044    function onMouseDown(e){
15045        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15046            hideAll();
15047        }
15048    }
15049
15050    // private
15051    function onBeforeCheck(mi, state){
15052        if(state){
15053            var g = groups[mi.group];
15054            for(var i = 0, l = g.length; i < l; i++){
15055                if(g[i] != mi){
15056                    g[i].setChecked(false);
15057                }
15058            }
15059        }
15060    }
15061
15062    return {
15063
15064        /**
15065         * Hides all menus that are currently visible
15066         */
15067        hideAll : function(){
15068             hideAll();  
15069        },
15070
15071        // private
15072        register : function(menu){
15073            if(!menus){
15074                init();
15075            }
15076            menus[menu.id] = menu;
15077            menu.on("beforehide", onBeforeHide);
15078            menu.on("hide", onHide);
15079            menu.on("beforeshow", onBeforeShow);
15080            menu.on("show", onShow);
15081            var g = menu.group;
15082            if(g && menu.events["checkchange"]){
15083                if(!groups[g]){
15084                    groups[g] = [];
15085                }
15086                groups[g].push(menu);
15087                menu.on("checkchange", onCheck);
15088            }
15089        },
15090
15091         /**
15092          * Returns a {@link Roo.menu.Menu} object
15093          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15094          * be used to generate and return a new Menu instance.
15095          */
15096        get : function(menu){
15097            if(typeof menu == "string"){ // menu id
15098                return menus[menu];
15099            }else if(menu.events){  // menu instance
15100                return menu;
15101            }else if(typeof menu.length == 'number'){ // array of menu items?
15102                return new Roo.menu.Menu({items:menu});
15103            }else{ // otherwise, must be a config
15104                return new Roo.menu.Menu(menu);
15105            }
15106        },
15107
15108        // private
15109        unregister : function(menu){
15110            delete menus[menu.id];
15111            menu.un("beforehide", onBeforeHide);
15112            menu.un("hide", onHide);
15113            menu.un("beforeshow", onBeforeShow);
15114            menu.un("show", onShow);
15115            var g = menu.group;
15116            if(g && menu.events["checkchange"]){
15117                groups[g].remove(menu);
15118                menu.un("checkchange", onCheck);
15119            }
15120        },
15121
15122        // private
15123        registerCheckable : function(menuItem){
15124            var g = menuItem.group;
15125            if(g){
15126                if(!groups[g]){
15127                    groups[g] = [];
15128                }
15129                groups[g].push(menuItem);
15130                menuItem.on("beforecheckchange", onBeforeCheck);
15131            }
15132        },
15133
15134        // private
15135        unregisterCheckable : function(menuItem){
15136            var g = menuItem.group;
15137            if(g){
15138                groups[g].remove(menuItem);
15139                menuItem.un("beforecheckchange", onBeforeCheck);
15140            }
15141        }
15142    };
15143 }();/*
15144  * Based on:
15145  * Ext JS Library 1.1.1
15146  * Copyright(c) 2006-2007, Ext JS, LLC.
15147  *
15148  * Originally Released Under LGPL - original licence link has changed is not relivant.
15149  *
15150  * Fork - LGPL
15151  * <script type="text/javascript">
15152  */
15153  
15154
15155 /**
15156  * @class Roo.menu.BaseItem
15157  * @extends Roo.Component
15158  * @abstract
15159  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15160  * management and base configuration options shared by all menu components.
15161  * @constructor
15162  * Creates a new BaseItem
15163  * @param {Object} config Configuration options
15164  */
15165 Roo.menu.BaseItem = function(config){
15166     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15167
15168     this.addEvents({
15169         /**
15170          * @event click
15171          * Fires when this item is clicked
15172          * @param {Roo.menu.BaseItem} this
15173          * @param {Roo.EventObject} e
15174          */
15175         click: true,
15176         /**
15177          * @event activate
15178          * Fires when this item is activated
15179          * @param {Roo.menu.BaseItem} this
15180          */
15181         activate : true,
15182         /**
15183          * @event deactivate
15184          * Fires when this item is deactivated
15185          * @param {Roo.menu.BaseItem} this
15186          */
15187         deactivate : true
15188     });
15189
15190     if(this.handler){
15191         this.on("click", this.handler, this.scope, true);
15192     }
15193 };
15194
15195 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15196     /**
15197      * @cfg {Function} handler
15198      * A function that will handle the click event of this menu item (defaults to undefined)
15199      */
15200     /**
15201      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15202      */
15203     canActivate : false,
15204     
15205      /**
15206      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15207      */
15208     hidden: false,
15209     
15210     /**
15211      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15212      */
15213     activeClass : "x-menu-item-active",
15214     /**
15215      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15216      */
15217     hideOnClick : true,
15218     /**
15219      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15220      */
15221     hideDelay : 100,
15222
15223     // private
15224     ctype: "Roo.menu.BaseItem",
15225
15226     // private
15227     actionMode : "container",
15228
15229     // private
15230     render : function(container, parentMenu){
15231         this.parentMenu = parentMenu;
15232         Roo.menu.BaseItem.superclass.render.call(this, container);
15233         this.container.menuItemId = this.id;
15234     },
15235
15236     // private
15237     onRender : function(container, position){
15238         this.el = Roo.get(this.el);
15239         container.dom.appendChild(this.el.dom);
15240     },
15241
15242     // private
15243     onClick : function(e){
15244         if(!this.disabled && this.fireEvent("click", this, e) !== false
15245                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15246             this.handleClick(e);
15247         }else{
15248             e.stopEvent();
15249         }
15250     },
15251
15252     // private
15253     activate : function(){
15254         if(this.disabled){
15255             return false;
15256         }
15257         var li = this.container;
15258         li.addClass(this.activeClass);
15259         this.region = li.getRegion().adjust(2, 2, -2, -2);
15260         this.fireEvent("activate", this);
15261         return true;
15262     },
15263
15264     // private
15265     deactivate : function(){
15266         this.container.removeClass(this.activeClass);
15267         this.fireEvent("deactivate", this);
15268     },
15269
15270     // private
15271     shouldDeactivate : function(e){
15272         return !this.region || !this.region.contains(e.getPoint());
15273     },
15274
15275     // private
15276     handleClick : function(e){
15277         if(this.hideOnClick){
15278             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15279         }
15280     },
15281
15282     // private
15283     expandMenu : function(autoActivate){
15284         // do nothing
15285     },
15286
15287     // private
15288     hideMenu : function(){
15289         // do nothing
15290     }
15291 });/*
15292  * Based on:
15293  * Ext JS Library 1.1.1
15294  * Copyright(c) 2006-2007, Ext JS, LLC.
15295  *
15296  * Originally Released Under LGPL - original licence link has changed is not relivant.
15297  *
15298  * Fork - LGPL
15299  * <script type="text/javascript">
15300  */
15301  
15302 /**
15303  * @class Roo.menu.Adapter
15304  * @extends Roo.menu.BaseItem
15305  * @abstract
15306  * 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.
15307  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15308  * @constructor
15309  * Creates a new Adapter
15310  * @param {Object} config Configuration options
15311  */
15312 Roo.menu.Adapter = function(component, config){
15313     Roo.menu.Adapter.superclass.constructor.call(this, config);
15314     this.component = component;
15315 };
15316 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15317     // private
15318     canActivate : true,
15319
15320     // private
15321     onRender : function(container, position){
15322         this.component.render(container);
15323         this.el = this.component.getEl();
15324     },
15325
15326     // private
15327     activate : function(){
15328         if(this.disabled){
15329             return false;
15330         }
15331         this.component.focus();
15332         this.fireEvent("activate", this);
15333         return true;
15334     },
15335
15336     // private
15337     deactivate : function(){
15338         this.fireEvent("deactivate", this);
15339     },
15340
15341     // private
15342     disable : function(){
15343         this.component.disable();
15344         Roo.menu.Adapter.superclass.disable.call(this);
15345     },
15346
15347     // private
15348     enable : function(){
15349         this.component.enable();
15350         Roo.menu.Adapter.superclass.enable.call(this);
15351     }
15352 });/*
15353  * Based on:
15354  * Ext JS Library 1.1.1
15355  * Copyright(c) 2006-2007, Ext JS, LLC.
15356  *
15357  * Originally Released Under LGPL - original licence link has changed is not relivant.
15358  *
15359  * Fork - LGPL
15360  * <script type="text/javascript">
15361  */
15362
15363 /**
15364  * @class Roo.menu.TextItem
15365  * @extends Roo.menu.BaseItem
15366  * Adds a static text string to a menu, usually used as either a heading or group separator.
15367  * Note: old style constructor with text is still supported.
15368  * 
15369  * @constructor
15370  * Creates a new TextItem
15371  * @param {Object} cfg Configuration
15372  */
15373 Roo.menu.TextItem = function(cfg){
15374     if (typeof(cfg) == 'string') {
15375         this.text = cfg;
15376     } else {
15377         Roo.apply(this,cfg);
15378     }
15379     
15380     Roo.menu.TextItem.superclass.constructor.call(this);
15381 };
15382
15383 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15384     /**
15385      * @cfg {String} text Text to show on item.
15386      */
15387     text : '',
15388     
15389     /**
15390      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15391      */
15392     hideOnClick : false,
15393     /**
15394      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15395      */
15396     itemCls : "x-menu-text",
15397
15398     // private
15399     onRender : function(){
15400         var s = document.createElement("span");
15401         s.className = this.itemCls;
15402         s.innerHTML = this.text;
15403         this.el = s;
15404         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15405     }
15406 });/*
15407  * Based on:
15408  * Ext JS Library 1.1.1
15409  * Copyright(c) 2006-2007, Ext JS, LLC.
15410  *
15411  * Originally Released Under LGPL - original licence link has changed is not relivant.
15412  *
15413  * Fork - LGPL
15414  * <script type="text/javascript">
15415  */
15416
15417 /**
15418  * @class Roo.menu.Separator
15419  * @extends Roo.menu.BaseItem
15420  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15421  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15422  * @constructor
15423  * @param {Object} config Configuration options
15424  */
15425 Roo.menu.Separator = function(config){
15426     Roo.menu.Separator.superclass.constructor.call(this, config);
15427 };
15428
15429 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15430     /**
15431      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15432      */
15433     itemCls : "x-menu-sep",
15434     /**
15435      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15436      */
15437     hideOnClick : false,
15438
15439     // private
15440     onRender : function(li){
15441         var s = document.createElement("span");
15442         s.className = this.itemCls;
15443         s.innerHTML = "&#160;";
15444         this.el = s;
15445         li.addClass("x-menu-sep-li");
15446         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15447     }
15448 });/*
15449  * Based on:
15450  * Ext JS Library 1.1.1
15451  * Copyright(c) 2006-2007, Ext JS, LLC.
15452  *
15453  * Originally Released Under LGPL - original licence link has changed is not relivant.
15454  *
15455  * Fork - LGPL
15456  * <script type="text/javascript">
15457  */
15458 /**
15459  * @class Roo.menu.Item
15460  * @extends Roo.menu.BaseItem
15461  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15462  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15463  * activation and click handling.
15464  * @constructor
15465  * Creates a new Item
15466  * @param {Object} config Configuration options
15467  */
15468 Roo.menu.Item = function(config){
15469     Roo.menu.Item.superclass.constructor.call(this, config);
15470     if(this.menu){
15471         this.menu = Roo.menu.MenuMgr.get(this.menu);
15472     }
15473 };
15474 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15475     /**
15476      * @cfg {Roo.menu.Menu} menu
15477      * A Sub menu
15478      */
15479     /**
15480      * @cfg {String} text
15481      * The text to show on the menu item.
15482      */
15483     text: '',
15484      /**
15485      * @cfg {String} HTML to render in menu
15486      * The text to show on the menu item (HTML version).
15487      */
15488     html: '',
15489     /**
15490      * @cfg {String} icon
15491      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15492      */
15493     icon: undefined,
15494     /**
15495      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15496      */
15497     itemCls : "x-menu-item",
15498     /**
15499      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15500      */
15501     canActivate : true,
15502     /**
15503      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15504      */
15505     showDelay: 200,
15506     // doc'd in BaseItem
15507     hideDelay: 200,
15508
15509     // private
15510     ctype: "Roo.menu.Item",
15511     
15512     // private
15513     onRender : function(container, position){
15514         var el = document.createElement("a");
15515         el.hideFocus = true;
15516         el.unselectable = "on";
15517         el.href = this.href || "#";
15518         if(this.hrefTarget){
15519             el.target = this.hrefTarget;
15520         }
15521         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15522         
15523         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15524         
15525         el.innerHTML = String.format(
15526                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15527                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15528         this.el = el;
15529         Roo.menu.Item.superclass.onRender.call(this, container, position);
15530     },
15531
15532     /**
15533      * Sets the text to display in this menu item
15534      * @param {String} text The text to display
15535      * @param {Boolean} isHTML true to indicate text is pure html.
15536      */
15537     setText : function(text, isHTML){
15538         if (isHTML) {
15539             this.html = text;
15540         } else {
15541             this.text = text;
15542             this.html = '';
15543         }
15544         if(this.rendered){
15545             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15546      
15547             this.el.update(String.format(
15548                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15549                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15550             this.parentMenu.autoWidth();
15551         }
15552     },
15553
15554     // private
15555     handleClick : function(e){
15556         if(!this.href){ // if no link defined, stop the event automatically
15557             e.stopEvent();
15558         }
15559         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15560     },
15561
15562     // private
15563     activate : function(autoExpand){
15564         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15565             this.focus();
15566             if(autoExpand){
15567                 this.expandMenu();
15568             }
15569         }
15570         return true;
15571     },
15572
15573     // private
15574     shouldDeactivate : function(e){
15575         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15576             if(this.menu && this.menu.isVisible()){
15577                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15578             }
15579             return true;
15580         }
15581         return false;
15582     },
15583
15584     // private
15585     deactivate : function(){
15586         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15587         this.hideMenu();
15588     },
15589
15590     // private
15591     expandMenu : function(autoActivate){
15592         if(!this.disabled && this.menu){
15593             clearTimeout(this.hideTimer);
15594             delete this.hideTimer;
15595             if(!this.menu.isVisible() && !this.showTimer){
15596                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15597             }else if (this.menu.isVisible() && autoActivate){
15598                 this.menu.tryActivate(0, 1);
15599             }
15600         }
15601     },
15602
15603     // private
15604     deferExpand : function(autoActivate){
15605         delete this.showTimer;
15606         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15607         if(autoActivate){
15608             this.menu.tryActivate(0, 1);
15609         }
15610     },
15611
15612     // private
15613     hideMenu : function(){
15614         clearTimeout(this.showTimer);
15615         delete this.showTimer;
15616         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15617             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15618         }
15619     },
15620
15621     // private
15622     deferHide : function(){
15623         delete this.hideTimer;
15624         this.menu.hide();
15625     }
15626 });/*
15627  * Based on:
15628  * Ext JS Library 1.1.1
15629  * Copyright(c) 2006-2007, Ext JS, LLC.
15630  *
15631  * Originally Released Under LGPL - original licence link has changed is not relivant.
15632  *
15633  * Fork - LGPL
15634  * <script type="text/javascript">
15635  */
15636  
15637 /**
15638  * @class Roo.menu.CheckItem
15639  * @extends Roo.menu.Item
15640  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15641  * @constructor
15642  * Creates a new CheckItem
15643  * @param {Object} config Configuration options
15644  */
15645 Roo.menu.CheckItem = function(config){
15646     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15647     this.addEvents({
15648         /**
15649          * @event beforecheckchange
15650          * Fires before the checked value is set, providing an opportunity to cancel if needed
15651          * @param {Roo.menu.CheckItem} this
15652          * @param {Boolean} checked The new checked value that will be set
15653          */
15654         "beforecheckchange" : true,
15655         /**
15656          * @event checkchange
15657          * Fires after the checked value has been set
15658          * @param {Roo.menu.CheckItem} this
15659          * @param {Boolean} checked The checked value that was set
15660          */
15661         "checkchange" : true
15662     });
15663     if(this.checkHandler){
15664         this.on('checkchange', this.checkHandler, this.scope);
15665     }
15666 };
15667 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15668     /**
15669      * @cfg {String} group
15670      * All check items with the same group name will automatically be grouped into a single-select
15671      * radio button group (defaults to '')
15672      */
15673     /**
15674      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15675      */
15676     itemCls : "x-menu-item x-menu-check-item",
15677     /**
15678      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15679      */
15680     groupClass : "x-menu-group-item",
15681
15682     /**
15683      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15684      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15685      * initialized with checked = true will be rendered as checked.
15686      */
15687     checked: false,
15688
15689     // private
15690     ctype: "Roo.menu.CheckItem",
15691
15692     // private
15693     onRender : function(c){
15694         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15695         if(this.group){
15696             this.el.addClass(this.groupClass);
15697         }
15698         Roo.menu.MenuMgr.registerCheckable(this);
15699         if(this.checked){
15700             this.checked = false;
15701             this.setChecked(true, true);
15702         }
15703     },
15704
15705     // private
15706     destroy : function(){
15707         if(this.rendered){
15708             Roo.menu.MenuMgr.unregisterCheckable(this);
15709         }
15710         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15711     },
15712
15713     /**
15714      * Set the checked state of this item
15715      * @param {Boolean} checked The new checked value
15716      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15717      */
15718     setChecked : function(state, suppressEvent){
15719         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15720             if(this.container){
15721                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15722             }
15723             this.checked = state;
15724             if(suppressEvent !== true){
15725                 this.fireEvent("checkchange", this, state);
15726             }
15727         }
15728     },
15729
15730     // private
15731     handleClick : function(e){
15732        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15733            this.setChecked(!this.checked);
15734        }
15735        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15736     }
15737 });/*
15738  * Based on:
15739  * Ext JS Library 1.1.1
15740  * Copyright(c) 2006-2007, Ext JS, LLC.
15741  *
15742  * Originally Released Under LGPL - original licence link has changed is not relivant.
15743  *
15744  * Fork - LGPL
15745  * <script type="text/javascript">
15746  */
15747  
15748 /**
15749  * @class Roo.menu.DateItem
15750  * @extends Roo.menu.Adapter
15751  * A menu item that wraps the {@link Roo.DatPicker} component.
15752  * @constructor
15753  * Creates a new DateItem
15754  * @param {Object} config Configuration options
15755  */
15756 Roo.menu.DateItem = function(config){
15757     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15758     /** The Roo.DatePicker object @type Roo.DatePicker */
15759     this.picker = this.component;
15760     this.addEvents({select: true});
15761     
15762     this.picker.on("render", function(picker){
15763         picker.getEl().swallowEvent("click");
15764         picker.container.addClass("x-menu-date-item");
15765     });
15766
15767     this.picker.on("select", this.onSelect, this);
15768 };
15769
15770 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15771     // private
15772     onSelect : function(picker, date){
15773         this.fireEvent("select", this, date, picker);
15774         Roo.menu.DateItem.superclass.handleClick.call(this);
15775     }
15776 });/*
15777  * Based on:
15778  * Ext JS Library 1.1.1
15779  * Copyright(c) 2006-2007, Ext JS, LLC.
15780  *
15781  * Originally Released Under LGPL - original licence link has changed is not relivant.
15782  *
15783  * Fork - LGPL
15784  * <script type="text/javascript">
15785  */
15786  
15787 /**
15788  * @class Roo.menu.ColorItem
15789  * @extends Roo.menu.Adapter
15790  * A menu item that wraps the {@link Roo.ColorPalette} component.
15791  * @constructor
15792  * Creates a new ColorItem
15793  * @param {Object} config Configuration options
15794  */
15795 Roo.menu.ColorItem = function(config){
15796     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15797     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15798     this.palette = this.component;
15799     this.relayEvents(this.palette, ["select"]);
15800     if(this.selectHandler){
15801         this.on('select', this.selectHandler, this.scope);
15802     }
15803 };
15804 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15805  * Based on:
15806  * Ext JS Library 1.1.1
15807  * Copyright(c) 2006-2007, Ext JS, LLC.
15808  *
15809  * Originally Released Under LGPL - original licence link has changed is not relivant.
15810  *
15811  * Fork - LGPL
15812  * <script type="text/javascript">
15813  */
15814  
15815
15816 /**
15817  * @class Roo.menu.DateMenu
15818  * @extends Roo.menu.Menu
15819  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15820  * @constructor
15821  * Creates a new DateMenu
15822  * @param {Object} config Configuration options
15823  */
15824 Roo.menu.DateMenu = function(config){
15825     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15826     this.plain = true;
15827     var di = new Roo.menu.DateItem(config);
15828     this.add(di);
15829     /**
15830      * The {@link Roo.DatePicker} instance for this DateMenu
15831      * @type DatePicker
15832      */
15833     this.picker = di.picker;
15834     /**
15835      * @event select
15836      * @param {DatePicker} picker
15837      * @param {Date} date
15838      */
15839     this.relayEvents(di, ["select"]);
15840     this.on('beforeshow', function(){
15841         if(this.picker){
15842             this.picker.hideMonthPicker(false);
15843         }
15844     }, this);
15845 };
15846 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15847     cls:'x-date-menu'
15848 });/*
15849  * Based on:
15850  * Ext JS Library 1.1.1
15851  * Copyright(c) 2006-2007, Ext JS, LLC.
15852  *
15853  * Originally Released Under LGPL - original licence link has changed is not relivant.
15854  *
15855  * Fork - LGPL
15856  * <script type="text/javascript">
15857  */
15858  
15859
15860 /**
15861  * @class Roo.menu.ColorMenu
15862  * @extends Roo.menu.Menu
15863  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15864  * @constructor
15865  * Creates a new ColorMenu
15866  * @param {Object} config Configuration options
15867  */
15868 Roo.menu.ColorMenu = function(config){
15869     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15870     this.plain = true;
15871     var ci = new Roo.menu.ColorItem(config);
15872     this.add(ci);
15873     /**
15874      * The {@link Roo.ColorPalette} instance for this ColorMenu
15875      * @type ColorPalette
15876      */
15877     this.palette = ci.palette;
15878     /**
15879      * @event select
15880      * @param {ColorPalette} palette
15881      * @param {String} color
15882      */
15883     this.relayEvents(ci, ["select"]);
15884 };
15885 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15886  * Based on:
15887  * Ext JS Library 1.1.1
15888  * Copyright(c) 2006-2007, Ext JS, LLC.
15889  *
15890  * Originally Released Under LGPL - original licence link has changed is not relivant.
15891  *
15892  * Fork - LGPL
15893  * <script type="text/javascript">
15894  */
15895  
15896 /**
15897  * @class Roo.form.TextItem
15898  * @extends Roo.BoxComponent
15899  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15900  * @constructor
15901  * Creates a new TextItem
15902  * @param {Object} config Configuration options
15903  */
15904 Roo.form.TextItem = function(config){
15905     Roo.form.TextItem.superclass.constructor.call(this, config);
15906 };
15907
15908 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15909     
15910     /**
15911      * @cfg {String} tag the tag for this item (default div)
15912      */
15913     tag : 'div',
15914     /**
15915      * @cfg {String} html the content for this item
15916      */
15917     html : '',
15918     
15919     getAutoCreate : function()
15920     {
15921         var cfg = {
15922             id: this.id,
15923             tag: this.tag,
15924             html: this.html,
15925             cls: 'x-form-item'
15926         };
15927         
15928         return cfg;
15929         
15930     },
15931     
15932     onRender : function(ct, position)
15933     {
15934         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15935         
15936         if(!this.el){
15937             var cfg = this.getAutoCreate();
15938             if(!cfg.name){
15939                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15940             }
15941             if (!cfg.name.length) {
15942                 delete cfg.name;
15943             }
15944             this.el = ct.createChild(cfg, position);
15945         }
15946     },
15947     /*
15948      * setHTML
15949      * @param {String} html update the Contents of the element.
15950      */
15951     setHTML : function(html)
15952     {
15953         this.fieldEl.dom.innerHTML = html;
15954     }
15955     
15956 });/*
15957  * Based on:
15958  * Ext JS Library 1.1.1
15959  * Copyright(c) 2006-2007, Ext JS, LLC.
15960  *
15961  * Originally Released Under LGPL - original licence link has changed is not relivant.
15962  *
15963  * Fork - LGPL
15964  * <script type="text/javascript">
15965  */
15966  
15967 /**
15968  * @class Roo.form.Field
15969  * @extends Roo.BoxComponent
15970  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15971  * @constructor
15972  * Creates a new Field
15973  * @param {Object} config Configuration options
15974  */
15975 Roo.form.Field = function(config){
15976     Roo.form.Field.superclass.constructor.call(this, config);
15977 };
15978
15979 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15980     /**
15981      * @cfg {String} fieldLabel Label to use when rendering a form.
15982      */
15983        /**
15984      * @cfg {String} qtip Mouse over tip
15985      */
15986      
15987     /**
15988      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15989      */
15990     invalidClass : "x-form-invalid",
15991     /**
15992      * @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")
15993      */
15994     invalidText : "The value in this field is invalid",
15995     /**
15996      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15997      */
15998     focusClass : "x-form-focus",
15999     /**
16000      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16001       automatic validation (defaults to "keyup").
16002      */
16003     validationEvent : "keyup",
16004     /**
16005      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16006      */
16007     validateOnBlur : true,
16008     /**
16009      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16010      */
16011     validationDelay : 250,
16012     /**
16013      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16014      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16015      */
16016     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16017     /**
16018      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16019      */
16020     fieldClass : "x-form-field",
16021     /**
16022      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16023      *<pre>
16024 Value         Description
16025 -----------   ----------------------------------------------------------------------
16026 qtip          Display a quick tip when the user hovers over the field
16027 title         Display a default browser title attribute popup
16028 under         Add a block div beneath the field containing the error text
16029 side          Add an error icon to the right of the field with a popup on hover
16030 [element id]  Add the error text directly to the innerHTML of the specified element
16031 </pre>
16032      */
16033     msgTarget : 'qtip',
16034     /**
16035      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16036      */
16037     msgFx : 'normal',
16038
16039     /**
16040      * @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.
16041      */
16042     readOnly : false,
16043
16044     /**
16045      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16046      */
16047     disabled : false,
16048
16049     /**
16050      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16051      */
16052     inputType : undefined,
16053     
16054     /**
16055      * @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).
16056          */
16057         tabIndex : undefined,
16058         
16059     // private
16060     isFormField : true,
16061
16062     // private
16063     hasFocus : false,
16064     /**
16065      * @property {Roo.Element} fieldEl
16066      * Element Containing the rendered Field (with label etc.)
16067      */
16068     /**
16069      * @cfg {Mixed} value A value to initialize this field with.
16070      */
16071     value : undefined,
16072
16073     /**
16074      * @cfg {String} name The field's HTML name attribute.
16075      */
16076     /**
16077      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16078      */
16079     // private
16080     loadedValue : false,
16081      
16082      
16083         // private ??
16084         initComponent : function(){
16085         Roo.form.Field.superclass.initComponent.call(this);
16086         this.addEvents({
16087             /**
16088              * @event focus
16089              * Fires when this field receives input focus.
16090              * @param {Roo.form.Field} this
16091              */
16092             focus : true,
16093             /**
16094              * @event blur
16095              * Fires when this field loses input focus.
16096              * @param {Roo.form.Field} this
16097              */
16098             blur : true,
16099             /**
16100              * @event specialkey
16101              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16102              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16103              * @param {Roo.form.Field} this
16104              * @param {Roo.EventObject} e The event object
16105              */
16106             specialkey : true,
16107             /**
16108              * @event change
16109              * Fires just before the field blurs if the field value has changed.
16110              * @param {Roo.form.Field} this
16111              * @param {Mixed} newValue The new value
16112              * @param {Mixed} oldValue The original value
16113              */
16114             change : true,
16115             /**
16116              * @event invalid
16117              * Fires after the field has been marked as invalid.
16118              * @param {Roo.form.Field} this
16119              * @param {String} msg The validation message
16120              */
16121             invalid : true,
16122             /**
16123              * @event valid
16124              * Fires after the field has been validated with no errors.
16125              * @param {Roo.form.Field} this
16126              */
16127             valid : true,
16128              /**
16129              * @event keyup
16130              * Fires after the key up
16131              * @param {Roo.form.Field} this
16132              * @param {Roo.EventObject}  e The event Object
16133              */
16134             keyup : true
16135         });
16136     },
16137
16138     /**
16139      * Returns the name attribute of the field if available
16140      * @return {String} name The field name
16141      */
16142     getName: function(){
16143          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16144     },
16145
16146     // private
16147     onRender : function(ct, position){
16148         Roo.form.Field.superclass.onRender.call(this, ct, position);
16149         if(!this.el){
16150             var cfg = this.getAutoCreate();
16151             if(!cfg.name){
16152                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16153             }
16154             if (!cfg.name.length) {
16155                 delete cfg.name;
16156             }
16157             if(this.inputType){
16158                 cfg.type = this.inputType;
16159             }
16160             this.el = ct.createChild(cfg, position);
16161         }
16162         var type = this.el.dom.type;
16163         if(type){
16164             if(type == 'password'){
16165                 type = 'text';
16166             }
16167             this.el.addClass('x-form-'+type);
16168         }
16169         if(this.readOnly){
16170             this.el.dom.readOnly = true;
16171         }
16172         if(this.tabIndex !== undefined){
16173             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16174         }
16175
16176         this.el.addClass([this.fieldClass, this.cls]);
16177         this.initValue();
16178     },
16179
16180     /**
16181      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16182      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16183      * @return {Roo.form.Field} this
16184      */
16185     applyTo : function(target){
16186         this.allowDomMove = false;
16187         this.el = Roo.get(target);
16188         this.render(this.el.dom.parentNode);
16189         return this;
16190     },
16191
16192     // private
16193     initValue : function(){
16194         if(this.value !== undefined){
16195             this.setValue(this.value);
16196         }else if(this.el.dom.value.length > 0){
16197             this.setValue(this.el.dom.value);
16198         }
16199     },
16200
16201     /**
16202      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16203      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16204      */
16205     isDirty : function() {
16206         if(this.disabled) {
16207             return false;
16208         }
16209         return String(this.getValue()) !== String(this.originalValue);
16210     },
16211
16212     /**
16213      * stores the current value in loadedValue
16214      */
16215     resetHasChanged : function()
16216     {
16217         this.loadedValue = String(this.getValue());
16218     },
16219     /**
16220      * checks the current value against the 'loaded' value.
16221      * Note - will return false if 'resetHasChanged' has not been called first.
16222      */
16223     hasChanged : function()
16224     {
16225         if(this.disabled || this.readOnly) {
16226             return false;
16227         }
16228         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16229     },
16230     
16231     
16232     
16233     // private
16234     afterRender : function(){
16235         Roo.form.Field.superclass.afterRender.call(this);
16236         this.initEvents();
16237     },
16238
16239     // private
16240     fireKey : function(e){
16241         //Roo.log('field ' + e.getKey());
16242         if(e.isNavKeyPress()){
16243             this.fireEvent("specialkey", this, e);
16244         }
16245     },
16246
16247     /**
16248      * Resets the current field value to the originally loaded value and clears any validation messages
16249      */
16250     reset : function(){
16251         this.setValue(this.resetValue);
16252         this.originalValue = this.getValue();
16253         this.clearInvalid();
16254     },
16255
16256     // private
16257     initEvents : function(){
16258         // safari killled keypress - so keydown is now used..
16259         this.el.on("keydown" , this.fireKey,  this);
16260         this.el.on("focus", this.onFocus,  this);
16261         this.el.on("blur", this.onBlur,  this);
16262         this.el.relayEvent('keyup', this);
16263
16264         // reference to original value for reset
16265         this.originalValue = this.getValue();
16266         this.resetValue =  this.getValue();
16267     },
16268
16269     // private
16270     onFocus : function(){
16271         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16272             this.el.addClass(this.focusClass);
16273         }
16274         if(!this.hasFocus){
16275             this.hasFocus = true;
16276             this.startValue = this.getValue();
16277             this.fireEvent("focus", this);
16278         }
16279     },
16280
16281     beforeBlur : Roo.emptyFn,
16282
16283     // private
16284     onBlur : function(){
16285         this.beforeBlur();
16286         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16287             this.el.removeClass(this.focusClass);
16288         }
16289         this.hasFocus = false;
16290         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16291             this.validate();
16292         }
16293         var v = this.getValue();
16294         if(String(v) !== String(this.startValue)){
16295             this.fireEvent('change', this, v, this.startValue);
16296         }
16297         this.fireEvent("blur", this);
16298     },
16299
16300     /**
16301      * Returns whether or not the field value is currently valid
16302      * @param {Boolean} preventMark True to disable marking the field invalid
16303      * @return {Boolean} True if the value is valid, else false
16304      */
16305     isValid : function(preventMark){
16306         if(this.disabled){
16307             return true;
16308         }
16309         var restore = this.preventMark;
16310         this.preventMark = preventMark === true;
16311         var v = this.validateValue(this.processValue(this.getRawValue()));
16312         this.preventMark = restore;
16313         return v;
16314     },
16315
16316     /**
16317      * Validates the field value
16318      * @return {Boolean} True if the value is valid, else false
16319      */
16320     validate : function(){
16321         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16322             this.clearInvalid();
16323             return true;
16324         }
16325         return false;
16326     },
16327
16328     processValue : function(value){
16329         return value;
16330     },
16331
16332     // private
16333     // Subclasses should provide the validation implementation by overriding this
16334     validateValue : function(value){
16335         return true;
16336     },
16337
16338     /**
16339      * Mark this field as invalid
16340      * @param {String} msg The validation message
16341      */
16342     markInvalid : function(msg){
16343         if(!this.rendered || this.preventMark){ // not rendered
16344             return;
16345         }
16346         
16347         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16348         
16349         obj.el.addClass(this.invalidClass);
16350         msg = msg || this.invalidText;
16351         switch(this.msgTarget){
16352             case 'qtip':
16353                 obj.el.dom.qtip = msg;
16354                 obj.el.dom.qclass = 'x-form-invalid-tip';
16355                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16356                     Roo.QuickTips.enable();
16357                 }
16358                 break;
16359             case 'title':
16360                 this.el.dom.title = msg;
16361                 break;
16362             case 'under':
16363                 if(!this.errorEl){
16364                     var elp = this.el.findParent('.x-form-element', 5, true);
16365                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16366                     this.errorEl.setWidth(elp.getWidth(true)-20);
16367                 }
16368                 this.errorEl.update(msg);
16369                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16370                 break;
16371             case 'side':
16372                 if(!this.errorIcon){
16373                     var elp = this.el.findParent('.x-form-element', 5, true);
16374                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16375                 }
16376                 this.alignErrorIcon();
16377                 this.errorIcon.dom.qtip = msg;
16378                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16379                 this.errorIcon.show();
16380                 this.on('resize', this.alignErrorIcon, this);
16381                 break;
16382             default:
16383                 var t = Roo.getDom(this.msgTarget);
16384                 t.innerHTML = msg;
16385                 t.style.display = this.msgDisplay;
16386                 break;
16387         }
16388         this.fireEvent('invalid', this, msg);
16389     },
16390
16391     // private
16392     alignErrorIcon : function(){
16393         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16394     },
16395
16396     /**
16397      * Clear any invalid styles/messages for this field
16398      */
16399     clearInvalid : function(){
16400         if(!this.rendered || this.preventMark){ // not rendered
16401             return;
16402         }
16403         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16404         
16405         obj.el.removeClass(this.invalidClass);
16406         switch(this.msgTarget){
16407             case 'qtip':
16408                 obj.el.dom.qtip = '';
16409                 break;
16410             case 'title':
16411                 this.el.dom.title = '';
16412                 break;
16413             case 'under':
16414                 if(this.errorEl){
16415                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16416                 }
16417                 break;
16418             case 'side':
16419                 if(this.errorIcon){
16420                     this.errorIcon.dom.qtip = '';
16421                     this.errorIcon.hide();
16422                     this.un('resize', this.alignErrorIcon, this);
16423                 }
16424                 break;
16425             default:
16426                 var t = Roo.getDom(this.msgTarget);
16427                 t.innerHTML = '';
16428                 t.style.display = 'none';
16429                 break;
16430         }
16431         this.fireEvent('valid', this);
16432     },
16433
16434     /**
16435      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16436      * @return {Mixed} value The field value
16437      */
16438     getRawValue : function(){
16439         var v = this.el.getValue();
16440         
16441         return v;
16442     },
16443
16444     /**
16445      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16446      * @return {Mixed} value The field value
16447      */
16448     getValue : function(){
16449         var v = this.el.getValue();
16450          
16451         return v;
16452     },
16453
16454     /**
16455      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16456      * @param {Mixed} value The value to set
16457      */
16458     setRawValue : function(v){
16459         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16460     },
16461
16462     /**
16463      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16464      * @param {Mixed} value The value to set
16465      */
16466     setValue : function(v){
16467         this.value = v;
16468         if(this.rendered){
16469             this.el.dom.value = (v === null || v === undefined ? '' : v);
16470              this.validate();
16471         }
16472     },
16473
16474     adjustSize : function(w, h){
16475         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16476         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16477         return s;
16478     },
16479
16480     adjustWidth : function(tag, w){
16481         tag = tag.toLowerCase();
16482         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16483             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16484                 if(tag == 'input'){
16485                     return w + 2;
16486                 }
16487                 if(tag == 'textarea'){
16488                     return w-2;
16489                 }
16490             }else if(Roo.isOpera){
16491                 if(tag == 'input'){
16492                     return w + 2;
16493                 }
16494                 if(tag == 'textarea'){
16495                     return w-2;
16496                 }
16497             }
16498         }
16499         return w;
16500     }
16501 });
16502
16503
16504 // anything other than normal should be considered experimental
16505 Roo.form.Field.msgFx = {
16506     normal : {
16507         show: function(msgEl, f){
16508             msgEl.setDisplayed('block');
16509         },
16510
16511         hide : function(msgEl, f){
16512             msgEl.setDisplayed(false).update('');
16513         }
16514     },
16515
16516     slide : {
16517         show: function(msgEl, f){
16518             msgEl.slideIn('t', {stopFx:true});
16519         },
16520
16521         hide : function(msgEl, f){
16522             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16523         }
16524     },
16525
16526     slideRight : {
16527         show: function(msgEl, f){
16528             msgEl.fixDisplay();
16529             msgEl.alignTo(f.el, 'tl-tr');
16530             msgEl.slideIn('l', {stopFx:true});
16531         },
16532
16533         hide : function(msgEl, f){
16534             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16535         }
16536     }
16537 };/*
16538  * Based on:
16539  * Ext JS Library 1.1.1
16540  * Copyright(c) 2006-2007, Ext JS, LLC.
16541  *
16542  * Originally Released Under LGPL - original licence link has changed is not relivant.
16543  *
16544  * Fork - LGPL
16545  * <script type="text/javascript">
16546  */
16547  
16548
16549 /**
16550  * @class Roo.form.TextField
16551  * @extends Roo.form.Field
16552  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16553  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16554  * @constructor
16555  * Creates a new TextField
16556  * @param {Object} config Configuration options
16557  */
16558 Roo.form.TextField = function(config){
16559     Roo.form.TextField.superclass.constructor.call(this, config);
16560     this.addEvents({
16561         /**
16562          * @event autosize
16563          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16564          * according to the default logic, but this event provides a hook for the developer to apply additional
16565          * logic at runtime to resize the field if needed.
16566              * @param {Roo.form.Field} this This text field
16567              * @param {Number} width The new field width
16568              */
16569         autosize : true
16570     });
16571 };
16572
16573 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16574     /**
16575      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16576      */
16577     grow : false,
16578     /**
16579      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16580      */
16581     growMin : 30,
16582     /**
16583      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16584      */
16585     growMax : 800,
16586     /**
16587      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16588      */
16589     vtype : null,
16590     /**
16591      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16592      */
16593     maskRe : null,
16594     /**
16595      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16596      */
16597     disableKeyFilter : false,
16598     /**
16599      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16600      */
16601     allowBlank : true,
16602     /**
16603      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16604      */
16605     minLength : 0,
16606     /**
16607      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16608      */
16609     maxLength : Number.MAX_VALUE,
16610     /**
16611      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16612      */
16613     minLengthText : "The minimum length for this field is {0}",
16614     /**
16615      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16616      */
16617     maxLengthText : "The maximum length for this field is {0}",
16618     /**
16619      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16620      */
16621     selectOnFocus : false,
16622     /**
16623      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16624      */    
16625     allowLeadingSpace : false,
16626     /**
16627      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16628      */
16629     blankText : "This field is required",
16630     /**
16631      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16632      * If available, this function will be called only after the basic validators all return true, and will be passed the
16633      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16634      */
16635     validator : null,
16636     /**
16637      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16638      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16639      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16640      */
16641     regex : null,
16642     /**
16643      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16644      */
16645     regexText : "",
16646     /**
16647      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16648      */
16649     emptyText : null,
16650    
16651
16652     // private
16653     initEvents : function()
16654     {
16655         if (this.emptyText) {
16656             this.el.attr('placeholder', this.emptyText);
16657         }
16658         
16659         Roo.form.TextField.superclass.initEvents.call(this);
16660         if(this.validationEvent == 'keyup'){
16661             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16662             this.el.on('keyup', this.filterValidation, this);
16663         }
16664         else if(this.validationEvent !== false){
16665             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16666         }
16667         
16668         if(this.selectOnFocus){
16669             this.on("focus", this.preFocus, this);
16670         }
16671         if (!this.allowLeadingSpace) {
16672             this.on('blur', this.cleanLeadingSpace, this);
16673         }
16674         
16675         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16676             this.el.on("keypress", this.filterKeys, this);
16677         }
16678         if(this.grow){
16679             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16680             this.el.on("click", this.autoSize,  this);
16681         }
16682         if(this.el.is('input[type=password]') && Roo.isSafari){
16683             this.el.on('keydown', this.SafariOnKeyDown, this);
16684         }
16685     },
16686
16687     processValue : function(value){
16688         if(this.stripCharsRe){
16689             var newValue = value.replace(this.stripCharsRe, '');
16690             if(newValue !== value){
16691                 this.setRawValue(newValue);
16692                 return newValue;
16693             }
16694         }
16695         return value;
16696     },
16697
16698     filterValidation : function(e){
16699         if(!e.isNavKeyPress()){
16700             this.validationTask.delay(this.validationDelay);
16701         }
16702     },
16703
16704     // private
16705     onKeyUp : function(e){
16706         if(!e.isNavKeyPress()){
16707             this.autoSize();
16708         }
16709     },
16710     // private - clean the leading white space
16711     cleanLeadingSpace : function(e)
16712     {
16713         if ( this.inputType == 'file') {
16714             return;
16715         }
16716         
16717         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16718     },
16719     /**
16720      * Resets the current field value to the originally-loaded value and clears any validation messages.
16721      *  
16722      */
16723     reset : function(){
16724         Roo.form.TextField.superclass.reset.call(this);
16725        
16726     }, 
16727     // private
16728     preFocus : function(){
16729         
16730         if(this.selectOnFocus){
16731             this.el.dom.select();
16732         }
16733     },
16734
16735     
16736     // private
16737     filterKeys : function(e){
16738         var k = e.getKey();
16739         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16740             return;
16741         }
16742         var c = e.getCharCode(), cc = String.fromCharCode(c);
16743         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16744             return;
16745         }
16746         if(!this.maskRe.test(cc)){
16747             e.stopEvent();
16748         }
16749     },
16750
16751     setValue : function(v){
16752         
16753         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16754         
16755         this.autoSize();
16756     },
16757
16758     /**
16759      * Validates a value according to the field's validation rules and marks the field as invalid
16760      * if the validation fails
16761      * @param {Mixed} value The value to validate
16762      * @return {Boolean} True if the value is valid, else false
16763      */
16764     validateValue : function(value){
16765         if(value.length < 1)  { // if it's blank
16766              if(this.allowBlank){
16767                 this.clearInvalid();
16768                 return true;
16769              }else{
16770                 this.markInvalid(this.blankText);
16771                 return false;
16772              }
16773         }
16774         if(value.length < this.minLength){
16775             this.markInvalid(String.format(this.minLengthText, this.minLength));
16776             return false;
16777         }
16778         if(value.length > this.maxLength){
16779             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16780             return false;
16781         }
16782         if(this.vtype){
16783             var vt = Roo.form.VTypes;
16784             if(!vt[this.vtype](value, this)){
16785                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16786                 return false;
16787             }
16788         }
16789         if(typeof this.validator == "function"){
16790             var msg = this.validator(value);
16791             if(msg !== true){
16792                 this.markInvalid(msg);
16793                 return false;
16794             }
16795         }
16796         if(this.regex && !this.regex.test(value)){
16797             this.markInvalid(this.regexText);
16798             return false;
16799         }
16800         return true;
16801     },
16802
16803     /**
16804      * Selects text in this field
16805      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16806      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16807      */
16808     selectText : function(start, end){
16809         var v = this.getRawValue();
16810         if(v.length > 0){
16811             start = start === undefined ? 0 : start;
16812             end = end === undefined ? v.length : end;
16813             var d = this.el.dom;
16814             if(d.setSelectionRange){
16815                 d.setSelectionRange(start, end);
16816             }else if(d.createTextRange){
16817                 var range = d.createTextRange();
16818                 range.moveStart("character", start);
16819                 range.moveEnd("character", v.length-end);
16820                 range.select();
16821             }
16822         }
16823     },
16824
16825     /**
16826      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16827      * This only takes effect if grow = true, and fires the autosize event.
16828      */
16829     autoSize : function(){
16830         if(!this.grow || !this.rendered){
16831             return;
16832         }
16833         if(!this.metrics){
16834             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16835         }
16836         var el = this.el;
16837         var v = el.dom.value;
16838         var d = document.createElement('div');
16839         d.appendChild(document.createTextNode(v));
16840         v = d.innerHTML;
16841         d = null;
16842         v += "&#160;";
16843         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16844         this.el.setWidth(w);
16845         this.fireEvent("autosize", this, w);
16846     },
16847     
16848     // private
16849     SafariOnKeyDown : function(event)
16850     {
16851         // this is a workaround for a password hang bug on chrome/ webkit.
16852         
16853         var isSelectAll = false;
16854         
16855         if(this.el.dom.selectionEnd > 0){
16856             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16857         }
16858         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16859             event.preventDefault();
16860             this.setValue('');
16861             return;
16862         }
16863         
16864         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16865             
16866             event.preventDefault();
16867             // this is very hacky as keydown always get's upper case.
16868             
16869             var cc = String.fromCharCode(event.getCharCode());
16870             
16871             
16872             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16873             
16874         }
16875         
16876         
16877     }
16878 });/*
16879  * Based on:
16880  * Ext JS Library 1.1.1
16881  * Copyright(c) 2006-2007, Ext JS, LLC.
16882  *
16883  * Originally Released Under LGPL - original licence link has changed is not relivant.
16884  *
16885  * Fork - LGPL
16886  * <script type="text/javascript">
16887  */
16888  
16889 /**
16890  * @class Roo.form.Hidden
16891  * @extends Roo.form.TextField
16892  * Simple Hidden element used on forms 
16893  * 
16894  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16895  * 
16896  * @constructor
16897  * Creates a new Hidden form element.
16898  * @param {Object} config Configuration options
16899  */
16900
16901
16902
16903 // easy hidden field...
16904 Roo.form.Hidden = function(config){
16905     Roo.form.Hidden.superclass.constructor.call(this, config);
16906 };
16907   
16908 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16909     fieldLabel:      '',
16910     inputType:      'hidden',
16911     width:          50,
16912     allowBlank:     true,
16913     labelSeparator: '',
16914     hidden:         true,
16915     itemCls :       'x-form-item-display-none'
16916
16917
16918 });
16919
16920
16921 /*
16922  * Based on:
16923  * Ext JS Library 1.1.1
16924  * Copyright(c) 2006-2007, Ext JS, LLC.
16925  *
16926  * Originally Released Under LGPL - original licence link has changed is not relivant.
16927  *
16928  * Fork - LGPL
16929  * <script type="text/javascript">
16930  */
16931  
16932 /**
16933  * @class Roo.form.TriggerField
16934  * @extends Roo.form.TextField
16935  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16936  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16937  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16938  * for which you can provide a custom implementation.  For example:
16939  * <pre><code>
16940 var trigger = new Roo.form.TriggerField();
16941 trigger.onTriggerClick = myTriggerFn;
16942 trigger.applyTo('my-field');
16943 </code></pre>
16944  *
16945  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16946  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16947  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16948  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16949  * @constructor
16950  * Create a new TriggerField.
16951  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16952  * to the base TextField)
16953  */
16954 Roo.form.TriggerField = function(config){
16955     this.mimicing = false;
16956     Roo.form.TriggerField.superclass.constructor.call(this, config);
16957 };
16958
16959 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16960     /**
16961      * @cfg {String} triggerClass A CSS class to apply to the trigger
16962      */
16963     /**
16964      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16965      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16966      */
16967     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16968     /**
16969      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16970      */
16971     hideTrigger:false,
16972
16973     /** @cfg {Boolean} grow @hide */
16974     /** @cfg {Number} growMin @hide */
16975     /** @cfg {Number} growMax @hide */
16976
16977     /**
16978      * @hide 
16979      * @method
16980      */
16981     autoSize: Roo.emptyFn,
16982     // private
16983     monitorTab : true,
16984     // private
16985     deferHeight : true,
16986
16987     
16988     actionMode : 'wrap',
16989     // private
16990     onResize : function(w, h){
16991         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16992         if(typeof w == 'number'){
16993             var x = w - this.trigger.getWidth();
16994             this.el.setWidth(this.adjustWidth('input', x));
16995             this.trigger.setStyle('left', x+'px');
16996         }
16997     },
16998
16999     // private
17000     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17001
17002     // private
17003     getResizeEl : function(){
17004         return this.wrap;
17005     },
17006
17007     // private
17008     getPositionEl : function(){
17009         return this.wrap;
17010     },
17011
17012     // private
17013     alignErrorIcon : function(){
17014         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17015     },
17016
17017     // private
17018     onRender : function(ct, position){
17019         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17020         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17021         this.trigger = this.wrap.createChild(this.triggerConfig ||
17022                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17023         if(this.hideTrigger){
17024             this.trigger.setDisplayed(false);
17025         }
17026         this.initTrigger();
17027         if(!this.width){
17028             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17029         }
17030     },
17031
17032     // private
17033     initTrigger : function(){
17034         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17035         this.trigger.addClassOnOver('x-form-trigger-over');
17036         this.trigger.addClassOnClick('x-form-trigger-click');
17037     },
17038
17039     // private
17040     onDestroy : function(){
17041         if(this.trigger){
17042             this.trigger.removeAllListeners();
17043             this.trigger.remove();
17044         }
17045         if(this.wrap){
17046             this.wrap.remove();
17047         }
17048         Roo.form.TriggerField.superclass.onDestroy.call(this);
17049     },
17050
17051     // private
17052     onFocus : function(){
17053         Roo.form.TriggerField.superclass.onFocus.call(this);
17054         if(!this.mimicing){
17055             this.wrap.addClass('x-trigger-wrap-focus');
17056             this.mimicing = true;
17057             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17058             if(this.monitorTab){
17059                 this.el.on("keydown", this.checkTab, this);
17060             }
17061         }
17062     },
17063
17064     // private
17065     checkTab : function(e){
17066         if(e.getKey() == e.TAB){
17067             this.triggerBlur();
17068         }
17069     },
17070
17071     // private
17072     onBlur : function(){
17073         // do nothing
17074     },
17075
17076     // private
17077     mimicBlur : function(e, t){
17078         if(!this.wrap.contains(t) && this.validateBlur()){
17079             this.triggerBlur();
17080         }
17081     },
17082
17083     // private
17084     triggerBlur : function(){
17085         this.mimicing = false;
17086         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17087         if(this.monitorTab){
17088             this.el.un("keydown", this.checkTab, this);
17089         }
17090         this.wrap.removeClass('x-trigger-wrap-focus');
17091         Roo.form.TriggerField.superclass.onBlur.call(this);
17092     },
17093
17094     // private
17095     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17096     validateBlur : function(e, t){
17097         return true;
17098     },
17099
17100     // private
17101     onDisable : function(){
17102         Roo.form.TriggerField.superclass.onDisable.call(this);
17103         if(this.wrap){
17104             this.wrap.addClass('x-item-disabled');
17105         }
17106     },
17107
17108     // private
17109     onEnable : function(){
17110         Roo.form.TriggerField.superclass.onEnable.call(this);
17111         if(this.wrap){
17112             this.wrap.removeClass('x-item-disabled');
17113         }
17114     },
17115
17116     // private
17117     onShow : function(){
17118         var ae = this.getActionEl();
17119         
17120         if(ae){
17121             ae.dom.style.display = '';
17122             ae.dom.style.visibility = 'visible';
17123         }
17124     },
17125
17126     // private
17127     
17128     onHide : function(){
17129         var ae = this.getActionEl();
17130         ae.dom.style.display = 'none';
17131     },
17132
17133     /**
17134      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17135      * by an implementing function.
17136      * @method
17137      * @param {EventObject} e
17138      */
17139     onTriggerClick : Roo.emptyFn
17140 });
17141
17142 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17143 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17144 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17145 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17146     initComponent : function(){
17147         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17148
17149         this.triggerConfig = {
17150             tag:'span', cls:'x-form-twin-triggers', cn:[
17151             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17152             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17153         ]};
17154     },
17155
17156     getTrigger : function(index){
17157         return this.triggers[index];
17158     },
17159
17160     initTrigger : function(){
17161         var ts = this.trigger.select('.x-form-trigger', true);
17162         this.wrap.setStyle('overflow', 'hidden');
17163         var triggerField = this;
17164         ts.each(function(t, all, index){
17165             t.hide = function(){
17166                 var w = triggerField.wrap.getWidth();
17167                 this.dom.style.display = 'none';
17168                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17169             };
17170             t.show = function(){
17171                 var w = triggerField.wrap.getWidth();
17172                 this.dom.style.display = '';
17173                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17174             };
17175             var triggerIndex = 'Trigger'+(index+1);
17176
17177             if(this['hide'+triggerIndex]){
17178                 t.dom.style.display = 'none';
17179             }
17180             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17181             t.addClassOnOver('x-form-trigger-over');
17182             t.addClassOnClick('x-form-trigger-click');
17183         }, this);
17184         this.triggers = ts.elements;
17185     },
17186
17187     onTrigger1Click : Roo.emptyFn,
17188     onTrigger2Click : Roo.emptyFn
17189 });/*
17190  * Based on:
17191  * Ext JS Library 1.1.1
17192  * Copyright(c) 2006-2007, Ext JS, LLC.
17193  *
17194  * Originally Released Under LGPL - original licence link has changed is not relivant.
17195  *
17196  * Fork - LGPL
17197  * <script type="text/javascript">
17198  */
17199  
17200 /**
17201  * @class Roo.form.TextArea
17202  * @extends Roo.form.TextField
17203  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17204  * support for auto-sizing.
17205  * @constructor
17206  * Creates a new TextArea
17207  * @param {Object} config Configuration options
17208  */
17209 Roo.form.TextArea = function(config){
17210     Roo.form.TextArea.superclass.constructor.call(this, config);
17211     // these are provided exchanges for backwards compat
17212     // minHeight/maxHeight were replaced by growMin/growMax to be
17213     // compatible with TextField growing config values
17214     if(this.minHeight !== undefined){
17215         this.growMin = this.minHeight;
17216     }
17217     if(this.maxHeight !== undefined){
17218         this.growMax = this.maxHeight;
17219     }
17220 };
17221
17222 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17223     /**
17224      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17225      */
17226     growMin : 60,
17227     /**
17228      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17229      */
17230     growMax: 1000,
17231     /**
17232      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17233      * in the field (equivalent to setting overflow: hidden, defaults to false)
17234      */
17235     preventScrollbars: false,
17236     /**
17237      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17238      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17239      */
17240
17241     // private
17242     onRender : function(ct, position){
17243         if(!this.el){
17244             this.defaultAutoCreate = {
17245                 tag: "textarea",
17246                 style:"width:300px;height:60px;",
17247                 autocomplete: "new-password"
17248             };
17249         }
17250         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17251         if(this.grow){
17252             this.textSizeEl = Roo.DomHelper.append(document.body, {
17253                 tag: "pre", cls: "x-form-grow-sizer"
17254             });
17255             if(this.preventScrollbars){
17256                 this.el.setStyle("overflow", "hidden");
17257             }
17258             this.el.setHeight(this.growMin);
17259         }
17260     },
17261
17262     onDestroy : function(){
17263         if(this.textSizeEl){
17264             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17265         }
17266         Roo.form.TextArea.superclass.onDestroy.call(this);
17267     },
17268
17269     // private
17270     onKeyUp : function(e){
17271         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17272             this.autoSize();
17273         }
17274     },
17275
17276     /**
17277      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17278      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17279      */
17280     autoSize : function(){
17281         if(!this.grow || !this.textSizeEl){
17282             return;
17283         }
17284         var el = this.el;
17285         var v = el.dom.value;
17286         var ts = this.textSizeEl;
17287
17288         ts.innerHTML = '';
17289         ts.appendChild(document.createTextNode(v));
17290         v = ts.innerHTML;
17291
17292         Roo.fly(ts).setWidth(this.el.getWidth());
17293         if(v.length < 1){
17294             v = "&#160;&#160;";
17295         }else{
17296             if(Roo.isIE){
17297                 v = v.replace(/\n/g, '<p>&#160;</p>');
17298             }
17299             v += "&#160;\n&#160;";
17300         }
17301         ts.innerHTML = v;
17302         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17303         if(h != this.lastHeight){
17304             this.lastHeight = h;
17305             this.el.setHeight(h);
17306             this.fireEvent("autosize", this, h);
17307         }
17308     }
17309 });/*
17310  * Based on:
17311  * Ext JS Library 1.1.1
17312  * Copyright(c) 2006-2007, Ext JS, LLC.
17313  *
17314  * Originally Released Under LGPL - original licence link has changed is not relivant.
17315  *
17316  * Fork - LGPL
17317  * <script type="text/javascript">
17318  */
17319  
17320
17321 /**
17322  * @class Roo.form.NumberField
17323  * @extends Roo.form.TextField
17324  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17325  * @constructor
17326  * Creates a new NumberField
17327  * @param {Object} config Configuration options
17328  */
17329 Roo.form.NumberField = function(config){
17330     Roo.form.NumberField.superclass.constructor.call(this, config);
17331 };
17332
17333 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17334     /**
17335      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17336      */
17337     fieldClass: "x-form-field x-form-num-field",
17338     /**
17339      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17340      */
17341     allowDecimals : true,
17342     /**
17343      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17344      */
17345     decimalSeparator : ".",
17346     /**
17347      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17348      */
17349     decimalPrecision : 2,
17350     /**
17351      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17352      */
17353     allowNegative : true,
17354     /**
17355      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17356      */
17357     minValue : Number.NEGATIVE_INFINITY,
17358     /**
17359      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17360      */
17361     maxValue : Number.MAX_VALUE,
17362     /**
17363      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17364      */
17365     minText : "The minimum value for this field is {0}",
17366     /**
17367      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17368      */
17369     maxText : "The maximum value for this field is {0}",
17370     /**
17371      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17372      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17373      */
17374     nanText : "{0} is not a valid number",
17375
17376     // private
17377     initEvents : function(){
17378         Roo.form.NumberField.superclass.initEvents.call(this);
17379         var allowed = "0123456789";
17380         if(this.allowDecimals){
17381             allowed += this.decimalSeparator;
17382         }
17383         if(this.allowNegative){
17384             allowed += "-";
17385         }
17386         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17387         var keyPress = function(e){
17388             var k = e.getKey();
17389             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17390                 return;
17391             }
17392             var c = e.getCharCode();
17393             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17394                 e.stopEvent();
17395             }
17396         };
17397         this.el.on("keypress", keyPress, this);
17398     },
17399
17400     // private
17401     validateValue : function(value){
17402         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17403             return false;
17404         }
17405         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17406              return true;
17407         }
17408         var num = this.parseValue(value);
17409         if(isNaN(num)){
17410             this.markInvalid(String.format(this.nanText, value));
17411             return false;
17412         }
17413         if(num < this.minValue){
17414             this.markInvalid(String.format(this.minText, this.minValue));
17415             return false;
17416         }
17417         if(num > this.maxValue){
17418             this.markInvalid(String.format(this.maxText, this.maxValue));
17419             return false;
17420         }
17421         return true;
17422     },
17423
17424     getValue : function(){
17425         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17426     },
17427
17428     // private
17429     parseValue : function(value){
17430         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17431         return isNaN(value) ? '' : value;
17432     },
17433
17434     // private
17435     fixPrecision : function(value){
17436         var nan = isNaN(value);
17437         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17438             return nan ? '' : value;
17439         }
17440         return parseFloat(value).toFixed(this.decimalPrecision);
17441     },
17442
17443     setValue : function(v){
17444         v = this.fixPrecision(v);
17445         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17446     },
17447
17448     // private
17449     decimalPrecisionFcn : function(v){
17450         return Math.floor(v);
17451     },
17452
17453     beforeBlur : function(){
17454         var v = this.parseValue(this.getRawValue());
17455         if(v){
17456             this.setValue(v);
17457         }
17458     }
17459 });/*
17460  * Based on:
17461  * Ext JS Library 1.1.1
17462  * Copyright(c) 2006-2007, Ext JS, LLC.
17463  *
17464  * Originally Released Under LGPL - original licence link has changed is not relivant.
17465  *
17466  * Fork - LGPL
17467  * <script type="text/javascript">
17468  */
17469  
17470 /**
17471  * @class Roo.form.DateField
17472  * @extends Roo.form.TriggerField
17473  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17474 * @constructor
17475 * Create a new DateField
17476 * @param {Object} config
17477  */
17478 Roo.form.DateField = function(config)
17479 {
17480     Roo.form.DateField.superclass.constructor.call(this, config);
17481     
17482       this.addEvents({
17483          
17484         /**
17485          * @event select
17486          * Fires when a date is selected
17487              * @param {Roo.form.DateField} combo This combo box
17488              * @param {Date} date The date selected
17489              */
17490         'select' : true
17491          
17492     });
17493     
17494     
17495     if(typeof this.minValue == "string") {
17496         this.minValue = this.parseDate(this.minValue);
17497     }
17498     if(typeof this.maxValue == "string") {
17499         this.maxValue = this.parseDate(this.maxValue);
17500     }
17501     this.ddMatch = null;
17502     if(this.disabledDates){
17503         var dd = this.disabledDates;
17504         var re = "(?:";
17505         for(var i = 0; i < dd.length; i++){
17506             re += dd[i];
17507             if(i != dd.length-1) {
17508                 re += "|";
17509             }
17510         }
17511         this.ddMatch = new RegExp(re + ")");
17512     }
17513 };
17514
17515 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17516     /**
17517      * @cfg {String} format
17518      * The default date format string which can be overriden for localization support.  The format must be
17519      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17520      */
17521     format : "m/d/y",
17522     /**
17523      * @cfg {String} altFormats
17524      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17525      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17526      */
17527     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17528     /**
17529      * @cfg {Array} disabledDays
17530      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17531      */
17532     disabledDays : null,
17533     /**
17534      * @cfg {String} disabledDaysText
17535      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17536      */
17537     disabledDaysText : "Disabled",
17538     /**
17539      * @cfg {Array} disabledDates
17540      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17541      * expression so they are very powerful. Some examples:
17542      * <ul>
17543      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17544      * <li>["03/08", "09/16"] would disable those days for every year</li>
17545      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17546      * <li>["03/../2006"] would disable every day in March 2006</li>
17547      * <li>["^03"] would disable every day in every March</li>
17548      * </ul>
17549      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17550      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17551      */
17552     disabledDates : null,
17553     /**
17554      * @cfg {String} disabledDatesText
17555      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17556      */
17557     disabledDatesText : "Disabled",
17558     /**
17559      * @cfg {Date/String} minValue
17560      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17561      * valid format (defaults to null).
17562      */
17563     minValue : null,
17564     /**
17565      * @cfg {Date/String} maxValue
17566      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17567      * valid format (defaults to null).
17568      */
17569     maxValue : null,
17570     /**
17571      * @cfg {String} minText
17572      * The error text to display when the date in the cell is before minValue (defaults to
17573      * 'The date in this field must be after {minValue}').
17574      */
17575     minText : "The date in this field must be equal to or after {0}",
17576     /**
17577      * @cfg {String} maxText
17578      * The error text to display when the date in the cell is after maxValue (defaults to
17579      * 'The date in this field must be before {maxValue}').
17580      */
17581     maxText : "The date in this field must be equal to or before {0}",
17582     /**
17583      * @cfg {String} invalidText
17584      * The error text to display when the date in the field is invalid (defaults to
17585      * '{value} is not a valid date - it must be in the format {format}').
17586      */
17587     invalidText : "{0} is not a valid date - it must be in the format {1}",
17588     /**
17589      * @cfg {String} triggerClass
17590      * An additional CSS class used to style the trigger button.  The trigger will always get the
17591      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17592      * which displays a calendar icon).
17593      */
17594     triggerClass : 'x-form-date-trigger',
17595     
17596
17597     /**
17598      * @cfg {Boolean} useIso
17599      * if enabled, then the date field will use a hidden field to store the 
17600      * real value as iso formated date. default (false)
17601      */ 
17602     useIso : false,
17603     /**
17604      * @cfg {String/Object} autoCreate
17605      * A DomHelper element spec, or true for a default element spec (defaults to
17606      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17607      */ 
17608     // private
17609     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17610     
17611     // private
17612     hiddenField: false,
17613     
17614     onRender : function(ct, position)
17615     {
17616         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17617         if (this.useIso) {
17618             //this.el.dom.removeAttribute('name'); 
17619             Roo.log("Changing name?");
17620             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17621             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17622                     'before', true);
17623             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17624             // prevent input submission
17625             this.hiddenName = this.name;
17626         }
17627             
17628             
17629     },
17630     
17631     // private
17632     validateValue : function(value)
17633     {
17634         value = this.formatDate(value);
17635         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17636             Roo.log('super failed');
17637             return false;
17638         }
17639         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17640              return true;
17641         }
17642         var svalue = value;
17643         value = this.parseDate(value);
17644         if(!value){
17645             Roo.log('parse date failed' + svalue);
17646             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17647             return false;
17648         }
17649         var time = value.getTime();
17650         if(this.minValue && time < this.minValue.getTime()){
17651             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17652             return false;
17653         }
17654         if(this.maxValue && time > this.maxValue.getTime()){
17655             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17656             return false;
17657         }
17658         if(this.disabledDays){
17659             var day = value.getDay();
17660             for(var i = 0; i < this.disabledDays.length; i++) {
17661                 if(day === this.disabledDays[i]){
17662                     this.markInvalid(this.disabledDaysText);
17663                     return false;
17664                 }
17665             }
17666         }
17667         var fvalue = this.formatDate(value);
17668         if(this.ddMatch && this.ddMatch.test(fvalue)){
17669             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17670             return false;
17671         }
17672         return true;
17673     },
17674
17675     // private
17676     // Provides logic to override the default TriggerField.validateBlur which just returns true
17677     validateBlur : function(){
17678         return !this.menu || !this.menu.isVisible();
17679     },
17680     
17681     getName: function()
17682     {
17683         // returns hidden if it's set..
17684         if (!this.rendered) {return ''};
17685         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17686         
17687     },
17688
17689     /**
17690      * Returns the current date value of the date field.
17691      * @return {Date} The date value
17692      */
17693     getValue : function(){
17694         
17695         return  this.hiddenField ?
17696                 this.hiddenField.value :
17697                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17698     },
17699
17700     /**
17701      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17702      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17703      * (the default format used is "m/d/y").
17704      * <br />Usage:
17705      * <pre><code>
17706 //All of these calls set the same date value (May 4, 2006)
17707
17708 //Pass a date object:
17709 var dt = new Date('5/4/06');
17710 dateField.setValue(dt);
17711
17712 //Pass a date string (default format):
17713 dateField.setValue('5/4/06');
17714
17715 //Pass a date string (custom format):
17716 dateField.format = 'Y-m-d';
17717 dateField.setValue('2006-5-4');
17718 </code></pre>
17719      * @param {String/Date} date The date or valid date string
17720      */
17721     setValue : function(date){
17722         if (this.hiddenField) {
17723             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17724         }
17725         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17726         // make sure the value field is always stored as a date..
17727         this.value = this.parseDate(date);
17728         
17729         
17730     },
17731
17732     // private
17733     parseDate : function(value){
17734         if(!value || value instanceof Date){
17735             return value;
17736         }
17737         var v = Date.parseDate(value, this.format);
17738          if (!v && this.useIso) {
17739             v = Date.parseDate(value, 'Y-m-d');
17740         }
17741         if(!v && this.altFormats){
17742             if(!this.altFormatsArray){
17743                 this.altFormatsArray = this.altFormats.split("|");
17744             }
17745             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17746                 v = Date.parseDate(value, this.altFormatsArray[i]);
17747             }
17748         }
17749         return v;
17750     },
17751
17752     // private
17753     formatDate : function(date, fmt){
17754         return (!date || !(date instanceof Date)) ?
17755                date : date.dateFormat(fmt || this.format);
17756     },
17757
17758     // private
17759     menuListeners : {
17760         select: function(m, d){
17761             
17762             this.setValue(d);
17763             this.fireEvent('select', this, d);
17764         },
17765         show : function(){ // retain focus styling
17766             this.onFocus();
17767         },
17768         hide : function(){
17769             this.focus.defer(10, this);
17770             var ml = this.menuListeners;
17771             this.menu.un("select", ml.select,  this);
17772             this.menu.un("show", ml.show,  this);
17773             this.menu.un("hide", ml.hide,  this);
17774         }
17775     },
17776
17777     // private
17778     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17779     onTriggerClick : function(){
17780         if(this.disabled){
17781             return;
17782         }
17783         if(this.menu == null){
17784             this.menu = new Roo.menu.DateMenu();
17785         }
17786         Roo.apply(this.menu.picker,  {
17787             showClear: this.allowBlank,
17788             minDate : this.minValue,
17789             maxDate : this.maxValue,
17790             disabledDatesRE : this.ddMatch,
17791             disabledDatesText : this.disabledDatesText,
17792             disabledDays : this.disabledDays,
17793             disabledDaysText : this.disabledDaysText,
17794             format : this.useIso ? 'Y-m-d' : this.format,
17795             minText : String.format(this.minText, this.formatDate(this.minValue)),
17796             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17797         });
17798         this.menu.on(Roo.apply({}, this.menuListeners, {
17799             scope:this
17800         }));
17801         this.menu.picker.setValue(this.getValue() || new Date());
17802         this.menu.show(this.el, "tl-bl?");
17803     },
17804
17805     beforeBlur : function(){
17806         var v = this.parseDate(this.getRawValue());
17807         if(v){
17808             this.setValue(v);
17809         }
17810     },
17811
17812     /*@
17813      * overide
17814      * 
17815      */
17816     isDirty : function() {
17817         if(this.disabled) {
17818             return false;
17819         }
17820         
17821         if(typeof(this.startValue) === 'undefined'){
17822             return false;
17823         }
17824         
17825         return String(this.getValue()) !== String(this.startValue);
17826         
17827     },
17828     // @overide
17829     cleanLeadingSpace : function(e)
17830     {
17831        return;
17832     }
17833     
17834 });/*
17835  * Based on:
17836  * Ext JS Library 1.1.1
17837  * Copyright(c) 2006-2007, Ext JS, LLC.
17838  *
17839  * Originally Released Under LGPL - original licence link has changed is not relivant.
17840  *
17841  * Fork - LGPL
17842  * <script type="text/javascript">
17843  */
17844  
17845 /**
17846  * @class Roo.form.MonthField
17847  * @extends Roo.form.TriggerField
17848  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17849 * @constructor
17850 * Create a new MonthField
17851 * @param {Object} config
17852  */
17853 Roo.form.MonthField = function(config){
17854     
17855     Roo.form.MonthField.superclass.constructor.call(this, config);
17856     
17857       this.addEvents({
17858          
17859         /**
17860          * @event select
17861          * Fires when a date is selected
17862              * @param {Roo.form.MonthFieeld} combo This combo box
17863              * @param {Date} date The date selected
17864              */
17865         'select' : true
17866          
17867     });
17868     
17869     
17870     if(typeof this.minValue == "string") {
17871         this.minValue = this.parseDate(this.minValue);
17872     }
17873     if(typeof this.maxValue == "string") {
17874         this.maxValue = this.parseDate(this.maxValue);
17875     }
17876     this.ddMatch = null;
17877     if(this.disabledDates){
17878         var dd = this.disabledDates;
17879         var re = "(?:";
17880         for(var i = 0; i < dd.length; i++){
17881             re += dd[i];
17882             if(i != dd.length-1) {
17883                 re += "|";
17884             }
17885         }
17886         this.ddMatch = new RegExp(re + ")");
17887     }
17888 };
17889
17890 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17891     /**
17892      * @cfg {String} format
17893      * The default date format string which can be overriden for localization support.  The format must be
17894      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17895      */
17896     format : "M Y",
17897     /**
17898      * @cfg {String} altFormats
17899      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17900      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17901      */
17902     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17903     /**
17904      * @cfg {Array} disabledDays
17905      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17906      */
17907     disabledDays : [0,1,2,3,4,5,6],
17908     /**
17909      * @cfg {String} disabledDaysText
17910      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17911      */
17912     disabledDaysText : "Disabled",
17913     /**
17914      * @cfg {Array} disabledDates
17915      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17916      * expression so they are very powerful. Some examples:
17917      * <ul>
17918      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17919      * <li>["03/08", "09/16"] would disable those days for every year</li>
17920      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17921      * <li>["03/../2006"] would disable every day in March 2006</li>
17922      * <li>["^03"] would disable every day in every March</li>
17923      * </ul>
17924      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17925      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17926      */
17927     disabledDates : null,
17928     /**
17929      * @cfg {String} disabledDatesText
17930      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17931      */
17932     disabledDatesText : "Disabled",
17933     /**
17934      * @cfg {Date/String} minValue
17935      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17936      * valid format (defaults to null).
17937      */
17938     minValue : null,
17939     /**
17940      * @cfg {Date/String} maxValue
17941      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17942      * valid format (defaults to null).
17943      */
17944     maxValue : null,
17945     /**
17946      * @cfg {String} minText
17947      * The error text to display when the date in the cell is before minValue (defaults to
17948      * 'The date in this field must be after {minValue}').
17949      */
17950     minText : "The date in this field must be equal to or after {0}",
17951     /**
17952      * @cfg {String} maxTextf
17953      * The error text to display when the date in the cell is after maxValue (defaults to
17954      * 'The date in this field must be before {maxValue}').
17955      */
17956     maxText : "The date in this field must be equal to or before {0}",
17957     /**
17958      * @cfg {String} invalidText
17959      * The error text to display when the date in the field is invalid (defaults to
17960      * '{value} is not a valid date - it must be in the format {format}').
17961      */
17962     invalidText : "{0} is not a valid date - it must be in the format {1}",
17963     /**
17964      * @cfg {String} triggerClass
17965      * An additional CSS class used to style the trigger button.  The trigger will always get the
17966      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17967      * which displays a calendar icon).
17968      */
17969     triggerClass : 'x-form-date-trigger',
17970     
17971
17972     /**
17973      * @cfg {Boolean} useIso
17974      * if enabled, then the date field will use a hidden field to store the 
17975      * real value as iso formated date. default (true)
17976      */ 
17977     useIso : true,
17978     /**
17979      * @cfg {String/Object} autoCreate
17980      * A DomHelper element spec, or true for a default element spec (defaults to
17981      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17982      */ 
17983     // private
17984     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17985     
17986     // private
17987     hiddenField: false,
17988     
17989     hideMonthPicker : false,
17990     
17991     onRender : function(ct, position)
17992     {
17993         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17994         if (this.useIso) {
17995             this.el.dom.removeAttribute('name'); 
17996             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17997                     'before', true);
17998             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17999             // prevent input submission
18000             this.hiddenName = this.name;
18001         }
18002             
18003             
18004     },
18005     
18006     // private
18007     validateValue : function(value)
18008     {
18009         value = this.formatDate(value);
18010         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18011             return false;
18012         }
18013         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18014              return true;
18015         }
18016         var svalue = value;
18017         value = this.parseDate(value);
18018         if(!value){
18019             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18020             return false;
18021         }
18022         var time = value.getTime();
18023         if(this.minValue && time < this.minValue.getTime()){
18024             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18025             return false;
18026         }
18027         if(this.maxValue && time > this.maxValue.getTime()){
18028             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18029             return false;
18030         }
18031         /*if(this.disabledDays){
18032             var day = value.getDay();
18033             for(var i = 0; i < this.disabledDays.length; i++) {
18034                 if(day === this.disabledDays[i]){
18035                     this.markInvalid(this.disabledDaysText);
18036                     return false;
18037                 }
18038             }
18039         }
18040         */
18041         var fvalue = this.formatDate(value);
18042         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18043             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18044             return false;
18045         }
18046         */
18047         return true;
18048     },
18049
18050     // private
18051     // Provides logic to override the default TriggerField.validateBlur which just returns true
18052     validateBlur : function(){
18053         return !this.menu || !this.menu.isVisible();
18054     },
18055
18056     /**
18057      * Returns the current date value of the date field.
18058      * @return {Date} The date value
18059      */
18060     getValue : function(){
18061         
18062         
18063         
18064         return  this.hiddenField ?
18065                 this.hiddenField.value :
18066                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18067     },
18068
18069     /**
18070      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18071      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18072      * (the default format used is "m/d/y").
18073      * <br />Usage:
18074      * <pre><code>
18075 //All of these calls set the same date value (May 4, 2006)
18076
18077 //Pass a date object:
18078 var dt = new Date('5/4/06');
18079 monthField.setValue(dt);
18080
18081 //Pass a date string (default format):
18082 monthField.setValue('5/4/06');
18083
18084 //Pass a date string (custom format):
18085 monthField.format = 'Y-m-d';
18086 monthField.setValue('2006-5-4');
18087 </code></pre>
18088      * @param {String/Date} date The date or valid date string
18089      */
18090     setValue : function(date){
18091         Roo.log('month setValue' + date);
18092         // can only be first of month..
18093         
18094         var val = this.parseDate(date);
18095         
18096         if (this.hiddenField) {
18097             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18098         }
18099         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18100         this.value = this.parseDate(date);
18101     },
18102
18103     // private
18104     parseDate : function(value){
18105         if(!value || value instanceof Date){
18106             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18107             return value;
18108         }
18109         var v = Date.parseDate(value, this.format);
18110         if (!v && this.useIso) {
18111             v = Date.parseDate(value, 'Y-m-d');
18112         }
18113         if (v) {
18114             // 
18115             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18116         }
18117         
18118         
18119         if(!v && this.altFormats){
18120             if(!this.altFormatsArray){
18121                 this.altFormatsArray = this.altFormats.split("|");
18122             }
18123             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18124                 v = Date.parseDate(value, this.altFormatsArray[i]);
18125             }
18126         }
18127         return v;
18128     },
18129
18130     // private
18131     formatDate : function(date, fmt){
18132         return (!date || !(date instanceof Date)) ?
18133                date : date.dateFormat(fmt || this.format);
18134     },
18135
18136     // private
18137     menuListeners : {
18138         select: function(m, d){
18139             this.setValue(d);
18140             this.fireEvent('select', this, d);
18141         },
18142         show : function(){ // retain focus styling
18143             this.onFocus();
18144         },
18145         hide : function(){
18146             this.focus.defer(10, this);
18147             var ml = this.menuListeners;
18148             this.menu.un("select", ml.select,  this);
18149             this.menu.un("show", ml.show,  this);
18150             this.menu.un("hide", ml.hide,  this);
18151         }
18152     },
18153     // private
18154     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18155     onTriggerClick : function(){
18156         if(this.disabled){
18157             return;
18158         }
18159         if(this.menu == null){
18160             this.menu = new Roo.menu.DateMenu();
18161            
18162         }
18163         
18164         Roo.apply(this.menu.picker,  {
18165             
18166             showClear: this.allowBlank,
18167             minDate : this.minValue,
18168             maxDate : this.maxValue,
18169             disabledDatesRE : this.ddMatch,
18170             disabledDatesText : this.disabledDatesText,
18171             
18172             format : this.useIso ? 'Y-m-d' : this.format,
18173             minText : String.format(this.minText, this.formatDate(this.minValue)),
18174             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18175             
18176         });
18177          this.menu.on(Roo.apply({}, this.menuListeners, {
18178             scope:this
18179         }));
18180        
18181         
18182         var m = this.menu;
18183         var p = m.picker;
18184         
18185         // hide month picker get's called when we called by 'before hide';
18186         
18187         var ignorehide = true;
18188         p.hideMonthPicker  = function(disableAnim){
18189             if (ignorehide) {
18190                 return;
18191             }
18192              if(this.monthPicker){
18193                 Roo.log("hideMonthPicker called");
18194                 if(disableAnim === true){
18195                     this.monthPicker.hide();
18196                 }else{
18197                     this.monthPicker.slideOut('t', {duration:.2});
18198                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18199                     p.fireEvent("select", this, this.value);
18200                     m.hide();
18201                 }
18202             }
18203         }
18204         
18205         Roo.log('picker set value');
18206         Roo.log(this.getValue());
18207         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18208         m.show(this.el, 'tl-bl?');
18209         ignorehide  = false;
18210         // this will trigger hideMonthPicker..
18211         
18212         
18213         // hidden the day picker
18214         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18215         
18216         
18217         
18218       
18219         
18220         p.showMonthPicker.defer(100, p);
18221     
18222         
18223        
18224     },
18225
18226     beforeBlur : function(){
18227         var v = this.parseDate(this.getRawValue());
18228         if(v){
18229             this.setValue(v);
18230         }
18231     }
18232
18233     /** @cfg {Boolean} grow @hide */
18234     /** @cfg {Number} growMin @hide */
18235     /** @cfg {Number} growMax @hide */
18236     /**
18237      * @hide
18238      * @method autoSize
18239      */
18240 });/*
18241  * Based on:
18242  * Ext JS Library 1.1.1
18243  * Copyright(c) 2006-2007, Ext JS, LLC.
18244  *
18245  * Originally Released Under LGPL - original licence link has changed is not relivant.
18246  *
18247  * Fork - LGPL
18248  * <script type="text/javascript">
18249  */
18250  
18251
18252 /**
18253  * @class Roo.form.ComboBox
18254  * @extends Roo.form.TriggerField
18255  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18256  * @constructor
18257  * Create a new ComboBox.
18258  * @param {Object} config Configuration options
18259  */
18260 Roo.form.ComboBox = function(config){
18261     Roo.form.ComboBox.superclass.constructor.call(this, config);
18262     this.addEvents({
18263         /**
18264          * @event expand
18265          * Fires when the dropdown list is expanded
18266              * @param {Roo.form.ComboBox} combo This combo box
18267              */
18268         'expand' : true,
18269         /**
18270          * @event collapse
18271          * Fires when the dropdown list is collapsed
18272              * @param {Roo.form.ComboBox} combo This combo box
18273              */
18274         'collapse' : true,
18275         /**
18276          * @event beforeselect
18277          * Fires before a list item is selected. Return false to cancel the selection.
18278              * @param {Roo.form.ComboBox} combo This combo box
18279              * @param {Roo.data.Record} record The data record returned from the underlying store
18280              * @param {Number} index The index of the selected item in the dropdown list
18281              */
18282         'beforeselect' : true,
18283         /**
18284          * @event select
18285          * Fires when a list item is selected
18286              * @param {Roo.form.ComboBox} combo This combo box
18287              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18288              * @param {Number} index The index of the selected item in the dropdown list
18289              */
18290         'select' : true,
18291         /**
18292          * @event beforequery
18293          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18294          * The event object passed has these properties:
18295              * @param {Roo.form.ComboBox} combo This combo box
18296              * @param {String} query The query
18297              * @param {Boolean} forceAll true to force "all" query
18298              * @param {Boolean} cancel true to cancel the query
18299              * @param {Object} e The query event object
18300              */
18301         'beforequery': true,
18302          /**
18303          * @event add
18304          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18305              * @param {Roo.form.ComboBox} combo This combo box
18306              */
18307         'add' : true,
18308         /**
18309          * @event edit
18310          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18311              * @param {Roo.form.ComboBox} combo This combo box
18312              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18313              */
18314         'edit' : true
18315         
18316         
18317     });
18318     if(this.transform){
18319         this.allowDomMove = false;
18320         var s = Roo.getDom(this.transform);
18321         if(!this.hiddenName){
18322             this.hiddenName = s.name;
18323         }
18324         if(!this.store){
18325             this.mode = 'local';
18326             var d = [], opts = s.options;
18327             for(var i = 0, len = opts.length;i < len; i++){
18328                 var o = opts[i];
18329                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18330                 if(o.selected) {
18331                     this.value = value;
18332                 }
18333                 d.push([value, o.text]);
18334             }
18335             this.store = new Roo.data.SimpleStore({
18336                 'id': 0,
18337                 fields: ['value', 'text'],
18338                 data : d
18339             });
18340             this.valueField = 'value';
18341             this.displayField = 'text';
18342         }
18343         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18344         if(!this.lazyRender){
18345             this.target = true;
18346             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18347             s.parentNode.removeChild(s); // remove it
18348             this.render(this.el.parentNode);
18349         }else{
18350             s.parentNode.removeChild(s); // remove it
18351         }
18352
18353     }
18354     if (this.store) {
18355         this.store = Roo.factory(this.store, Roo.data);
18356     }
18357     
18358     this.selectedIndex = -1;
18359     if(this.mode == 'local'){
18360         if(config.queryDelay === undefined){
18361             this.queryDelay = 10;
18362         }
18363         if(config.minChars === undefined){
18364             this.minChars = 0;
18365         }
18366     }
18367 };
18368
18369 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18370     /**
18371      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18372      */
18373     /**
18374      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18375      * rendering into an Roo.Editor, defaults to false)
18376      */
18377     /**
18378      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18379      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18380      */
18381     /**
18382      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18383      */
18384     /**
18385      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18386      * the dropdown list (defaults to undefined, with no header element)
18387      */
18388
18389      /**
18390      * @cfg {String/Roo.Template} tpl The template to use to render the output
18391      */
18392      
18393     // private
18394     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18395     /**
18396      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18397      */
18398     listWidth: undefined,
18399     /**
18400      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18401      * mode = 'remote' or 'text' if mode = 'local')
18402      */
18403     displayField: undefined,
18404     /**
18405      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18406      * mode = 'remote' or 'value' if mode = 'local'). 
18407      * Note: use of a valueField requires the user make a selection
18408      * in order for a value to be mapped.
18409      */
18410     valueField: undefined,
18411     
18412     
18413     /**
18414      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18415      * field's data value (defaults to the underlying DOM element's name)
18416      */
18417     hiddenName: undefined,
18418     /**
18419      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18420      */
18421     listClass: '',
18422     /**
18423      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18424      */
18425     selectedClass: 'x-combo-selected',
18426     /**
18427      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18428      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18429      * which displays a downward arrow icon).
18430      */
18431     triggerClass : 'x-form-arrow-trigger',
18432     /**
18433      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18434      */
18435     shadow:'sides',
18436     /**
18437      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18438      * anchor positions (defaults to 'tl-bl')
18439      */
18440     listAlign: 'tl-bl?',
18441     /**
18442      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18443      */
18444     maxHeight: 300,
18445     /**
18446      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18447      * query specified by the allQuery config option (defaults to 'query')
18448      */
18449     triggerAction: 'query',
18450     /**
18451      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18452      * (defaults to 4, does not apply if editable = false)
18453      */
18454     minChars : 4,
18455     /**
18456      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18457      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18458      */
18459     typeAhead: false,
18460     /**
18461      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18462      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18463      */
18464     queryDelay: 500,
18465     /**
18466      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18467      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18468      */
18469     pageSize: 0,
18470     /**
18471      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18472      * when editable = true (defaults to false)
18473      */
18474     selectOnFocus:false,
18475     /**
18476      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18477      */
18478     queryParam: 'query',
18479     /**
18480      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18481      * when mode = 'remote' (defaults to 'Loading...')
18482      */
18483     loadingText: 'Loading...',
18484     /**
18485      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18486      */
18487     resizable: false,
18488     /**
18489      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18490      */
18491     handleHeight : 8,
18492     /**
18493      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18494      * traditional select (defaults to true)
18495      */
18496     editable: true,
18497     /**
18498      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18499      */
18500     allQuery: '',
18501     /**
18502      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18503      */
18504     mode: 'remote',
18505     /**
18506      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18507      * listWidth has a higher value)
18508      */
18509     minListWidth : 70,
18510     /**
18511      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18512      * allow the user to set arbitrary text into the field (defaults to false)
18513      */
18514     forceSelection:false,
18515     /**
18516      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18517      * if typeAhead = true (defaults to 250)
18518      */
18519     typeAheadDelay : 250,
18520     /**
18521      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18522      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18523      */
18524     valueNotFoundText : undefined,
18525     /**
18526      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18527      */
18528     blockFocus : false,
18529     
18530     /**
18531      * @cfg {Boolean} disableClear Disable showing of clear button.
18532      */
18533     disableClear : false,
18534     /**
18535      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18536      */
18537     alwaysQuery : false,
18538     
18539     //private
18540     addicon : false,
18541     editicon: false,
18542     
18543     // element that contains real text value.. (when hidden is used..)
18544      
18545     // private
18546     onRender : function(ct, position)
18547     {
18548         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18549         
18550         if(this.hiddenName){
18551             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18552                     'before', true);
18553             this.hiddenField.value =
18554                 this.hiddenValue !== undefined ? this.hiddenValue :
18555                 this.value !== undefined ? this.value : '';
18556
18557             // prevent input submission
18558             this.el.dom.removeAttribute('name');
18559              
18560              
18561         }
18562         
18563         if(Roo.isGecko){
18564             this.el.dom.setAttribute('autocomplete', 'off');
18565         }
18566
18567         var cls = 'x-combo-list';
18568
18569         this.list = new Roo.Layer({
18570             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18571         });
18572
18573         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18574         this.list.setWidth(lw);
18575         this.list.swallowEvent('mousewheel');
18576         this.assetHeight = 0;
18577
18578         if(this.title){
18579             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18580             this.assetHeight += this.header.getHeight();
18581         }
18582
18583         this.innerList = this.list.createChild({cls:cls+'-inner'});
18584         this.innerList.on('mouseover', this.onViewOver, this);
18585         this.innerList.on('mousemove', this.onViewMove, this);
18586         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18587         
18588         if(this.allowBlank && !this.pageSize && !this.disableClear){
18589             this.footer = this.list.createChild({cls:cls+'-ft'});
18590             this.pageTb = new Roo.Toolbar(this.footer);
18591            
18592         }
18593         if(this.pageSize){
18594             this.footer = this.list.createChild({cls:cls+'-ft'});
18595             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18596                     {pageSize: this.pageSize});
18597             
18598         }
18599         
18600         if (this.pageTb && this.allowBlank && !this.disableClear) {
18601             var _this = this;
18602             this.pageTb.add(new Roo.Toolbar.Fill(), {
18603                 cls: 'x-btn-icon x-btn-clear',
18604                 text: '&#160;',
18605                 handler: function()
18606                 {
18607                     _this.collapse();
18608                     _this.clearValue();
18609                     _this.onSelect(false, -1);
18610                 }
18611             });
18612         }
18613         if (this.footer) {
18614             this.assetHeight += this.footer.getHeight();
18615         }
18616         
18617
18618         if(!this.tpl){
18619             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18620         }
18621
18622         this.view = new Roo.View(this.innerList, this.tpl, {
18623             singleSelect:true,
18624             store: this.store,
18625             selectedClass: this.selectedClass
18626         });
18627
18628         this.view.on('click', this.onViewClick, this);
18629
18630         this.store.on('beforeload', this.onBeforeLoad, this);
18631         this.store.on('load', this.onLoad, this);
18632         this.store.on('loadexception', this.onLoadException, this);
18633
18634         if(this.resizable){
18635             this.resizer = new Roo.Resizable(this.list,  {
18636                pinned:true, handles:'se'
18637             });
18638             this.resizer.on('resize', function(r, w, h){
18639                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18640                 this.listWidth = w;
18641                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18642                 this.restrictHeight();
18643             }, this);
18644             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18645         }
18646         if(!this.editable){
18647             this.editable = true;
18648             this.setEditable(false);
18649         }  
18650         
18651         
18652         if (typeof(this.events.add.listeners) != 'undefined') {
18653             
18654             this.addicon = this.wrap.createChild(
18655                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18656        
18657             this.addicon.on('click', function(e) {
18658                 this.fireEvent('add', this);
18659             }, this);
18660         }
18661         if (typeof(this.events.edit.listeners) != 'undefined') {
18662             
18663             this.editicon = this.wrap.createChild(
18664                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18665             if (this.addicon) {
18666                 this.editicon.setStyle('margin-left', '40px');
18667             }
18668             this.editicon.on('click', function(e) {
18669                 
18670                 // we fire even  if inothing is selected..
18671                 this.fireEvent('edit', this, this.lastData );
18672                 
18673             }, this);
18674         }
18675         
18676         
18677         
18678     },
18679
18680     // private
18681     initEvents : function(){
18682         Roo.form.ComboBox.superclass.initEvents.call(this);
18683
18684         this.keyNav = new Roo.KeyNav(this.el, {
18685             "up" : function(e){
18686                 this.inKeyMode = true;
18687                 this.selectPrev();
18688             },
18689
18690             "down" : function(e){
18691                 if(!this.isExpanded()){
18692                     this.onTriggerClick();
18693                 }else{
18694                     this.inKeyMode = true;
18695                     this.selectNext();
18696                 }
18697             },
18698
18699             "enter" : function(e){
18700                 this.onViewClick();
18701                 //return true;
18702             },
18703
18704             "esc" : function(e){
18705                 this.collapse();
18706             },
18707
18708             "tab" : function(e){
18709                 this.onViewClick(false);
18710                 this.fireEvent("specialkey", this, e);
18711                 return true;
18712             },
18713
18714             scope : this,
18715
18716             doRelay : function(foo, bar, hname){
18717                 if(hname == 'down' || this.scope.isExpanded()){
18718                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18719                 }
18720                 return true;
18721             },
18722
18723             forceKeyDown: true
18724         });
18725         this.queryDelay = Math.max(this.queryDelay || 10,
18726                 this.mode == 'local' ? 10 : 250);
18727         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18728         if(this.typeAhead){
18729             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18730         }
18731         if(this.editable !== false){
18732             this.el.on("keyup", this.onKeyUp, this);
18733         }
18734         if(this.forceSelection){
18735             this.on('blur', this.doForce, this);
18736         }
18737     },
18738
18739     onDestroy : function(){
18740         if(this.view){
18741             this.view.setStore(null);
18742             this.view.el.removeAllListeners();
18743             this.view.el.remove();
18744             this.view.purgeListeners();
18745         }
18746         if(this.list){
18747             this.list.destroy();
18748         }
18749         if(this.store){
18750             this.store.un('beforeload', this.onBeforeLoad, this);
18751             this.store.un('load', this.onLoad, this);
18752             this.store.un('loadexception', this.onLoadException, this);
18753         }
18754         Roo.form.ComboBox.superclass.onDestroy.call(this);
18755     },
18756
18757     // private
18758     fireKey : function(e){
18759         if(e.isNavKeyPress() && !this.list.isVisible()){
18760             this.fireEvent("specialkey", this, e);
18761         }
18762     },
18763
18764     // private
18765     onResize: function(w, h){
18766         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18767         
18768         if(typeof w != 'number'){
18769             // we do not handle it!?!?
18770             return;
18771         }
18772         var tw = this.trigger.getWidth();
18773         tw += this.addicon ? this.addicon.getWidth() : 0;
18774         tw += this.editicon ? this.editicon.getWidth() : 0;
18775         var x = w - tw;
18776         this.el.setWidth( this.adjustWidth('input', x));
18777             
18778         this.trigger.setStyle('left', x+'px');
18779         
18780         if(this.list && this.listWidth === undefined){
18781             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18782             this.list.setWidth(lw);
18783             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18784         }
18785         
18786     
18787         
18788     },
18789
18790     /**
18791      * Allow or prevent the user from directly editing the field text.  If false is passed,
18792      * the user will only be able to select from the items defined in the dropdown list.  This method
18793      * is the runtime equivalent of setting the 'editable' config option at config time.
18794      * @param {Boolean} value True to allow the user to directly edit the field text
18795      */
18796     setEditable : function(value){
18797         if(value == this.editable){
18798             return;
18799         }
18800         this.editable = value;
18801         if(!value){
18802             this.el.dom.setAttribute('readOnly', true);
18803             this.el.on('mousedown', this.onTriggerClick,  this);
18804             this.el.addClass('x-combo-noedit');
18805         }else{
18806             this.el.dom.setAttribute('readOnly', false);
18807             this.el.un('mousedown', this.onTriggerClick,  this);
18808             this.el.removeClass('x-combo-noedit');
18809         }
18810     },
18811
18812     // private
18813     onBeforeLoad : function(){
18814         if(!this.hasFocus){
18815             return;
18816         }
18817         this.innerList.update(this.loadingText ?
18818                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18819         this.restrictHeight();
18820         this.selectedIndex = -1;
18821     },
18822
18823     // private
18824     onLoad : function(){
18825         if(!this.hasFocus){
18826             return;
18827         }
18828         if(this.store.getCount() > 0){
18829             this.expand();
18830             this.restrictHeight();
18831             if(this.lastQuery == this.allQuery){
18832                 if(this.editable){
18833                     this.el.dom.select();
18834                 }
18835                 if(!this.selectByValue(this.value, true)){
18836                     this.select(0, true);
18837                 }
18838             }else{
18839                 this.selectNext();
18840                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18841                     this.taTask.delay(this.typeAheadDelay);
18842                 }
18843             }
18844         }else{
18845             this.onEmptyResults();
18846         }
18847         //this.el.focus();
18848     },
18849     // private
18850     onLoadException : function()
18851     {
18852         this.collapse();
18853         Roo.log(this.store.reader.jsonData);
18854         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18855             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18856         }
18857         
18858         
18859     },
18860     // private
18861     onTypeAhead : function(){
18862         if(this.store.getCount() > 0){
18863             var r = this.store.getAt(0);
18864             var newValue = r.data[this.displayField];
18865             var len = newValue.length;
18866             var selStart = this.getRawValue().length;
18867             if(selStart != len){
18868                 this.setRawValue(newValue);
18869                 this.selectText(selStart, newValue.length);
18870             }
18871         }
18872     },
18873
18874     // private
18875     onSelect : function(record, index){
18876         if(this.fireEvent('beforeselect', this, record, index) !== false){
18877             this.setFromData(index > -1 ? record.data : false);
18878             this.collapse();
18879             this.fireEvent('select', this, record, index);
18880         }
18881     },
18882
18883     /**
18884      * Returns the currently selected field value or empty string if no value is set.
18885      * @return {String} value The selected value
18886      */
18887     getValue : function(){
18888         if(this.valueField){
18889             return typeof this.value != 'undefined' ? this.value : '';
18890         }
18891         return Roo.form.ComboBox.superclass.getValue.call(this);
18892     },
18893
18894     /**
18895      * Clears any text/value currently set in the field
18896      */
18897     clearValue : function(){
18898         if(this.hiddenField){
18899             this.hiddenField.value = '';
18900         }
18901         this.value = '';
18902         this.setRawValue('');
18903         this.lastSelectionText = '';
18904         
18905     },
18906
18907     /**
18908      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18909      * will be displayed in the field.  If the value does not match the data value of an existing item,
18910      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18911      * Otherwise the field will be blank (although the value will still be set).
18912      * @param {String} value The value to match
18913      */
18914     setValue : function(v){
18915         var text = v;
18916         if(this.valueField){
18917             var r = this.findRecord(this.valueField, v);
18918             if(r){
18919                 text = r.data[this.displayField];
18920             }else if(this.valueNotFoundText !== undefined){
18921                 text = this.valueNotFoundText;
18922             }
18923         }
18924         this.lastSelectionText = text;
18925         if(this.hiddenField){
18926             this.hiddenField.value = v;
18927         }
18928         Roo.form.ComboBox.superclass.setValue.call(this, text);
18929         this.value = v;
18930     },
18931     /**
18932      * @property {Object} the last set data for the element
18933      */
18934     
18935     lastData : false,
18936     /**
18937      * Sets the value of the field based on a object which is related to the record format for the store.
18938      * @param {Object} value the value to set as. or false on reset?
18939      */
18940     setFromData : function(o){
18941         var dv = ''; // display value
18942         var vv = ''; // value value..
18943         this.lastData = o;
18944         if (this.displayField) {
18945             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18946         } else {
18947             // this is an error condition!!!
18948             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18949         }
18950         
18951         if(this.valueField){
18952             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18953         }
18954         if(this.hiddenField){
18955             this.hiddenField.value = vv;
18956             
18957             this.lastSelectionText = dv;
18958             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18959             this.value = vv;
18960             return;
18961         }
18962         // no hidden field.. - we store the value in 'value', but still display
18963         // display field!!!!
18964         this.lastSelectionText = dv;
18965         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18966         this.value = vv;
18967         
18968         
18969     },
18970     // private
18971     reset : function(){
18972         // overridden so that last data is reset..
18973         this.setValue(this.resetValue);
18974         this.originalValue = this.getValue();
18975         this.clearInvalid();
18976         this.lastData = false;
18977         if (this.view) {
18978             this.view.clearSelections();
18979         }
18980     },
18981     // private
18982     findRecord : function(prop, value){
18983         var record;
18984         if(this.store.getCount() > 0){
18985             this.store.each(function(r){
18986                 if(r.data[prop] == value){
18987                     record = r;
18988                     return false;
18989                 }
18990                 return true;
18991             });
18992         }
18993         return record;
18994     },
18995     
18996     getName: function()
18997     {
18998         // returns hidden if it's set..
18999         if (!this.rendered) {return ''};
19000         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19001         
19002     },
19003     // private
19004     onViewMove : function(e, t){
19005         this.inKeyMode = false;
19006     },
19007
19008     // private
19009     onViewOver : function(e, t){
19010         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19011             return;
19012         }
19013         var item = this.view.findItemFromChild(t);
19014         if(item){
19015             var index = this.view.indexOf(item);
19016             this.select(index, false);
19017         }
19018     },
19019
19020     // private
19021     onViewClick : function(doFocus)
19022     {
19023         var index = this.view.getSelectedIndexes()[0];
19024         var r = this.store.getAt(index);
19025         if(r){
19026             this.onSelect(r, index);
19027         }
19028         if(doFocus !== false && !this.blockFocus){
19029             this.el.focus();
19030         }
19031     },
19032
19033     // private
19034     restrictHeight : function(){
19035         this.innerList.dom.style.height = '';
19036         var inner = this.innerList.dom;
19037         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19038         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19039         this.list.beginUpdate();
19040         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19041         this.list.alignTo(this.el, this.listAlign);
19042         this.list.endUpdate();
19043     },
19044
19045     // private
19046     onEmptyResults : function(){
19047         this.collapse();
19048     },
19049
19050     /**
19051      * Returns true if the dropdown list is expanded, else false.
19052      */
19053     isExpanded : function(){
19054         return this.list.isVisible();
19055     },
19056
19057     /**
19058      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19059      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19060      * @param {String} value The data value of the item to select
19061      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19062      * selected item if it is not currently in view (defaults to true)
19063      * @return {Boolean} True if the value matched an item in the list, else false
19064      */
19065     selectByValue : function(v, scrollIntoView){
19066         if(v !== undefined && v !== null){
19067             var r = this.findRecord(this.valueField || this.displayField, v);
19068             if(r){
19069                 this.select(this.store.indexOf(r), scrollIntoView);
19070                 return true;
19071             }
19072         }
19073         return false;
19074     },
19075
19076     /**
19077      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19078      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19079      * @param {Number} index The zero-based index of the list item to select
19080      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19081      * selected item if it is not currently in view (defaults to true)
19082      */
19083     select : function(index, scrollIntoView){
19084         this.selectedIndex = index;
19085         this.view.select(index);
19086         if(scrollIntoView !== false){
19087             var el = this.view.getNode(index);
19088             if(el){
19089                 this.innerList.scrollChildIntoView(el, false);
19090             }
19091         }
19092     },
19093
19094     // private
19095     selectNext : function(){
19096         var ct = this.store.getCount();
19097         if(ct > 0){
19098             if(this.selectedIndex == -1){
19099                 this.select(0);
19100             }else if(this.selectedIndex < ct-1){
19101                 this.select(this.selectedIndex+1);
19102             }
19103         }
19104     },
19105
19106     // private
19107     selectPrev : function(){
19108         var ct = this.store.getCount();
19109         if(ct > 0){
19110             if(this.selectedIndex == -1){
19111                 this.select(0);
19112             }else if(this.selectedIndex != 0){
19113                 this.select(this.selectedIndex-1);
19114             }
19115         }
19116     },
19117
19118     // private
19119     onKeyUp : function(e){
19120         if(this.editable !== false && !e.isSpecialKey()){
19121             this.lastKey = e.getKey();
19122             this.dqTask.delay(this.queryDelay);
19123         }
19124     },
19125
19126     // private
19127     validateBlur : function(){
19128         return !this.list || !this.list.isVisible();   
19129     },
19130
19131     // private
19132     initQuery : function(){
19133         this.doQuery(this.getRawValue());
19134     },
19135
19136     // private
19137     doForce : function(){
19138         if(this.el.dom.value.length > 0){
19139             this.el.dom.value =
19140                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19141              
19142         }
19143     },
19144
19145     /**
19146      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19147      * query allowing the query action to be canceled if needed.
19148      * @param {String} query The SQL query to execute
19149      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19150      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19151      * saved in the current store (defaults to false)
19152      */
19153     doQuery : function(q, forceAll){
19154         if(q === undefined || q === null){
19155             q = '';
19156         }
19157         var qe = {
19158             query: q,
19159             forceAll: forceAll,
19160             combo: this,
19161             cancel:false
19162         };
19163         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19164             return false;
19165         }
19166         q = qe.query;
19167         forceAll = qe.forceAll;
19168         if(forceAll === true || (q.length >= this.minChars)){
19169             if(this.lastQuery != q || this.alwaysQuery){
19170                 this.lastQuery = q;
19171                 if(this.mode == 'local'){
19172                     this.selectedIndex = -1;
19173                     if(forceAll){
19174                         this.store.clearFilter();
19175                     }else{
19176                         this.store.filter(this.displayField, q);
19177                     }
19178                     this.onLoad();
19179                 }else{
19180                     this.store.baseParams[this.queryParam] = q;
19181                     this.store.load({
19182                         params: this.getParams(q)
19183                     });
19184                     this.expand();
19185                 }
19186             }else{
19187                 this.selectedIndex = -1;
19188                 this.onLoad();   
19189             }
19190         }
19191     },
19192
19193     // private
19194     getParams : function(q){
19195         var p = {};
19196         //p[this.queryParam] = q;
19197         if(this.pageSize){
19198             p.start = 0;
19199             p.limit = this.pageSize;
19200         }
19201         return p;
19202     },
19203
19204     /**
19205      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19206      */
19207     collapse : function(){
19208         if(!this.isExpanded()){
19209             return;
19210         }
19211         this.list.hide();
19212         Roo.get(document).un('mousedown', this.collapseIf, this);
19213         Roo.get(document).un('mousewheel', this.collapseIf, this);
19214         if (!this.editable) {
19215             Roo.get(document).un('keydown', this.listKeyPress, this);
19216         }
19217         this.fireEvent('collapse', this);
19218     },
19219
19220     // private
19221     collapseIf : function(e){
19222         if(!e.within(this.wrap) && !e.within(this.list)){
19223             this.collapse();
19224         }
19225     },
19226
19227     /**
19228      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19229      */
19230     expand : function(){
19231         if(this.isExpanded() || !this.hasFocus){
19232             return;
19233         }
19234         this.list.alignTo(this.el, this.listAlign);
19235         this.list.show();
19236         Roo.get(document).on('mousedown', this.collapseIf, this);
19237         Roo.get(document).on('mousewheel', this.collapseIf, this);
19238         if (!this.editable) {
19239             Roo.get(document).on('keydown', this.listKeyPress, this);
19240         }
19241         
19242         this.fireEvent('expand', this);
19243     },
19244
19245     // private
19246     // Implements the default empty TriggerField.onTriggerClick function
19247     onTriggerClick : function(){
19248         if(this.disabled){
19249             return;
19250         }
19251         if(this.isExpanded()){
19252             this.collapse();
19253             if (!this.blockFocus) {
19254                 this.el.focus();
19255             }
19256             
19257         }else {
19258             this.hasFocus = true;
19259             if(this.triggerAction == 'all') {
19260                 this.doQuery(this.allQuery, true);
19261             } else {
19262                 this.doQuery(this.getRawValue());
19263             }
19264             if (!this.blockFocus) {
19265                 this.el.focus();
19266             }
19267         }
19268     },
19269     listKeyPress : function(e)
19270     {
19271         //Roo.log('listkeypress');
19272         // scroll to first matching element based on key pres..
19273         if (e.isSpecialKey()) {
19274             return false;
19275         }
19276         var k = String.fromCharCode(e.getKey()).toUpperCase();
19277         //Roo.log(k);
19278         var match  = false;
19279         var csel = this.view.getSelectedNodes();
19280         var cselitem = false;
19281         if (csel.length) {
19282             var ix = this.view.indexOf(csel[0]);
19283             cselitem  = this.store.getAt(ix);
19284             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19285                 cselitem = false;
19286             }
19287             
19288         }
19289         
19290         this.store.each(function(v) { 
19291             if (cselitem) {
19292                 // start at existing selection.
19293                 if (cselitem.id == v.id) {
19294                     cselitem = false;
19295                 }
19296                 return;
19297             }
19298                 
19299             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19300                 match = this.store.indexOf(v);
19301                 return false;
19302             }
19303         }, this);
19304         
19305         if (match === false) {
19306             return true; // no more action?
19307         }
19308         // scroll to?
19309         this.view.select(match);
19310         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19311         sn.scrollIntoView(sn.dom.parentNode, false);
19312     } 
19313
19314     /** 
19315     * @cfg {Boolean} grow 
19316     * @hide 
19317     */
19318     /** 
19319     * @cfg {Number} growMin 
19320     * @hide 
19321     */
19322     /** 
19323     * @cfg {Number} growMax 
19324     * @hide 
19325     */
19326     /**
19327      * @hide
19328      * @method autoSize
19329      */
19330 });/*
19331  * Copyright(c) 2010-2012, Roo J Solutions Limited
19332  *
19333  * Licence LGPL
19334  *
19335  */
19336
19337 /**
19338  * @class Roo.form.ComboBoxArray
19339  * @extends Roo.form.TextField
19340  * A facebook style adder... for lists of email / people / countries  etc...
19341  * pick multiple items from a combo box, and shows each one.
19342  *
19343  *  Fred [x]  Brian [x]  [Pick another |v]
19344  *
19345  *
19346  *  For this to work: it needs various extra information
19347  *    - normal combo problay has
19348  *      name, hiddenName
19349  *    + displayField, valueField
19350  *
19351  *    For our purpose...
19352  *
19353  *
19354  *   If we change from 'extends' to wrapping...
19355  *   
19356  *  
19357  *
19358  
19359  
19360  * @constructor
19361  * Create a new ComboBoxArray.
19362  * @param {Object} config Configuration options
19363  */
19364  
19365
19366 Roo.form.ComboBoxArray = function(config)
19367 {
19368     this.addEvents({
19369         /**
19370          * @event beforeremove
19371          * Fires before remove the value from the list
19372              * @param {Roo.form.ComboBoxArray} _self This combo box array
19373              * @param {Roo.form.ComboBoxArray.Item} item removed item
19374              */
19375         'beforeremove' : true,
19376         /**
19377          * @event remove
19378          * Fires when 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         'remove' : true
19383         
19384         
19385     });
19386     
19387     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19388     
19389     this.items = new Roo.util.MixedCollection(false);
19390     
19391     // construct the child combo...
19392     
19393     
19394     
19395     
19396    
19397     
19398 }
19399
19400  
19401 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19402
19403     /**
19404      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19405      */
19406     
19407     lastData : false,
19408     
19409     // behavies liek a hiddne field
19410     inputType:      'hidden',
19411     /**
19412      * @cfg {Number} width The width of the box that displays the selected element
19413      */ 
19414     width:          300,
19415
19416     
19417     
19418     /**
19419      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19420      */
19421     name : false,
19422     /**
19423      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19424      */
19425     hiddenName : false,
19426       /**
19427      * @cfg {String} seperator    The value seperator normally ',' 
19428      */
19429     seperator : ',',
19430     
19431     // private the array of items that are displayed..
19432     items  : false,
19433     // private - the hidden field el.
19434     hiddenEl : false,
19435     // private - the filed el..
19436     el : false,
19437     
19438     //validateValue : function() { return true; }, // all values are ok!
19439     //onAddClick: function() { },
19440     
19441     onRender : function(ct, position) 
19442     {
19443         
19444         // create the standard hidden element
19445         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19446         
19447         
19448         // give fake names to child combo;
19449         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19450         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19451         
19452         this.combo = Roo.factory(this.combo, Roo.form);
19453         this.combo.onRender(ct, position);
19454         if (typeof(this.combo.width) != 'undefined') {
19455             this.combo.onResize(this.combo.width,0);
19456         }
19457         
19458         this.combo.initEvents();
19459         
19460         // assigned so form know we need to do this..
19461         this.store          = this.combo.store;
19462         this.valueField     = this.combo.valueField;
19463         this.displayField   = this.combo.displayField ;
19464         
19465         
19466         this.combo.wrap.addClass('x-cbarray-grp');
19467         
19468         var cbwrap = this.combo.wrap.createChild(
19469             {tag: 'div', cls: 'x-cbarray-cb'},
19470             this.combo.el.dom
19471         );
19472         
19473              
19474         this.hiddenEl = this.combo.wrap.createChild({
19475             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19476         });
19477         this.el = this.combo.wrap.createChild({
19478             tag: 'input',  type:'hidden' , name: this.name, value : ''
19479         });
19480          //   this.el.dom.removeAttribute("name");
19481         
19482         
19483         this.outerWrap = this.combo.wrap;
19484         this.wrap = cbwrap;
19485         
19486         this.outerWrap.setWidth(this.width);
19487         this.outerWrap.dom.removeChild(this.el.dom);
19488         
19489         this.wrap.dom.appendChild(this.el.dom);
19490         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19491         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19492         
19493         this.combo.trigger.setStyle('position','relative');
19494         this.combo.trigger.setStyle('left', '0px');
19495         this.combo.trigger.setStyle('top', '2px');
19496         
19497         this.combo.el.setStyle('vertical-align', 'text-bottom');
19498         
19499         //this.trigger.setStyle('vertical-align', 'top');
19500         
19501         // this should use the code from combo really... on('add' ....)
19502         if (this.adder) {
19503             
19504         
19505             this.adder = this.outerWrap.createChild(
19506                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19507             var _t = this;
19508             this.adder.on('click', function(e) {
19509                 _t.fireEvent('adderclick', this, e);
19510             }, _t);
19511         }
19512         //var _t = this;
19513         //this.adder.on('click', this.onAddClick, _t);
19514         
19515         
19516         this.combo.on('select', function(cb, rec, ix) {
19517             this.addItem(rec.data);
19518             
19519             cb.setValue('');
19520             cb.el.dom.value = '';
19521             //cb.lastData = rec.data;
19522             // add to list
19523             
19524         }, this);
19525         
19526         
19527     },
19528     
19529     
19530     getName: function()
19531     {
19532         // returns hidden if it's set..
19533         if (!this.rendered) {return ''};
19534         return  this.hiddenName ? this.hiddenName : this.name;
19535         
19536     },
19537     
19538     
19539     onResize: function(w, h){
19540         
19541         return;
19542         // not sure if this is needed..
19543         //this.combo.onResize(w,h);
19544         
19545         if(typeof w != 'number'){
19546             // we do not handle it!?!?
19547             return;
19548         }
19549         var tw = this.combo.trigger.getWidth();
19550         tw += this.addicon ? this.addicon.getWidth() : 0;
19551         tw += this.editicon ? this.editicon.getWidth() : 0;
19552         var x = w - tw;
19553         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19554             
19555         this.combo.trigger.setStyle('left', '0px');
19556         
19557         if(this.list && this.listWidth === undefined){
19558             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19559             this.list.setWidth(lw);
19560             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19561         }
19562         
19563     
19564         
19565     },
19566     
19567     addItem: function(rec)
19568     {
19569         var valueField = this.combo.valueField;
19570         var displayField = this.combo.displayField;
19571         
19572         if (this.items.indexOfKey(rec[valueField]) > -1) {
19573             //console.log("GOT " + rec.data.id);
19574             return;
19575         }
19576         
19577         var x = new Roo.form.ComboBoxArray.Item({
19578             //id : rec[this.idField],
19579             data : rec,
19580             displayField : displayField ,
19581             tipField : displayField ,
19582             cb : this
19583         });
19584         // use the 
19585         this.items.add(rec[valueField],x);
19586         // add it before the element..
19587         this.updateHiddenEl();
19588         x.render(this.outerWrap, this.wrap.dom);
19589         // add the image handler..
19590     },
19591     
19592     updateHiddenEl : function()
19593     {
19594         this.validate();
19595         if (!this.hiddenEl) {
19596             return;
19597         }
19598         var ar = [];
19599         var idField = this.combo.valueField;
19600         
19601         this.items.each(function(f) {
19602             ar.push(f.data[idField]);
19603         });
19604         this.hiddenEl.dom.value = ar.join(this.seperator);
19605         this.validate();
19606     },
19607     
19608     reset : function()
19609     {
19610         this.items.clear();
19611         
19612         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19613            el.remove();
19614         });
19615         
19616         this.el.dom.value = '';
19617         if (this.hiddenEl) {
19618             this.hiddenEl.dom.value = '';
19619         }
19620         
19621     },
19622     getValue: function()
19623     {
19624         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19625     },
19626     setValue: function(v) // not a valid action - must use addItems..
19627     {
19628         
19629         this.reset();
19630          
19631         if (this.store.isLocal && (typeof(v) == 'string')) {
19632             // then we can use the store to find the values..
19633             // comma seperated at present.. this needs to allow JSON based encoding..
19634             this.hiddenEl.value  = v;
19635             var v_ar = [];
19636             Roo.each(v.split(this.seperator), function(k) {
19637                 Roo.log("CHECK " + this.valueField + ',' + k);
19638                 var li = this.store.query(this.valueField, k);
19639                 if (!li.length) {
19640                     return;
19641                 }
19642                 var add = {};
19643                 add[this.valueField] = k;
19644                 add[this.displayField] = li.item(0).data[this.displayField];
19645                 
19646                 this.addItem(add);
19647             }, this) 
19648              
19649         }
19650         if (typeof(v) == 'object' ) {
19651             // then let's assume it's an array of objects..
19652             Roo.each(v, function(l) {
19653                 var add = l;
19654                 if (typeof(l) == 'string') {
19655                     add = {};
19656                     add[this.valueField] = l;
19657                     add[this.displayField] = l
19658                 }
19659                 this.addItem(add);
19660             }, this);
19661              
19662         }
19663         
19664         
19665     },
19666     setFromData: function(v)
19667     {
19668         // this recieves an object, if setValues is called.
19669         this.reset();
19670         this.el.dom.value = v[this.displayField];
19671         this.hiddenEl.dom.value = v[this.valueField];
19672         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19673             return;
19674         }
19675         var kv = v[this.valueField];
19676         var dv = v[this.displayField];
19677         kv = typeof(kv) != 'string' ? '' : kv;
19678         dv = typeof(dv) != 'string' ? '' : dv;
19679         
19680         
19681         var keys = kv.split(this.seperator);
19682         var display = dv.split(this.seperator);
19683         for (var i = 0 ; i < keys.length; i++) {
19684             add = {};
19685             add[this.valueField] = keys[i];
19686             add[this.displayField] = display[i];
19687             this.addItem(add);
19688         }
19689       
19690         
19691     },
19692     
19693     /**
19694      * Validates the combox array value
19695      * @return {Boolean} True if the value is valid, else false
19696      */
19697     validate : function(){
19698         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19699             this.clearInvalid();
19700             return true;
19701         }
19702         return false;
19703     },
19704     
19705     validateValue : function(value){
19706         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19707         
19708     },
19709     
19710     /*@
19711      * overide
19712      * 
19713      */
19714     isDirty : function() {
19715         if(this.disabled) {
19716             return false;
19717         }
19718         
19719         try {
19720             var d = Roo.decode(String(this.originalValue));
19721         } catch (e) {
19722             return String(this.getValue()) !== String(this.originalValue);
19723         }
19724         
19725         var originalValue = [];
19726         
19727         for (var i = 0; i < d.length; i++){
19728             originalValue.push(d[i][this.valueField]);
19729         }
19730         
19731         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19732         
19733     }
19734     
19735 });
19736
19737
19738
19739 /**
19740  * @class Roo.form.ComboBoxArray.Item
19741  * @extends Roo.BoxComponent
19742  * A selected item in the list
19743  *  Fred [x]  Brian [x]  [Pick another |v]
19744  * 
19745  * @constructor
19746  * Create a new item.
19747  * @param {Object} config Configuration options
19748  */
19749  
19750 Roo.form.ComboBoxArray.Item = function(config) {
19751     config.id = Roo.id();
19752     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19753 }
19754
19755 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19756     data : {},
19757     cb: false,
19758     displayField : false,
19759     tipField : false,
19760     
19761     
19762     defaultAutoCreate : {
19763         tag: 'div',
19764         cls: 'x-cbarray-item',
19765         cn : [ 
19766             { tag: 'div' },
19767             {
19768                 tag: 'img',
19769                 width:16,
19770                 height : 16,
19771                 src : Roo.BLANK_IMAGE_URL ,
19772                 align: 'center'
19773             }
19774         ]
19775         
19776     },
19777     
19778  
19779     onRender : function(ct, position)
19780     {
19781         Roo.form.Field.superclass.onRender.call(this, ct, position);
19782         
19783         if(!this.el){
19784             var cfg = this.getAutoCreate();
19785             this.el = ct.createChild(cfg, position);
19786         }
19787         
19788         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19789         
19790         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19791             this.cb.renderer(this.data) :
19792             String.format('{0}',this.data[this.displayField]);
19793         
19794             
19795         this.el.child('div').dom.setAttribute('qtip',
19796                         String.format('{0}',this.data[this.tipField])
19797         );
19798         
19799         this.el.child('img').on('click', this.remove, this);
19800         
19801     },
19802    
19803     remove : function()
19804     {
19805         if(this.cb.disabled){
19806             return;
19807         }
19808         
19809         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19810             this.cb.items.remove(this);
19811             this.el.child('img').un('click', this.remove, this);
19812             this.el.remove();
19813             this.cb.updateHiddenEl();
19814
19815             this.cb.fireEvent('remove', this.cb, this);
19816         }
19817         
19818     }
19819 });/*
19820  * RooJS Library 1.1.1
19821  * Copyright(c) 2008-2011  Alan Knowles
19822  *
19823  * License - LGPL
19824  */
19825  
19826
19827 /**
19828  * @class Roo.form.ComboNested
19829  * @extends Roo.form.ComboBox
19830  * A combobox for that allows selection of nested items in a list,
19831  * eg.
19832  *
19833  *  Book
19834  *    -> red
19835  *    -> green
19836  *  Table
19837  *    -> square
19838  *      ->red
19839  *      ->green
19840  *    -> rectangle
19841  *      ->green
19842  *      
19843  * 
19844  * @constructor
19845  * Create a new ComboNested
19846  * @param {Object} config Configuration options
19847  */
19848 Roo.form.ComboNested = function(config){
19849     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19850     // should verify some data...
19851     // like
19852     // hiddenName = required..
19853     // displayField = required
19854     // valudField == required
19855     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19856     var _t = this;
19857     Roo.each(req, function(e) {
19858         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19859             throw "Roo.form.ComboNested : missing value for: " + e;
19860         }
19861     });
19862      
19863     
19864 };
19865
19866 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19867    
19868     /*
19869      * @config {Number} max Number of columns to show
19870      */
19871     
19872     maxColumns : 3,
19873    
19874     list : null, // the outermost div..
19875     innerLists : null, // the
19876     views : null,
19877     stores : null,
19878     // private
19879     loadingChildren : false,
19880     
19881     onRender : function(ct, position)
19882     {
19883         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19884         
19885         if(this.hiddenName){
19886             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19887                     'before', true);
19888             this.hiddenField.value =
19889                 this.hiddenValue !== undefined ? this.hiddenValue :
19890                 this.value !== undefined ? this.value : '';
19891
19892             // prevent input submission
19893             this.el.dom.removeAttribute('name');
19894              
19895              
19896         }
19897         
19898         if(Roo.isGecko){
19899             this.el.dom.setAttribute('autocomplete', 'off');
19900         }
19901
19902         var cls = 'x-combo-list';
19903
19904         this.list = new Roo.Layer({
19905             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19906         });
19907
19908         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19909         this.list.setWidth(lw);
19910         this.list.swallowEvent('mousewheel');
19911         this.assetHeight = 0;
19912
19913         if(this.title){
19914             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19915             this.assetHeight += this.header.getHeight();
19916         }
19917         this.innerLists = [];
19918         this.views = [];
19919         this.stores = [];
19920         for (var i =0 ; i < this.maxColumns; i++) {
19921             this.onRenderList( cls, i);
19922         }
19923         
19924         // always needs footer, as we are going to have an 'OK' button.
19925         this.footer = this.list.createChild({cls:cls+'-ft'});
19926         this.pageTb = new Roo.Toolbar(this.footer);  
19927         var _this = this;
19928         this.pageTb.add(  {
19929             
19930             text: 'Done',
19931             handler: function()
19932             {
19933                 _this.collapse();
19934             }
19935         });
19936         
19937         if ( this.allowBlank && !this.disableClear) {
19938             
19939             this.pageTb.add(new Roo.Toolbar.Fill(), {
19940                 cls: 'x-btn-icon x-btn-clear',
19941                 text: '&#160;',
19942                 handler: function()
19943                 {
19944                     _this.collapse();
19945                     _this.clearValue();
19946                     _this.onSelect(false, -1);
19947                 }
19948             });
19949         }
19950         if (this.footer) {
19951             this.assetHeight += this.footer.getHeight();
19952         }
19953         
19954     },
19955     onRenderList : function (  cls, i)
19956     {
19957         
19958         var lw = Math.floor(
19959                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19960         );
19961         
19962         this.list.setWidth(lw); // default to '1'
19963
19964         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19965         //il.on('mouseover', this.onViewOver, this, { list:  i });
19966         //il.on('mousemove', this.onViewMove, this, { list:  i });
19967         il.setWidth(lw);
19968         il.setStyle({ 'overflow-x' : 'hidden'});
19969
19970         if(!this.tpl){
19971             this.tpl = new Roo.Template({
19972                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19973                 isEmpty: function (value, allValues) {
19974                     //Roo.log(value);
19975                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19976                     return dl ? 'has-children' : 'no-children'
19977                 }
19978             });
19979         }
19980         
19981         var store  = this.store;
19982         if (i > 0) {
19983             store  = new Roo.data.SimpleStore({
19984                 //fields : this.store.reader.meta.fields,
19985                 reader : this.store.reader,
19986                 data : [ ]
19987             });
19988         }
19989         this.stores[i]  = store;
19990                   
19991         var view = this.views[i] = new Roo.View(
19992             il,
19993             this.tpl,
19994             {
19995                 singleSelect:true,
19996                 store: store,
19997                 selectedClass: this.selectedClass
19998             }
19999         );
20000         view.getEl().setWidth(lw);
20001         view.getEl().setStyle({
20002             position: i < 1 ? 'relative' : 'absolute',
20003             top: 0,
20004             left: (i * lw ) + 'px',
20005             display : i > 0 ? 'none' : 'block'
20006         });
20007         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20008         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20009         //view.on('click', this.onViewClick, this, { list : i });
20010
20011         store.on('beforeload', this.onBeforeLoad, this);
20012         store.on('load',  this.onLoad, this, { list  : i});
20013         store.on('loadexception', this.onLoadException, this);
20014
20015         // hide the other vies..
20016         
20017         
20018         
20019     },
20020       
20021     restrictHeight : function()
20022     {
20023         var mh = 0;
20024         Roo.each(this.innerLists, function(il,i) {
20025             var el = this.views[i].getEl();
20026             el.dom.style.height = '';
20027             var inner = el.dom;
20028             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20029             // only adjust heights on other ones..
20030             mh = Math.max(h, mh);
20031             if (i < 1) {
20032                 
20033                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20034                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20035                
20036             }
20037             
20038             
20039         }, this);
20040         
20041         this.list.beginUpdate();
20042         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20043         this.list.alignTo(this.el, this.listAlign);
20044         this.list.endUpdate();
20045         
20046     },
20047      
20048     
20049     // -- store handlers..
20050     // private
20051     onBeforeLoad : function()
20052     {
20053         if(!this.hasFocus){
20054             return;
20055         }
20056         this.innerLists[0].update(this.loadingText ?
20057                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20058         this.restrictHeight();
20059         this.selectedIndex = -1;
20060     },
20061     // private
20062     onLoad : function(a,b,c,d)
20063     {
20064         if (!this.loadingChildren) {
20065             // then we are loading the top level. - hide the children
20066             for (var i = 1;i < this.views.length; i++) {
20067                 this.views[i].getEl().setStyle({ display : 'none' });
20068             }
20069             var lw = Math.floor(
20070                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20071             );
20072         
20073              this.list.setWidth(lw); // default to '1'
20074
20075             
20076         }
20077         if(!this.hasFocus){
20078             return;
20079         }
20080         
20081         if(this.store.getCount() > 0) {
20082             this.expand();
20083             this.restrictHeight();   
20084         } else {
20085             this.onEmptyResults();
20086         }
20087         
20088         if (!this.loadingChildren) {
20089             this.selectActive();
20090         }
20091         /*
20092         this.stores[1].loadData([]);
20093         this.stores[2].loadData([]);
20094         this.views
20095         */    
20096     
20097         //this.el.focus();
20098     },
20099     
20100     
20101     // private
20102     onLoadException : function()
20103     {
20104         this.collapse();
20105         Roo.log(this.store.reader.jsonData);
20106         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20107             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20108         }
20109         
20110         
20111     },
20112     // no cleaning of leading spaces on blur here.
20113     cleanLeadingSpace : function(e) { },
20114     
20115
20116     onSelectChange : function (view, sels, opts )
20117     {
20118         var ix = view.getSelectedIndexes();
20119          
20120         if (opts.list > this.maxColumns - 2) {
20121             if (view.store.getCount()<  1) {
20122                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20123
20124             } else  {
20125                 if (ix.length) {
20126                     // used to clear ?? but if we are loading unselected 
20127                     this.setFromData(view.store.getAt(ix[0]).data);
20128                 }
20129                 
20130             }
20131             
20132             return;
20133         }
20134         
20135         if (!ix.length) {
20136             // this get's fired when trigger opens..
20137            // this.setFromData({});
20138             var str = this.stores[opts.list+1];
20139             str.data.clear(); // removeall wihtout the fire events..
20140             return;
20141         }
20142         
20143         var rec = view.store.getAt(ix[0]);
20144          
20145         this.setFromData(rec.data);
20146         this.fireEvent('select', this, rec, ix[0]);
20147         
20148         var lw = Math.floor(
20149              (
20150                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20151              ) / this.maxColumns
20152         );
20153         this.loadingChildren = true;
20154         this.stores[opts.list+1].loadDataFromChildren( rec );
20155         this.loadingChildren = false;
20156         var dl = this.stores[opts.list+1]. getTotalCount();
20157         
20158         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20159         
20160         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20161         for (var i = opts.list+2; i < this.views.length;i++) {
20162             this.views[i].getEl().setStyle({ display : 'none' });
20163         }
20164         
20165         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20166         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20167         
20168         if (this.isLoading) {
20169            // this.selectActive(opts.list);
20170         }
20171          
20172     },
20173     
20174     
20175     
20176     
20177     onDoubleClick : function()
20178     {
20179         this.collapse(); //??
20180     },
20181     
20182      
20183     
20184     
20185     
20186     // private
20187     recordToStack : function(store, prop, value, stack)
20188     {
20189         var cstore = new Roo.data.SimpleStore({
20190             //fields : this.store.reader.meta.fields, // we need array reader.. for
20191             reader : this.store.reader,
20192             data : [ ]
20193         });
20194         var _this = this;
20195         var record  = false;
20196         var srec = false;
20197         if(store.getCount() < 1){
20198             return false;
20199         }
20200         store.each(function(r){
20201             if(r.data[prop] == value){
20202                 record = r;
20203             srec = r;
20204                 return false;
20205             }
20206             if (r.data.cn && r.data.cn.length) {
20207                 cstore.loadDataFromChildren( r);
20208                 var cret = _this.recordToStack(cstore, prop, value, stack);
20209                 if (cret !== false) {
20210                     record = cret;
20211                     srec = r;
20212                     return false;
20213                 }
20214             }
20215              
20216             return true;
20217         });
20218         if (record == false) {
20219             return false
20220         }
20221         stack.unshift(srec);
20222         return record;
20223     },
20224     
20225     /*
20226      * find the stack of stores that match our value.
20227      *
20228      * 
20229      */
20230     
20231     selectActive : function ()
20232     {
20233         // if store is not loaded, then we will need to wait for that to happen first.
20234         var stack = [];
20235         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20236         for (var i = 0; i < stack.length; i++ ) {
20237             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20238         }
20239         
20240     }
20241         
20242          
20243     
20244     
20245     
20246     
20247 });/*
20248  * Based on:
20249  * Ext JS Library 1.1.1
20250  * Copyright(c) 2006-2007, Ext JS, LLC.
20251  *
20252  * Originally Released Under LGPL - original licence link has changed is not relivant.
20253  *
20254  * Fork - LGPL
20255  * <script type="text/javascript">
20256  */
20257 /**
20258  * @class Roo.form.Checkbox
20259  * @extends Roo.form.Field
20260  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20261  * @constructor
20262  * Creates a new Checkbox
20263  * @param {Object} config Configuration options
20264  */
20265 Roo.form.Checkbox = function(config){
20266     Roo.form.Checkbox.superclass.constructor.call(this, config);
20267     this.addEvents({
20268         /**
20269          * @event check
20270          * Fires when the checkbox is checked or unchecked.
20271              * @param {Roo.form.Checkbox} this This checkbox
20272              * @param {Boolean} checked The new checked value
20273              */
20274         check : true
20275     });
20276 };
20277
20278 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20279     /**
20280      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20281      */
20282     focusClass : undefined,
20283     /**
20284      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20285      */
20286     fieldClass: "x-form-field",
20287     /**
20288      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20289      */
20290     checked: false,
20291     /**
20292      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20293      * {tag: "input", type: "checkbox", autocomplete: "off"})
20294      */
20295     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20296     /**
20297      * @cfg {String} boxLabel The text that appears beside the checkbox
20298      */
20299     boxLabel : "",
20300     /**
20301      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20302      */  
20303     inputValue : '1',
20304     /**
20305      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20306      */
20307      valueOff: '0', // value when not checked..
20308
20309     actionMode : 'viewEl', 
20310     //
20311     // private
20312     itemCls : 'x-menu-check-item x-form-item',
20313     groupClass : 'x-menu-group-item',
20314     inputType : 'hidden',
20315     
20316     
20317     inSetChecked: false, // check that we are not calling self...
20318     
20319     inputElement: false, // real input element?
20320     basedOn: false, // ????
20321     
20322     isFormField: true, // not sure where this is needed!!!!
20323
20324     onResize : function(){
20325         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20326         if(!this.boxLabel){
20327             this.el.alignTo(this.wrap, 'c-c');
20328         }
20329     },
20330
20331     initEvents : function(){
20332         Roo.form.Checkbox.superclass.initEvents.call(this);
20333         this.el.on("click", this.onClick,  this);
20334         this.el.on("change", this.onClick,  this);
20335     },
20336
20337
20338     getResizeEl : function(){
20339         return this.wrap;
20340     },
20341
20342     getPositionEl : function(){
20343         return this.wrap;
20344     },
20345
20346     // private
20347     onRender : function(ct, position){
20348         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20349         /*
20350         if(this.inputValue !== undefined){
20351             this.el.dom.value = this.inputValue;
20352         }
20353         */
20354         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20355         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20356         var viewEl = this.wrap.createChild({ 
20357             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20358         this.viewEl = viewEl;   
20359         this.wrap.on('click', this.onClick,  this); 
20360         
20361         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20362         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20363         
20364         
20365         
20366         if(this.boxLabel){
20367             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20368         //    viewEl.on('click', this.onClick,  this); 
20369         }
20370         //if(this.checked){
20371             this.setChecked(this.checked);
20372         //}else{
20373             //this.checked = this.el.dom;
20374         //}
20375
20376     },
20377
20378     // private
20379     initValue : Roo.emptyFn,
20380
20381     /**
20382      * Returns the checked state of the checkbox.
20383      * @return {Boolean} True if checked, else false
20384      */
20385     getValue : function(){
20386         if(this.el){
20387             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20388         }
20389         return this.valueOff;
20390         
20391     },
20392
20393         // private
20394     onClick : function(){ 
20395         if (this.disabled) {
20396             return;
20397         }
20398         this.setChecked(!this.checked);
20399
20400         //if(this.el.dom.checked != this.checked){
20401         //    this.setValue(this.el.dom.checked);
20402        // }
20403     },
20404
20405     /**
20406      * Sets the checked state of the checkbox.
20407      * On is always based on a string comparison between inputValue and the param.
20408      * @param {Boolean/String} value - the value to set 
20409      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20410      */
20411     setValue : function(v,suppressEvent){
20412         
20413         
20414         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20415         //if(this.el && this.el.dom){
20416         //    this.el.dom.checked = this.checked;
20417         //    this.el.dom.defaultChecked = this.checked;
20418         //}
20419         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20420         //this.fireEvent("check", this, this.checked);
20421     },
20422     // private..
20423     setChecked : function(state,suppressEvent)
20424     {
20425         if (this.inSetChecked) {
20426             this.checked = state;
20427             return;
20428         }
20429         
20430     
20431         if(this.wrap){
20432             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20433         }
20434         this.checked = state;
20435         if(suppressEvent !== true){
20436             this.fireEvent('check', this, state);
20437         }
20438         this.inSetChecked = true;
20439         this.el.dom.value = state ? this.inputValue : this.valueOff;
20440         this.inSetChecked = false;
20441         
20442     },
20443     // handle setting of hidden value by some other method!!?!?
20444     setFromHidden: function()
20445     {
20446         if(!this.el){
20447             return;
20448         }
20449         //console.log("SET FROM HIDDEN");
20450         //alert('setFrom hidden');
20451         this.setValue(this.el.dom.value);
20452     },
20453     
20454     onDestroy : function()
20455     {
20456         if(this.viewEl){
20457             Roo.get(this.viewEl).remove();
20458         }
20459          
20460         Roo.form.Checkbox.superclass.onDestroy.call(this);
20461     },
20462     
20463     setBoxLabel : function(str)
20464     {
20465         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20466     }
20467
20468 });/*
20469  * Based on:
20470  * Ext JS Library 1.1.1
20471  * Copyright(c) 2006-2007, Ext JS, LLC.
20472  *
20473  * Originally Released Under LGPL - original licence link has changed is not relivant.
20474  *
20475  * Fork - LGPL
20476  * <script type="text/javascript">
20477  */
20478  
20479 /**
20480  * @class Roo.form.Radio
20481  * @extends Roo.form.Checkbox
20482  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20483  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20484  * @constructor
20485  * Creates a new Radio
20486  * @param {Object} config Configuration options
20487  */
20488 Roo.form.Radio = function(){
20489     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20490 };
20491 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20492     inputType: 'radio',
20493
20494     /**
20495      * If this radio is part of a group, it will return the selected value
20496      * @return {String}
20497      */
20498     getGroupValue : function(){
20499         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20500     },
20501     
20502     
20503     onRender : function(ct, position){
20504         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20505         
20506         if(this.inputValue !== undefined){
20507             this.el.dom.value = this.inputValue;
20508         }
20509          
20510         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20511         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20512         //var viewEl = this.wrap.createChild({ 
20513         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20514         //this.viewEl = viewEl;   
20515         //this.wrap.on('click', this.onClick,  this); 
20516         
20517         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20518         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20519         
20520         
20521         
20522         if(this.boxLabel){
20523             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20524         //    viewEl.on('click', this.onClick,  this); 
20525         }
20526          if(this.checked){
20527             this.el.dom.checked =   'checked' ;
20528         }
20529          
20530     } 
20531     
20532     
20533 });//<script type="text/javascript">
20534
20535 /*
20536  * Based  Ext JS Library 1.1.1
20537  * Copyright(c) 2006-2007, Ext JS, LLC.
20538  * LGPL
20539  *
20540  */
20541  
20542 /**
20543  * @class Roo.HtmlEditorCore
20544  * @extends Roo.Component
20545  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20546  *
20547  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20548  */
20549
20550 Roo.HtmlEditorCore = function(config){
20551     
20552     
20553     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20554     
20555     
20556     this.addEvents({
20557         /**
20558          * @event initialize
20559          * Fires when the editor is fully initialized (including the iframe)
20560          * @param {Roo.HtmlEditorCore} this
20561          */
20562         initialize: true,
20563         /**
20564          * @event activate
20565          * Fires when the editor is first receives the focus. Any insertion must wait
20566          * until after this event.
20567          * @param {Roo.HtmlEditorCore} this
20568          */
20569         activate: true,
20570          /**
20571          * @event beforesync
20572          * Fires before the textarea is updated with content from the editor iframe. Return false
20573          * to cancel the sync.
20574          * @param {Roo.HtmlEditorCore} this
20575          * @param {String} html
20576          */
20577         beforesync: true,
20578          /**
20579          * @event beforepush
20580          * Fires before the iframe editor is updated with content from the textarea. Return false
20581          * to cancel the push.
20582          * @param {Roo.HtmlEditorCore} this
20583          * @param {String} html
20584          */
20585         beforepush: true,
20586          /**
20587          * @event sync
20588          * Fires when the textarea is updated with content from the editor iframe.
20589          * @param {Roo.HtmlEditorCore} this
20590          * @param {String} html
20591          */
20592         sync: true,
20593          /**
20594          * @event push
20595          * Fires when the iframe editor is updated with content from the textarea.
20596          * @param {Roo.HtmlEditorCore} this
20597          * @param {String} html
20598          */
20599         push: true,
20600         
20601         /**
20602          * @event editorevent
20603          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20604          * @param {Roo.HtmlEditorCore} this
20605          */
20606         editorevent: true
20607         
20608     });
20609     
20610     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20611     
20612     // defaults : white / black...
20613     this.applyBlacklists();
20614     
20615     
20616     
20617 };
20618
20619
20620 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20621
20622
20623      /**
20624      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20625      */
20626     
20627     owner : false,
20628     
20629      /**
20630      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20631      *                        Roo.resizable.
20632      */
20633     resizable : false,
20634      /**
20635      * @cfg {Number} height (in pixels)
20636      */   
20637     height: 300,
20638    /**
20639      * @cfg {Number} width (in pixels)
20640      */   
20641     width: 500,
20642     
20643     /**
20644      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20645      * 
20646      */
20647     stylesheets: false,
20648     
20649     /**
20650      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20651      */
20652     allowComments: false,
20653     // id of frame..
20654     frameId: false,
20655     
20656     // private properties
20657     validationEvent : false,
20658     deferHeight: true,
20659     initialized : false,
20660     activated : false,
20661     sourceEditMode : false,
20662     onFocus : Roo.emptyFn,
20663     iframePad:3,
20664     hideMode:'offsets',
20665     
20666     clearUp: true,
20667     
20668     // blacklist + whitelisted elements..
20669     black: false,
20670     white: false,
20671      
20672     bodyCls : '',
20673
20674     /**
20675      * Protected method that will not generally be called directly. It
20676      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20677      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20678      */
20679     getDocMarkup : function(){
20680         // body styles..
20681         var st = '';
20682         
20683         // inherit styels from page...?? 
20684         if (this.stylesheets === false) {
20685             
20686             Roo.get(document.head).select('style').each(function(node) {
20687                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20688             });
20689             
20690             Roo.get(document.head).select('link').each(function(node) { 
20691                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20692             });
20693             
20694         } else if (!this.stylesheets.length) {
20695                 // simple..
20696                 st = '<style type="text/css">' +
20697                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20698                    '</style>';
20699         } else {
20700             for (var i in this.stylesheets) { 
20701                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20702             }
20703             
20704         }
20705         
20706         st +=  '<style type="text/css">' +
20707             'IMG { cursor: pointer } ' +
20708         '</style>';
20709
20710         var cls = 'roo-htmleditor-body';
20711         
20712         if(this.bodyCls.length){
20713             cls += ' ' + this.bodyCls;
20714         }
20715         
20716         return '<html><head>' + st  +
20717             //<style type="text/css">' +
20718             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20719             //'</style>' +
20720             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20721     },
20722
20723     // private
20724     onRender : function(ct, position)
20725     {
20726         var _t = this;
20727         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20728         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20729         
20730         
20731         this.el.dom.style.border = '0 none';
20732         this.el.dom.setAttribute('tabIndex', -1);
20733         this.el.addClass('x-hidden hide');
20734         
20735         
20736         
20737         if(Roo.isIE){ // fix IE 1px bogus margin
20738             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20739         }
20740        
20741         
20742         this.frameId = Roo.id();
20743         
20744          
20745         
20746         var iframe = this.owner.wrap.createChild({
20747             tag: 'iframe',
20748             cls: 'form-control', // bootstrap..
20749             id: this.frameId,
20750             name: this.frameId,
20751             frameBorder : 'no',
20752             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20753         }, this.el
20754         );
20755         
20756         
20757         this.iframe = iframe.dom;
20758
20759          this.assignDocWin();
20760         
20761         this.doc.designMode = 'on';
20762        
20763         this.doc.open();
20764         this.doc.write(this.getDocMarkup());
20765         this.doc.close();
20766
20767         
20768         var task = { // must defer to wait for browser to be ready
20769             run : function(){
20770                 //console.log("run task?" + this.doc.readyState);
20771                 this.assignDocWin();
20772                 if(this.doc.body || this.doc.readyState == 'complete'){
20773                     try {
20774                         this.doc.designMode="on";
20775                     } catch (e) {
20776                         return;
20777                     }
20778                     Roo.TaskMgr.stop(task);
20779                     this.initEditor.defer(10, this);
20780                 }
20781             },
20782             interval : 10,
20783             duration: 10000,
20784             scope: this
20785         };
20786         Roo.TaskMgr.start(task);
20787
20788     },
20789
20790     // private
20791     onResize : function(w, h)
20792     {
20793          Roo.log('resize: ' +w + ',' + h );
20794         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20795         if(!this.iframe){
20796             return;
20797         }
20798         if(typeof w == 'number'){
20799             
20800             this.iframe.style.width = w + 'px';
20801         }
20802         if(typeof h == 'number'){
20803             
20804             this.iframe.style.height = h + 'px';
20805             if(this.doc){
20806                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20807             }
20808         }
20809         
20810     },
20811
20812     /**
20813      * Toggles the editor between standard and source edit mode.
20814      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20815      */
20816     toggleSourceEdit : function(sourceEditMode){
20817         
20818         this.sourceEditMode = sourceEditMode === true;
20819         
20820         if(this.sourceEditMode){
20821  
20822             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20823             
20824         }else{
20825             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20826             //this.iframe.className = '';
20827             this.deferFocus();
20828         }
20829         //this.setSize(this.owner.wrap.getSize());
20830         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20831     },
20832
20833     
20834   
20835
20836     /**
20837      * Protected method that will not generally be called directly. If you need/want
20838      * custom HTML cleanup, this is the method you should override.
20839      * @param {String} html The HTML to be cleaned
20840      * return {String} The cleaned HTML
20841      */
20842     cleanHtml : function(html){
20843         html = String(html);
20844         if(html.length > 5){
20845             if(Roo.isSafari){ // strip safari nonsense
20846                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20847             }
20848         }
20849         if(html == '&nbsp;'){
20850             html = '';
20851         }
20852         return html;
20853     },
20854
20855     /**
20856      * HTML Editor -> Textarea
20857      * Protected method that will not generally be called directly. Syncs the contents
20858      * of the editor iframe with the textarea.
20859      */
20860     syncValue : function(){
20861         if(this.initialized){
20862             var bd = (this.doc.body || this.doc.documentElement);
20863             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20864             var html = bd.innerHTML;
20865             if(Roo.isSafari){
20866                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20867                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20868                 if(m && m[1]){
20869                     html = '<div style="'+m[0]+'">' + html + '</div>';
20870                 }
20871             }
20872             html = this.cleanHtml(html);
20873             // fix up the special chars.. normaly like back quotes in word...
20874             // however we do not want to do this with chinese..
20875             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20876                 
20877                 var cc = match.charCodeAt();
20878
20879                 // Get the character value, handling surrogate pairs
20880                 if (match.length == 2) {
20881                     // It's a surrogate pair, calculate the Unicode code point
20882                     var high = match.charCodeAt(0) - 0xD800;
20883                     var low  = match.charCodeAt(1) - 0xDC00;
20884                     cc = (high * 0x400) + low + 0x10000;
20885                 }  else if (
20886                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20887                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20888                     (cc >= 0xf900 && cc < 0xfb00 )
20889                 ) {
20890                         return match;
20891                 }  
20892          
20893                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20894                 return "&#" + cc + ";";
20895                 
20896                 
20897             });
20898             
20899             
20900              
20901             if(this.owner.fireEvent('beforesync', this, html) !== false){
20902                 this.el.dom.value = html;
20903                 this.owner.fireEvent('sync', this, html);
20904             }
20905         }
20906     },
20907
20908     /**
20909      * Protected method that will not generally be called directly. Pushes the value of the textarea
20910      * into the iframe editor.
20911      */
20912     pushValue : function(){
20913         if(this.initialized){
20914             var v = this.el.dom.value.trim();
20915             
20916 //            if(v.length < 1){
20917 //                v = '&#160;';
20918 //            }
20919             
20920             if(this.owner.fireEvent('beforepush', this, v) !== false){
20921                 var d = (this.doc.body || this.doc.documentElement);
20922                 d.innerHTML = v;
20923                 this.cleanUpPaste();
20924                 this.el.dom.value = d.innerHTML;
20925                 this.owner.fireEvent('push', this, v);
20926             }
20927         }
20928     },
20929
20930     // private
20931     deferFocus : function(){
20932         this.focus.defer(10, this);
20933     },
20934
20935     // doc'ed in Field
20936     focus : function(){
20937         if(this.win && !this.sourceEditMode){
20938             this.win.focus();
20939         }else{
20940             this.el.focus();
20941         }
20942     },
20943     
20944     assignDocWin: function()
20945     {
20946         var iframe = this.iframe;
20947         
20948          if(Roo.isIE){
20949             this.doc = iframe.contentWindow.document;
20950             this.win = iframe.contentWindow;
20951         } else {
20952 //            if (!Roo.get(this.frameId)) {
20953 //                return;
20954 //            }
20955 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20956 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20957             
20958             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20959                 return;
20960             }
20961             
20962             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20963             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20964         }
20965     },
20966     
20967     // private
20968     initEditor : function(){
20969         //console.log("INIT EDITOR");
20970         this.assignDocWin();
20971         
20972         
20973         
20974         this.doc.designMode="on";
20975         this.doc.open();
20976         this.doc.write(this.getDocMarkup());
20977         this.doc.close();
20978         
20979         var dbody = (this.doc.body || this.doc.documentElement);
20980         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20981         // this copies styles from the containing element into thsi one..
20982         // not sure why we need all of this..
20983         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20984         
20985         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20986         //ss['background-attachment'] = 'fixed'; // w3c
20987         dbody.bgProperties = 'fixed'; // ie
20988         //Roo.DomHelper.applyStyles(dbody, ss);
20989         Roo.EventManager.on(this.doc, {
20990             //'mousedown': this.onEditorEvent,
20991             'mouseup': this.onEditorEvent,
20992             'dblclick': this.onEditorEvent,
20993             'click': this.onEditorEvent,
20994             'keyup': this.onEditorEvent,
20995             buffer:100,
20996             scope: this
20997         });
20998         if(Roo.isGecko){
20999             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21000         }
21001         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21002             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21003         }
21004         this.initialized = true;
21005
21006         this.owner.fireEvent('initialize', this);
21007         this.pushValue();
21008     },
21009
21010     // private
21011     onDestroy : function(){
21012         
21013         
21014         
21015         if(this.rendered){
21016             
21017             //for (var i =0; i < this.toolbars.length;i++) {
21018             //    // fixme - ask toolbars for heights?
21019             //    this.toolbars[i].onDestroy();
21020            // }
21021             
21022             //this.wrap.dom.innerHTML = '';
21023             //this.wrap.remove();
21024         }
21025     },
21026
21027     // private
21028     onFirstFocus : function(){
21029         
21030         this.assignDocWin();
21031         
21032         
21033         this.activated = true;
21034          
21035     
21036         if(Roo.isGecko){ // prevent silly gecko errors
21037             this.win.focus();
21038             var s = this.win.getSelection();
21039             if(!s.focusNode || s.focusNode.nodeType != 3){
21040                 var r = s.getRangeAt(0);
21041                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21042                 r.collapse(true);
21043                 this.deferFocus();
21044             }
21045             try{
21046                 this.execCmd('useCSS', true);
21047                 this.execCmd('styleWithCSS', false);
21048             }catch(e){}
21049         }
21050         this.owner.fireEvent('activate', this);
21051     },
21052
21053     // private
21054     adjustFont: function(btn){
21055         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21056         //if(Roo.isSafari){ // safari
21057         //    adjust *= 2;
21058        // }
21059         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21060         if(Roo.isSafari){ // safari
21061             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21062             v =  (v < 10) ? 10 : v;
21063             v =  (v > 48) ? 48 : v;
21064             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21065             
21066         }
21067         
21068         
21069         v = Math.max(1, v+adjust);
21070         
21071         this.execCmd('FontSize', v  );
21072     },
21073
21074     onEditorEvent : function(e)
21075     {
21076         this.owner.fireEvent('editorevent', this, e);
21077       //  this.updateToolbar();
21078         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21079     },
21080
21081     insertTag : function(tg)
21082     {
21083         // could be a bit smarter... -> wrap the current selected tRoo..
21084         if (tg.toLowerCase() == 'span' ||
21085             tg.toLowerCase() == 'code' ||
21086             tg.toLowerCase() == 'sup' ||
21087             tg.toLowerCase() == 'sub' 
21088             ) {
21089             
21090             range = this.createRange(this.getSelection());
21091             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21092             wrappingNode.appendChild(range.extractContents());
21093             range.insertNode(wrappingNode);
21094
21095             return;
21096             
21097             
21098             
21099         }
21100         this.execCmd("formatblock",   tg);
21101         
21102     },
21103     
21104     insertText : function(txt)
21105     {
21106         
21107         
21108         var range = this.createRange();
21109         range.deleteContents();
21110                //alert(Sender.getAttribute('label'));
21111                
21112         range.insertNode(this.doc.createTextNode(txt));
21113     } ,
21114     
21115      
21116
21117     /**
21118      * Executes a Midas editor command on the editor document and performs necessary focus and
21119      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21120      * @param {String} cmd The Midas command
21121      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21122      */
21123     relayCmd : function(cmd, value){
21124         this.win.focus();
21125         this.execCmd(cmd, value);
21126         this.owner.fireEvent('editorevent', this);
21127         //this.updateToolbar();
21128         this.owner.deferFocus();
21129     },
21130
21131     /**
21132      * Executes a Midas editor command directly on the editor document.
21133      * For visual commands, you should use {@link #relayCmd} instead.
21134      * <b>This should only be called after the editor is initialized.</b>
21135      * @param {String} cmd The Midas command
21136      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21137      */
21138     execCmd : function(cmd, value){
21139         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21140         this.syncValue();
21141     },
21142  
21143  
21144    
21145     /**
21146      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21147      * to insert tRoo.
21148      * @param {String} text | dom node.. 
21149      */
21150     insertAtCursor : function(text)
21151     {
21152         
21153         if(!this.activated){
21154             return;
21155         }
21156         /*
21157         if(Roo.isIE){
21158             this.win.focus();
21159             var r = this.doc.selection.createRange();
21160             if(r){
21161                 r.collapse(true);
21162                 r.pasteHTML(text);
21163                 this.syncValue();
21164                 this.deferFocus();
21165             
21166             }
21167             return;
21168         }
21169         */
21170         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21171             this.win.focus();
21172             
21173             
21174             // from jquery ui (MIT licenced)
21175             var range, node;
21176             var win = this.win;
21177             
21178             if (win.getSelection && win.getSelection().getRangeAt) {
21179                 range = win.getSelection().getRangeAt(0);
21180                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21181                 range.insertNode(node);
21182             } else if (win.document.selection && win.document.selection.createRange) {
21183                 // no firefox support
21184                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21185                 win.document.selection.createRange().pasteHTML(txt);
21186             } else {
21187                 // no firefox support
21188                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21189                 this.execCmd('InsertHTML', txt);
21190             } 
21191             
21192             this.syncValue();
21193             
21194             this.deferFocus();
21195         }
21196     },
21197  // private
21198     mozKeyPress : function(e){
21199         if(e.ctrlKey){
21200             var c = e.getCharCode(), cmd;
21201           
21202             if(c > 0){
21203                 c = String.fromCharCode(c).toLowerCase();
21204                 switch(c){
21205                     case 'b':
21206                         cmd = 'bold';
21207                         break;
21208                     case 'i':
21209                         cmd = 'italic';
21210                         break;
21211                     
21212                     case 'u':
21213                         cmd = 'underline';
21214                         break;
21215                     
21216                     case 'v':
21217                         this.cleanUpPaste.defer(100, this);
21218                         return;
21219                         
21220                 }
21221                 if(cmd){
21222                     this.win.focus();
21223                     this.execCmd(cmd);
21224                     this.deferFocus();
21225                     e.preventDefault();
21226                 }
21227                 
21228             }
21229         }
21230     },
21231
21232     // private
21233     fixKeys : function(){ // load time branching for fastest keydown performance
21234         if(Roo.isIE){
21235             return function(e){
21236                 var k = e.getKey(), r;
21237                 if(k == e.TAB){
21238                     e.stopEvent();
21239                     r = this.doc.selection.createRange();
21240                     if(r){
21241                         r.collapse(true);
21242                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21243                         this.deferFocus();
21244                     }
21245                     return;
21246                 }
21247                 
21248                 if(k == e.ENTER){
21249                     r = this.doc.selection.createRange();
21250                     if(r){
21251                         var target = r.parentElement();
21252                         if(!target || target.tagName.toLowerCase() != 'li'){
21253                             e.stopEvent();
21254                             r.pasteHTML('<br />');
21255                             r.collapse(false);
21256                             r.select();
21257                         }
21258                     }
21259                 }
21260                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21261                     this.cleanUpPaste.defer(100, this);
21262                     return;
21263                 }
21264                 
21265                 
21266             };
21267         }else if(Roo.isOpera){
21268             return function(e){
21269                 var k = e.getKey();
21270                 if(k == e.TAB){
21271                     e.stopEvent();
21272                     this.win.focus();
21273                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21274                     this.deferFocus();
21275                 }
21276                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21277                     this.cleanUpPaste.defer(100, this);
21278                     return;
21279                 }
21280                 
21281             };
21282         }else if(Roo.isSafari){
21283             return function(e){
21284                 var k = e.getKey();
21285                 
21286                 if(k == e.TAB){
21287                     e.stopEvent();
21288                     this.execCmd('InsertText','\t');
21289                     this.deferFocus();
21290                     return;
21291                 }
21292                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21293                     this.cleanUpPaste.defer(100, this);
21294                     return;
21295                 }
21296                 
21297              };
21298         }
21299     }(),
21300     
21301     getAllAncestors: function()
21302     {
21303         var p = this.getSelectedNode();
21304         var a = [];
21305         if (!p) {
21306             a.push(p); // push blank onto stack..
21307             p = this.getParentElement();
21308         }
21309         
21310         
21311         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21312             a.push(p);
21313             p = p.parentNode;
21314         }
21315         a.push(this.doc.body);
21316         return a;
21317     },
21318     lastSel : false,
21319     lastSelNode : false,
21320     
21321     
21322     getSelection : function() 
21323     {
21324         this.assignDocWin();
21325         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21326     },
21327     
21328     getSelectedNode: function() 
21329     {
21330         // this may only work on Gecko!!!
21331         
21332         // should we cache this!!!!
21333         
21334         
21335         
21336          
21337         var range = this.createRange(this.getSelection()).cloneRange();
21338         
21339         if (Roo.isIE) {
21340             var parent = range.parentElement();
21341             while (true) {
21342                 var testRange = range.duplicate();
21343                 testRange.moveToElementText(parent);
21344                 if (testRange.inRange(range)) {
21345                     break;
21346                 }
21347                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21348                     break;
21349                 }
21350                 parent = parent.parentElement;
21351             }
21352             return parent;
21353         }
21354         
21355         // is ancestor a text element.
21356         var ac =  range.commonAncestorContainer;
21357         if (ac.nodeType == 3) {
21358             ac = ac.parentNode;
21359         }
21360         
21361         var ar = ac.childNodes;
21362          
21363         var nodes = [];
21364         var other_nodes = [];
21365         var has_other_nodes = false;
21366         for (var i=0;i<ar.length;i++) {
21367             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21368                 continue;
21369             }
21370             // fullly contained node.
21371             
21372             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21373                 nodes.push(ar[i]);
21374                 continue;
21375             }
21376             
21377             // probably selected..
21378             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21379                 other_nodes.push(ar[i]);
21380                 continue;
21381             }
21382             // outer..
21383             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21384                 continue;
21385             }
21386             
21387             
21388             has_other_nodes = true;
21389         }
21390         if (!nodes.length && other_nodes.length) {
21391             nodes= other_nodes;
21392         }
21393         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21394             return false;
21395         }
21396         
21397         return nodes[0];
21398     },
21399     createRange: function(sel)
21400     {
21401         // this has strange effects when using with 
21402         // top toolbar - not sure if it's a great idea.
21403         //this.editor.contentWindow.focus();
21404         if (typeof sel != "undefined") {
21405             try {
21406                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21407             } catch(e) {
21408                 return this.doc.createRange();
21409             }
21410         } else {
21411             return this.doc.createRange();
21412         }
21413     },
21414     getParentElement: function()
21415     {
21416         
21417         this.assignDocWin();
21418         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21419         
21420         var range = this.createRange(sel);
21421          
21422         try {
21423             var p = range.commonAncestorContainer;
21424             while (p.nodeType == 3) { // text node
21425                 p = p.parentNode;
21426             }
21427             return p;
21428         } catch (e) {
21429             return null;
21430         }
21431     
21432     },
21433     /***
21434      *
21435      * Range intersection.. the hard stuff...
21436      *  '-1' = before
21437      *  '0' = hits..
21438      *  '1' = after.
21439      *         [ -- selected range --- ]
21440      *   [fail]                        [fail]
21441      *
21442      *    basically..
21443      *      if end is before start or  hits it. fail.
21444      *      if start is after end or hits it fail.
21445      *
21446      *   if either hits (but other is outside. - then it's not 
21447      *   
21448      *    
21449      **/
21450     
21451     
21452     // @see http://www.thismuchiknow.co.uk/?p=64.
21453     rangeIntersectsNode : function(range, node)
21454     {
21455         var nodeRange = node.ownerDocument.createRange();
21456         try {
21457             nodeRange.selectNode(node);
21458         } catch (e) {
21459             nodeRange.selectNodeContents(node);
21460         }
21461     
21462         var rangeStartRange = range.cloneRange();
21463         rangeStartRange.collapse(true);
21464     
21465         var rangeEndRange = range.cloneRange();
21466         rangeEndRange.collapse(false);
21467     
21468         var nodeStartRange = nodeRange.cloneRange();
21469         nodeStartRange.collapse(true);
21470     
21471         var nodeEndRange = nodeRange.cloneRange();
21472         nodeEndRange.collapse(false);
21473     
21474         return rangeStartRange.compareBoundaryPoints(
21475                  Range.START_TO_START, nodeEndRange) == -1 &&
21476                rangeEndRange.compareBoundaryPoints(
21477                  Range.START_TO_START, nodeStartRange) == 1;
21478         
21479          
21480     },
21481     rangeCompareNode : function(range, node)
21482     {
21483         var nodeRange = node.ownerDocument.createRange();
21484         try {
21485             nodeRange.selectNode(node);
21486         } catch (e) {
21487             nodeRange.selectNodeContents(node);
21488         }
21489         
21490         
21491         range.collapse(true);
21492     
21493         nodeRange.collapse(true);
21494      
21495         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21496         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21497          
21498         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21499         
21500         var nodeIsBefore   =  ss == 1;
21501         var nodeIsAfter    = ee == -1;
21502         
21503         if (nodeIsBefore && nodeIsAfter) {
21504             return 0; // outer
21505         }
21506         if (!nodeIsBefore && nodeIsAfter) {
21507             return 1; //right trailed.
21508         }
21509         
21510         if (nodeIsBefore && !nodeIsAfter) {
21511             return 2;  // left trailed.
21512         }
21513         // fully contined.
21514         return 3;
21515     },
21516
21517     // private? - in a new class?
21518     cleanUpPaste :  function()
21519     {
21520         // cleans up the whole document..
21521         Roo.log('cleanuppaste');
21522         
21523         this.cleanUpChildren(this.doc.body);
21524         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21525         if (clean != this.doc.body.innerHTML) {
21526             this.doc.body.innerHTML = clean;
21527         }
21528         
21529     },
21530     
21531     cleanWordChars : function(input) {// change the chars to hex code
21532         var he = Roo.HtmlEditorCore;
21533         
21534         var output = input;
21535         Roo.each(he.swapCodes, function(sw) { 
21536             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21537             
21538             output = output.replace(swapper, sw[1]);
21539         });
21540         
21541         return output;
21542     },
21543     
21544     
21545     cleanUpChildren : function (n)
21546     {
21547         if (!n.childNodes.length) {
21548             return;
21549         }
21550         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21551            this.cleanUpChild(n.childNodes[i]);
21552         }
21553     },
21554     
21555     
21556         
21557     
21558     cleanUpChild : function (node)
21559     {
21560         var ed = this;
21561         //console.log(node);
21562         if (node.nodeName == "#text") {
21563             // clean up silly Windows -- stuff?
21564             return; 
21565         }
21566         if (node.nodeName == "#comment") {
21567             if (!this.allowComments) {
21568                 node.parentNode.removeChild(node);
21569             }
21570             // clean up silly Windows -- stuff?
21571             return; 
21572         }
21573         var lcname = node.tagName.toLowerCase();
21574         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21575         // whitelist of tags..
21576         
21577         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21578             // remove node.
21579             node.parentNode.removeChild(node);
21580             return;
21581             
21582         }
21583         
21584         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21585         
21586         // spans with no attributes - just remove them..
21587         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21588             remove_keep_children = true;
21589         }
21590         
21591         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21592         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21593         
21594         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21595         //    remove_keep_children = true;
21596         //}
21597         
21598         if (remove_keep_children) {
21599             this.cleanUpChildren(node);
21600             // inserts everything just before this node...
21601             while (node.childNodes.length) {
21602                 var cn = node.childNodes[0];
21603                 node.removeChild(cn);
21604                 node.parentNode.insertBefore(cn, node);
21605             }
21606             node.parentNode.removeChild(node);
21607             return;
21608         }
21609         
21610         if (!node.attributes || !node.attributes.length) {
21611             
21612           
21613             
21614             
21615             this.cleanUpChildren(node);
21616             return;
21617         }
21618         
21619         function cleanAttr(n,v)
21620         {
21621             
21622             if (v.match(/^\./) || v.match(/^\//)) {
21623                 return;
21624             }
21625             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21626                 return;
21627             }
21628             if (v.match(/^#/)) {
21629                 return;
21630             }
21631             if (v.match(/^\{/)) { // allow template editing.
21632                 return;
21633             }
21634 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21635             node.removeAttribute(n);
21636             
21637         }
21638         
21639         var cwhite = this.cwhite;
21640         var cblack = this.cblack;
21641             
21642         function cleanStyle(n,v)
21643         {
21644             if (v.match(/expression/)) { //XSS?? should we even bother..
21645                 node.removeAttribute(n);
21646                 return;
21647             }
21648             
21649             var parts = v.split(/;/);
21650             var clean = [];
21651             
21652             Roo.each(parts, function(p) {
21653                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21654                 if (!p.length) {
21655                     return true;
21656                 }
21657                 var l = p.split(':').shift().replace(/\s+/g,'');
21658                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21659                 
21660                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21661 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21662                     //node.removeAttribute(n);
21663                     return true;
21664                 }
21665                 //Roo.log()
21666                 // only allow 'c whitelisted system attributes'
21667                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21668 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21669                     //node.removeAttribute(n);
21670                     return true;
21671                 }
21672                 
21673                 
21674                  
21675                 
21676                 clean.push(p);
21677                 return true;
21678             });
21679             if (clean.length) { 
21680                 node.setAttribute(n, clean.join(';'));
21681             } else {
21682                 node.removeAttribute(n);
21683             }
21684             
21685         }
21686         
21687         
21688         for (var i = node.attributes.length-1; i > -1 ; i--) {
21689             var a = node.attributes[i];
21690             //console.log(a);
21691             
21692             if (a.name.toLowerCase().substr(0,2)=='on')  {
21693                 node.removeAttribute(a.name);
21694                 continue;
21695             }
21696             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21697                 node.removeAttribute(a.name);
21698                 continue;
21699             }
21700             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21701                 cleanAttr(a.name,a.value); // fixme..
21702                 continue;
21703             }
21704             if (a.name == 'style') {
21705                 cleanStyle(a.name,a.value);
21706                 continue;
21707             }
21708             /// clean up MS crap..
21709             // tecnically this should be a list of valid class'es..
21710             
21711             
21712             if (a.name == 'class') {
21713                 if (a.value.match(/^Mso/)) {
21714                     node.removeAttribute('class');
21715                 }
21716                 
21717                 if (a.value.match(/^body$/)) {
21718                     node.removeAttribute('class');
21719                 }
21720                 continue;
21721             }
21722             
21723             // style cleanup!?
21724             // class cleanup?
21725             
21726         }
21727         
21728         
21729         this.cleanUpChildren(node);
21730         
21731         
21732     },
21733     
21734     /**
21735      * Clean up MS wordisms...
21736      */
21737     cleanWord : function(node)
21738     {
21739         if (!node) {
21740             this.cleanWord(this.doc.body);
21741             return;
21742         }
21743         
21744         if(
21745                 node.nodeName == 'SPAN' &&
21746                 !node.hasAttributes() &&
21747                 node.childNodes.length == 1 &&
21748                 node.firstChild.nodeName == "#text"  
21749         ) {
21750             var textNode = node.firstChild;
21751             node.removeChild(textNode);
21752             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21753                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21754             }
21755             node.parentNode.insertBefore(textNode, node);
21756             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21757                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21758             }
21759             node.parentNode.removeChild(node);
21760         }
21761         
21762         if (node.nodeName == "#text") {
21763             // clean up silly Windows -- stuff?
21764             return; 
21765         }
21766         if (node.nodeName == "#comment") {
21767             node.parentNode.removeChild(node);
21768             // clean up silly Windows -- stuff?
21769             return; 
21770         }
21771         
21772         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21773             node.parentNode.removeChild(node);
21774             return;
21775         }
21776         //Roo.log(node.tagName);
21777         // remove - but keep children..
21778         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21779             //Roo.log('-- removed');
21780             while (node.childNodes.length) {
21781                 var cn = node.childNodes[0];
21782                 node.removeChild(cn);
21783                 node.parentNode.insertBefore(cn, node);
21784                 // move node to parent - and clean it..
21785                 this.cleanWord(cn);
21786             }
21787             node.parentNode.removeChild(node);
21788             /// no need to iterate chidlren = it's got none..
21789             //this.iterateChildren(node, this.cleanWord);
21790             return;
21791         }
21792         // clean styles
21793         if (node.className.length) {
21794             
21795             var cn = node.className.split(/\W+/);
21796             var cna = [];
21797             Roo.each(cn, function(cls) {
21798                 if (cls.match(/Mso[a-zA-Z]+/)) {
21799                     return;
21800                 }
21801                 cna.push(cls);
21802             });
21803             node.className = cna.length ? cna.join(' ') : '';
21804             if (!cna.length) {
21805                 node.removeAttribute("class");
21806             }
21807         }
21808         
21809         if (node.hasAttribute("lang")) {
21810             node.removeAttribute("lang");
21811         }
21812         
21813         if (node.hasAttribute("style")) {
21814             
21815             var styles = node.getAttribute("style").split(";");
21816             var nstyle = [];
21817             Roo.each(styles, function(s) {
21818                 if (!s.match(/:/)) {
21819                     return;
21820                 }
21821                 var kv = s.split(":");
21822                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21823                     return;
21824                 }
21825                 // what ever is left... we allow.
21826                 nstyle.push(s);
21827             });
21828             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21829             if (!nstyle.length) {
21830                 node.removeAttribute('style');
21831             }
21832         }
21833         this.iterateChildren(node, this.cleanWord);
21834         
21835         
21836         
21837     },
21838     /**
21839      * iterateChildren of a Node, calling fn each time, using this as the scole..
21840      * @param {DomNode} node node to iterate children of.
21841      * @param {Function} fn method of this class to call on each item.
21842      */
21843     iterateChildren : function(node, fn)
21844     {
21845         if (!node.childNodes.length) {
21846                 return;
21847         }
21848         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21849            fn.call(this, node.childNodes[i])
21850         }
21851     },
21852     
21853     
21854     /**
21855      * cleanTableWidths.
21856      *
21857      * Quite often pasting from word etc.. results in tables with column and widths.
21858      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21859      *
21860      */
21861     cleanTableWidths : function(node)
21862     {
21863          
21864          
21865         if (!node) {
21866             this.cleanTableWidths(this.doc.body);
21867             return;
21868         }
21869         
21870         // ignore list...
21871         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21872             return; 
21873         }
21874         Roo.log(node.tagName);
21875         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21876             this.iterateChildren(node, this.cleanTableWidths);
21877             return;
21878         }
21879         if (node.hasAttribute('width')) {
21880             node.removeAttribute('width');
21881         }
21882         
21883          
21884         if (node.hasAttribute("style")) {
21885             // pretty basic...
21886             
21887             var styles = node.getAttribute("style").split(";");
21888             var nstyle = [];
21889             Roo.each(styles, function(s) {
21890                 if (!s.match(/:/)) {
21891                     return;
21892                 }
21893                 var kv = s.split(":");
21894                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21895                     return;
21896                 }
21897                 // what ever is left... we allow.
21898                 nstyle.push(s);
21899             });
21900             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21901             if (!nstyle.length) {
21902                 node.removeAttribute('style');
21903             }
21904         }
21905         
21906         this.iterateChildren(node, this.cleanTableWidths);
21907         
21908         
21909     },
21910     
21911     
21912     
21913     
21914     domToHTML : function(currentElement, depth, nopadtext) {
21915         
21916         depth = depth || 0;
21917         nopadtext = nopadtext || false;
21918     
21919         if (!currentElement) {
21920             return this.domToHTML(this.doc.body);
21921         }
21922         
21923         //Roo.log(currentElement);
21924         var j;
21925         var allText = false;
21926         var nodeName = currentElement.nodeName;
21927         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21928         
21929         if  (nodeName == '#text') {
21930             
21931             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21932         }
21933         
21934         
21935         var ret = '';
21936         if (nodeName != 'BODY') {
21937              
21938             var i = 0;
21939             // Prints the node tagName, such as <A>, <IMG>, etc
21940             if (tagName) {
21941                 var attr = [];
21942                 for(i = 0; i < currentElement.attributes.length;i++) {
21943                     // quoting?
21944                     var aname = currentElement.attributes.item(i).name;
21945                     if (!currentElement.attributes.item(i).value.length) {
21946                         continue;
21947                     }
21948                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21949                 }
21950                 
21951                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21952             } 
21953             else {
21954                 
21955                 // eack
21956             }
21957         } else {
21958             tagName = false;
21959         }
21960         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21961             return ret;
21962         }
21963         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21964             nopadtext = true;
21965         }
21966         
21967         
21968         // Traverse the tree
21969         i = 0;
21970         var currentElementChild = currentElement.childNodes.item(i);
21971         var allText = true;
21972         var innerHTML  = '';
21973         lastnode = '';
21974         while (currentElementChild) {
21975             // Formatting code (indent the tree so it looks nice on the screen)
21976             var nopad = nopadtext;
21977             if (lastnode == 'SPAN') {
21978                 nopad  = true;
21979             }
21980             // text
21981             if  (currentElementChild.nodeName == '#text') {
21982                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21983                 toadd = nopadtext ? toadd : toadd.trim();
21984                 if (!nopad && toadd.length > 80) {
21985                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21986                 }
21987                 innerHTML  += toadd;
21988                 
21989                 i++;
21990                 currentElementChild = currentElement.childNodes.item(i);
21991                 lastNode = '';
21992                 continue;
21993             }
21994             allText = false;
21995             
21996             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21997                 
21998             // Recursively traverse the tree structure of the child node
21999             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22000             lastnode = currentElementChild.nodeName;
22001             i++;
22002             currentElementChild=currentElement.childNodes.item(i);
22003         }
22004         
22005         ret += innerHTML;
22006         
22007         if (!allText) {
22008                 // The remaining code is mostly for formatting the tree
22009             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22010         }
22011         
22012         
22013         if (tagName) {
22014             ret+= "</"+tagName+">";
22015         }
22016         return ret;
22017         
22018     },
22019         
22020     applyBlacklists : function()
22021     {
22022         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22023         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22024         
22025         this.white = [];
22026         this.black = [];
22027         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22028             if (b.indexOf(tag) > -1) {
22029                 return;
22030             }
22031             this.white.push(tag);
22032             
22033         }, this);
22034         
22035         Roo.each(w, function(tag) {
22036             if (b.indexOf(tag) > -1) {
22037                 return;
22038             }
22039             if (this.white.indexOf(tag) > -1) {
22040                 return;
22041             }
22042             this.white.push(tag);
22043             
22044         }, this);
22045         
22046         
22047         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22048             if (w.indexOf(tag) > -1) {
22049                 return;
22050             }
22051             this.black.push(tag);
22052             
22053         }, this);
22054         
22055         Roo.each(b, function(tag) {
22056             if (w.indexOf(tag) > -1) {
22057                 return;
22058             }
22059             if (this.black.indexOf(tag) > -1) {
22060                 return;
22061             }
22062             this.black.push(tag);
22063             
22064         }, this);
22065         
22066         
22067         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22068         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22069         
22070         this.cwhite = [];
22071         this.cblack = [];
22072         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22073             if (b.indexOf(tag) > -1) {
22074                 return;
22075             }
22076             this.cwhite.push(tag);
22077             
22078         }, this);
22079         
22080         Roo.each(w, function(tag) {
22081             if (b.indexOf(tag) > -1) {
22082                 return;
22083             }
22084             if (this.cwhite.indexOf(tag) > -1) {
22085                 return;
22086             }
22087             this.cwhite.push(tag);
22088             
22089         }, this);
22090         
22091         
22092         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22093             if (w.indexOf(tag) > -1) {
22094                 return;
22095             }
22096             this.cblack.push(tag);
22097             
22098         }, this);
22099         
22100         Roo.each(b, function(tag) {
22101             if (w.indexOf(tag) > -1) {
22102                 return;
22103             }
22104             if (this.cblack.indexOf(tag) > -1) {
22105                 return;
22106             }
22107             this.cblack.push(tag);
22108             
22109         }, this);
22110     },
22111     
22112     setStylesheets : function(stylesheets)
22113     {
22114         if(typeof(stylesheets) == 'string'){
22115             Roo.get(this.iframe.contentDocument.head).createChild({
22116                 tag : 'link',
22117                 rel : 'stylesheet',
22118                 type : 'text/css',
22119                 href : stylesheets
22120             });
22121             
22122             return;
22123         }
22124         var _this = this;
22125      
22126         Roo.each(stylesheets, function(s) {
22127             if(!s.length){
22128                 return;
22129             }
22130             
22131             Roo.get(_this.iframe.contentDocument.head).createChild({
22132                 tag : 'link',
22133                 rel : 'stylesheet',
22134                 type : 'text/css',
22135                 href : s
22136             });
22137         });
22138
22139         
22140     },
22141     
22142     removeStylesheets : function()
22143     {
22144         var _this = this;
22145         
22146         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22147             s.remove();
22148         });
22149     },
22150     
22151     setStyle : function(style)
22152     {
22153         Roo.get(this.iframe.contentDocument.head).createChild({
22154             tag : 'style',
22155             type : 'text/css',
22156             html : style
22157         });
22158
22159         return;
22160     }
22161     
22162     // hide stuff that is not compatible
22163     /**
22164      * @event blur
22165      * @hide
22166      */
22167     /**
22168      * @event change
22169      * @hide
22170      */
22171     /**
22172      * @event focus
22173      * @hide
22174      */
22175     /**
22176      * @event specialkey
22177      * @hide
22178      */
22179     /**
22180      * @cfg {String} fieldClass @hide
22181      */
22182     /**
22183      * @cfg {String} focusClass @hide
22184      */
22185     /**
22186      * @cfg {String} autoCreate @hide
22187      */
22188     /**
22189      * @cfg {String} inputType @hide
22190      */
22191     /**
22192      * @cfg {String} invalidClass @hide
22193      */
22194     /**
22195      * @cfg {String} invalidText @hide
22196      */
22197     /**
22198      * @cfg {String} msgFx @hide
22199      */
22200     /**
22201      * @cfg {String} validateOnBlur @hide
22202      */
22203 });
22204
22205 Roo.HtmlEditorCore.white = [
22206         'area', 'br', 'img', 'input', 'hr', 'wbr',
22207         
22208        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22209        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22210        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22211        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22212        'table',   'ul',         'xmp', 
22213        
22214        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22215       'thead',   'tr', 
22216      
22217       'dir', 'menu', 'ol', 'ul', 'dl',
22218        
22219       'embed',  'object'
22220 ];
22221
22222
22223 Roo.HtmlEditorCore.black = [
22224     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22225         'applet', // 
22226         'base',   'basefont', 'bgsound', 'blink',  'body', 
22227         'frame',  'frameset', 'head',    'html',   'ilayer', 
22228         'iframe', 'layer',  'link',     'meta',    'object',   
22229         'script', 'style' ,'title',  'xml' // clean later..
22230 ];
22231 Roo.HtmlEditorCore.clean = [
22232     'script', 'style', 'title', 'xml'
22233 ];
22234 Roo.HtmlEditorCore.remove = [
22235     'font'
22236 ];
22237 // attributes..
22238
22239 Roo.HtmlEditorCore.ablack = [
22240     'on'
22241 ];
22242     
22243 Roo.HtmlEditorCore.aclean = [ 
22244     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22245 ];
22246
22247 // protocols..
22248 Roo.HtmlEditorCore.pwhite= [
22249         'http',  'https',  'mailto'
22250 ];
22251
22252 // white listed style attributes.
22253 Roo.HtmlEditorCore.cwhite= [
22254       //  'text-align', /// default is to allow most things..
22255       
22256          
22257 //        'font-size'//??
22258 ];
22259
22260 // black listed style attributes.
22261 Roo.HtmlEditorCore.cblack= [
22262       //  'font-size' -- this can be set by the project 
22263 ];
22264
22265
22266 Roo.HtmlEditorCore.swapCodes   =[ 
22267     [    8211, "&#8211;" ], 
22268     [    8212, "&#8212;" ], 
22269     [    8216,  "'" ],  
22270     [    8217, "'" ],  
22271     [    8220, '"' ],  
22272     [    8221, '"' ],  
22273     [    8226, "*" ],  
22274     [    8230, "..." ]
22275 ]; 
22276
22277     //<script type="text/javascript">
22278
22279 /*
22280  * Ext JS Library 1.1.1
22281  * Copyright(c) 2006-2007, Ext JS, LLC.
22282  * Licence LGPL
22283  * 
22284  */
22285  
22286  
22287 Roo.form.HtmlEditor = function(config){
22288     
22289     
22290     
22291     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22292     
22293     if (!this.toolbars) {
22294         this.toolbars = [];
22295     }
22296     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22297     
22298     
22299 };
22300
22301 /**
22302  * @class Roo.form.HtmlEditor
22303  * @extends Roo.form.Field
22304  * Provides a lightweight HTML Editor component.
22305  *
22306  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22307  * 
22308  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22309  * supported by this editor.</b><br/><br/>
22310  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22311  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22312  */
22313 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22314     /**
22315      * @cfg {Boolean} clearUp
22316      */
22317     clearUp : true,
22318       /**
22319      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22320      */
22321     toolbars : false,
22322    
22323      /**
22324      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22325      *                        Roo.resizable.
22326      */
22327     resizable : false,
22328      /**
22329      * @cfg {Number} height (in pixels)
22330      */   
22331     height: 300,
22332    /**
22333      * @cfg {Number} width (in pixels)
22334      */   
22335     width: 500,
22336     
22337     /**
22338      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22339      * 
22340      */
22341     stylesheets: false,
22342     
22343     
22344      /**
22345      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22346      * 
22347      */
22348     cblack: false,
22349     /**
22350      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22351      * 
22352      */
22353     cwhite: false,
22354     
22355      /**
22356      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22357      * 
22358      */
22359     black: false,
22360     /**
22361      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22362      * 
22363      */
22364     white: false,
22365     /**
22366      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22367      */
22368     allowComments: false,
22369     
22370     // id of frame..
22371     frameId: false,
22372     
22373     // private properties
22374     validationEvent : false,
22375     deferHeight: true,
22376     initialized : false,
22377     activated : false,
22378     
22379     onFocus : Roo.emptyFn,
22380     iframePad:3,
22381     hideMode:'offsets',
22382     
22383     actionMode : 'container', // defaults to hiding it...
22384     
22385     defaultAutoCreate : { // modified by initCompnoent..
22386         tag: "textarea",
22387         style:"width:500px;height:300px;",
22388         autocomplete: "new-password"
22389     },
22390
22391     // private
22392     initComponent : function(){
22393         this.addEvents({
22394             /**
22395              * @event initialize
22396              * Fires when the editor is fully initialized (including the iframe)
22397              * @param {HtmlEditor} this
22398              */
22399             initialize: true,
22400             /**
22401              * @event activate
22402              * Fires when the editor is first receives the focus. Any insertion must wait
22403              * until after this event.
22404              * @param {HtmlEditor} this
22405              */
22406             activate: true,
22407              /**
22408              * @event beforesync
22409              * Fires before the textarea is updated with content from the editor iframe. Return false
22410              * to cancel the sync.
22411              * @param {HtmlEditor} this
22412              * @param {String} html
22413              */
22414             beforesync: true,
22415              /**
22416              * @event beforepush
22417              * Fires before the iframe editor is updated with content from the textarea. Return false
22418              * to cancel the push.
22419              * @param {HtmlEditor} this
22420              * @param {String} html
22421              */
22422             beforepush: true,
22423              /**
22424              * @event sync
22425              * Fires when the textarea is updated with content from the editor iframe.
22426              * @param {HtmlEditor} this
22427              * @param {String} html
22428              */
22429             sync: true,
22430              /**
22431              * @event push
22432              * Fires when the iframe editor is updated with content from the textarea.
22433              * @param {HtmlEditor} this
22434              * @param {String} html
22435              */
22436             push: true,
22437              /**
22438              * @event editmodechange
22439              * Fires when the editor switches edit modes
22440              * @param {HtmlEditor} this
22441              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22442              */
22443             editmodechange: true,
22444             /**
22445              * @event editorevent
22446              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22447              * @param {HtmlEditor} this
22448              */
22449             editorevent: true,
22450             /**
22451              * @event firstfocus
22452              * Fires when on first focus - needed by toolbars..
22453              * @param {HtmlEditor} this
22454              */
22455             firstfocus: true,
22456             /**
22457              * @event autosave
22458              * Auto save the htmlEditor value as a file into Events
22459              * @param {HtmlEditor} this
22460              */
22461             autosave: true,
22462             /**
22463              * @event savedpreview
22464              * preview the saved version of htmlEditor
22465              * @param {HtmlEditor} this
22466              */
22467             savedpreview: true,
22468             
22469             /**
22470             * @event stylesheetsclick
22471             * Fires when press the Sytlesheets button
22472             * @param {Roo.HtmlEditorCore} this
22473             */
22474             stylesheetsclick: true
22475         });
22476         this.defaultAutoCreate =  {
22477             tag: "textarea",
22478             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22479             autocomplete: "new-password"
22480         };
22481     },
22482
22483     /**
22484      * Protected method that will not generally be called directly. It
22485      * is called when the editor creates its toolbar. Override this method if you need to
22486      * add custom toolbar buttons.
22487      * @param {HtmlEditor} editor
22488      */
22489     createToolbar : function(editor){
22490         Roo.log("create toolbars");
22491         if (!editor.toolbars || !editor.toolbars.length) {
22492             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22493         }
22494         
22495         for (var i =0 ; i < editor.toolbars.length;i++) {
22496             editor.toolbars[i] = Roo.factory(
22497                     typeof(editor.toolbars[i]) == 'string' ?
22498                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22499                 Roo.form.HtmlEditor);
22500             editor.toolbars[i].init(editor);
22501         }
22502          
22503         
22504     },
22505
22506      
22507     // private
22508     onRender : function(ct, position)
22509     {
22510         var _t = this;
22511         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22512         
22513         this.wrap = this.el.wrap({
22514             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22515         });
22516         
22517         this.editorcore.onRender(ct, position);
22518          
22519         if (this.resizable) {
22520             this.resizeEl = new Roo.Resizable(this.wrap, {
22521                 pinned : true,
22522                 wrap: true,
22523                 dynamic : true,
22524                 minHeight : this.height,
22525                 height: this.height,
22526                 handles : this.resizable,
22527                 width: this.width,
22528                 listeners : {
22529                     resize : function(r, w, h) {
22530                         _t.onResize(w,h); // -something
22531                     }
22532                 }
22533             });
22534             
22535         }
22536         this.createToolbar(this);
22537        
22538         
22539         if(!this.width){
22540             this.setSize(this.wrap.getSize());
22541         }
22542         if (this.resizeEl) {
22543             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22544             // should trigger onReize..
22545         }
22546         
22547         this.keyNav = new Roo.KeyNav(this.el, {
22548             
22549             "tab" : function(e){
22550                 e.preventDefault();
22551                 
22552                 var value = this.getValue();
22553                 
22554                 var start = this.el.dom.selectionStart;
22555                 var end = this.el.dom.selectionEnd;
22556                 
22557                 if(!e.shiftKey){
22558                     
22559                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22560                     this.el.dom.setSelectionRange(end + 1, end + 1);
22561                     return;
22562                 }
22563                 
22564                 var f = value.substring(0, start).split("\t");
22565                 
22566                 if(f.pop().length != 0){
22567                     return;
22568                 }
22569                 
22570                 this.setValue(f.join("\t") + value.substring(end));
22571                 this.el.dom.setSelectionRange(start - 1, start - 1);
22572                 
22573             },
22574             
22575             "home" : function(e){
22576                 e.preventDefault();
22577                 
22578                 var curr = this.el.dom.selectionStart;
22579                 var lines = this.getValue().split("\n");
22580                 
22581                 if(!lines.length){
22582                     return;
22583                 }
22584                 
22585                 if(e.ctrlKey){
22586                     this.el.dom.setSelectionRange(0, 0);
22587                     return;
22588                 }
22589                 
22590                 var pos = 0;
22591                 
22592                 for (var i = 0; i < lines.length;i++) {
22593                     pos += lines[i].length;
22594                     
22595                     if(i != 0){
22596                         pos += 1;
22597                     }
22598                     
22599                     if(pos < curr){
22600                         continue;
22601                     }
22602                     
22603                     pos -= lines[i].length;
22604                     
22605                     break;
22606                 }
22607                 
22608                 if(!e.shiftKey){
22609                     this.el.dom.setSelectionRange(pos, pos);
22610                     return;
22611                 }
22612                 
22613                 this.el.dom.selectionStart = pos;
22614                 this.el.dom.selectionEnd = curr;
22615             },
22616             
22617             "end" : function(e){
22618                 e.preventDefault();
22619                 
22620                 var curr = this.el.dom.selectionStart;
22621                 var lines = this.getValue().split("\n");
22622                 
22623                 if(!lines.length){
22624                     return;
22625                 }
22626                 
22627                 if(e.ctrlKey){
22628                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22629                     return;
22630                 }
22631                 
22632                 var pos = 0;
22633                 
22634                 for (var i = 0; i < lines.length;i++) {
22635                     
22636                     pos += lines[i].length;
22637                     
22638                     if(i != 0){
22639                         pos += 1;
22640                     }
22641                     
22642                     if(pos < curr){
22643                         continue;
22644                     }
22645                     
22646                     break;
22647                 }
22648                 
22649                 if(!e.shiftKey){
22650                     this.el.dom.setSelectionRange(pos, pos);
22651                     return;
22652                 }
22653                 
22654                 this.el.dom.selectionStart = curr;
22655                 this.el.dom.selectionEnd = pos;
22656             },
22657
22658             scope : this,
22659
22660             doRelay : function(foo, bar, hname){
22661                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22662             },
22663
22664             forceKeyDown: true
22665         });
22666         
22667 //        if(this.autosave && this.w){
22668 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22669 //        }
22670     },
22671
22672     // private
22673     onResize : function(w, h)
22674     {
22675         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22676         var ew = false;
22677         var eh = false;
22678         
22679         if(this.el ){
22680             if(typeof w == 'number'){
22681                 var aw = w - this.wrap.getFrameWidth('lr');
22682                 this.el.setWidth(this.adjustWidth('textarea', aw));
22683                 ew = aw;
22684             }
22685             if(typeof h == 'number'){
22686                 var tbh = 0;
22687                 for (var i =0; i < this.toolbars.length;i++) {
22688                     // fixme - ask toolbars for heights?
22689                     tbh += this.toolbars[i].tb.el.getHeight();
22690                     if (this.toolbars[i].footer) {
22691                         tbh += this.toolbars[i].footer.el.getHeight();
22692                     }
22693                 }
22694                 
22695                 
22696                 
22697                 
22698                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22699                 ah -= 5; // knock a few pixes off for look..
22700 //                Roo.log(ah);
22701                 this.el.setHeight(this.adjustWidth('textarea', ah));
22702                 var eh = ah;
22703             }
22704         }
22705         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22706         this.editorcore.onResize(ew,eh);
22707         
22708     },
22709
22710     /**
22711      * Toggles the editor between standard and source edit mode.
22712      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22713      */
22714     toggleSourceEdit : function(sourceEditMode)
22715     {
22716         this.editorcore.toggleSourceEdit(sourceEditMode);
22717         
22718         if(this.editorcore.sourceEditMode){
22719             Roo.log('editor - showing textarea');
22720             
22721 //            Roo.log('in');
22722 //            Roo.log(this.syncValue());
22723             this.editorcore.syncValue();
22724             this.el.removeClass('x-hidden');
22725             this.el.dom.removeAttribute('tabIndex');
22726             this.el.focus();
22727             
22728             for (var i = 0; i < this.toolbars.length; i++) {
22729                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22730                     this.toolbars[i].tb.hide();
22731                     this.toolbars[i].footer.hide();
22732                 }
22733             }
22734             
22735         }else{
22736             Roo.log('editor - hiding textarea');
22737 //            Roo.log('out')
22738 //            Roo.log(this.pushValue()); 
22739             this.editorcore.pushValue();
22740             
22741             this.el.addClass('x-hidden');
22742             this.el.dom.setAttribute('tabIndex', -1);
22743             
22744             for (var i = 0; i < this.toolbars.length; i++) {
22745                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22746                     this.toolbars[i].tb.show();
22747                     this.toolbars[i].footer.show();
22748                 }
22749             }
22750             
22751             //this.deferFocus();
22752         }
22753         
22754         this.setSize(this.wrap.getSize());
22755         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22756         
22757         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22758     },
22759  
22760     // private (for BoxComponent)
22761     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22762
22763     // private (for BoxComponent)
22764     getResizeEl : function(){
22765         return this.wrap;
22766     },
22767
22768     // private (for BoxComponent)
22769     getPositionEl : function(){
22770         return this.wrap;
22771     },
22772
22773     // private
22774     initEvents : function(){
22775         this.originalValue = this.getValue();
22776     },
22777
22778     /**
22779      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22780      * @method
22781      */
22782     markInvalid : Roo.emptyFn,
22783     /**
22784      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22785      * @method
22786      */
22787     clearInvalid : Roo.emptyFn,
22788
22789     setValue : function(v){
22790         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22791         this.editorcore.pushValue();
22792     },
22793
22794      
22795     // private
22796     deferFocus : function(){
22797         this.focus.defer(10, this);
22798     },
22799
22800     // doc'ed in Field
22801     focus : function(){
22802         this.editorcore.focus();
22803         
22804     },
22805       
22806
22807     // private
22808     onDestroy : function(){
22809         
22810         
22811         
22812         if(this.rendered){
22813             
22814             for (var i =0; i < this.toolbars.length;i++) {
22815                 // fixme - ask toolbars for heights?
22816                 this.toolbars[i].onDestroy();
22817             }
22818             
22819             this.wrap.dom.innerHTML = '';
22820             this.wrap.remove();
22821         }
22822     },
22823
22824     // private
22825     onFirstFocus : function(){
22826         //Roo.log("onFirstFocus");
22827         this.editorcore.onFirstFocus();
22828          for (var i =0; i < this.toolbars.length;i++) {
22829             this.toolbars[i].onFirstFocus();
22830         }
22831         
22832     },
22833     
22834     // private
22835     syncValue : function()
22836     {
22837         this.editorcore.syncValue();
22838     },
22839     
22840     pushValue : function()
22841     {
22842         this.editorcore.pushValue();
22843     },
22844     
22845     setStylesheets : function(stylesheets)
22846     {
22847         this.editorcore.setStylesheets(stylesheets);
22848     },
22849     
22850     removeStylesheets : function()
22851     {
22852         this.editorcore.removeStylesheets();
22853     }
22854      
22855     
22856     // hide stuff that is not compatible
22857     /**
22858      * @event blur
22859      * @hide
22860      */
22861     /**
22862      * @event change
22863      * @hide
22864      */
22865     /**
22866      * @event focus
22867      * @hide
22868      */
22869     /**
22870      * @event specialkey
22871      * @hide
22872      */
22873     /**
22874      * @cfg {String} fieldClass @hide
22875      */
22876     /**
22877      * @cfg {String} focusClass @hide
22878      */
22879     /**
22880      * @cfg {String} autoCreate @hide
22881      */
22882     /**
22883      * @cfg {String} inputType @hide
22884      */
22885     /**
22886      * @cfg {String} invalidClass @hide
22887      */
22888     /**
22889      * @cfg {String} invalidText @hide
22890      */
22891     /**
22892      * @cfg {String} msgFx @hide
22893      */
22894     /**
22895      * @cfg {String} validateOnBlur @hide
22896      */
22897 });
22898  
22899     // <script type="text/javascript">
22900 /*
22901  * Based on
22902  * Ext JS Library 1.1.1
22903  * Copyright(c) 2006-2007, Ext JS, LLC.
22904  *  
22905  
22906  */
22907
22908 /**
22909  * @class Roo.form.HtmlEditorToolbar1
22910  * Basic Toolbar
22911  * 
22912  * Usage:
22913  *
22914  new Roo.form.HtmlEditor({
22915     ....
22916     toolbars : [
22917         new Roo.form.HtmlEditorToolbar1({
22918             disable : { fonts: 1 , format: 1, ..., ... , ...],
22919             btns : [ .... ]
22920         })
22921     }
22922      
22923  * 
22924  * @cfg {Object} disable List of elements to disable..
22925  * @cfg {Array} btns List of additional buttons.
22926  * 
22927  * 
22928  * NEEDS Extra CSS? 
22929  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22930  */
22931  
22932 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22933 {
22934     
22935     Roo.apply(this, config);
22936     
22937     // default disabled, based on 'good practice'..
22938     this.disable = this.disable || {};
22939     Roo.applyIf(this.disable, {
22940         fontSize : true,
22941         colors : true,
22942         specialElements : true
22943     });
22944     
22945     
22946     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22947     // dont call parent... till later.
22948 }
22949
22950 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22951     
22952     tb: false,
22953     
22954     rendered: false,
22955     
22956     editor : false,
22957     editorcore : false,
22958     /**
22959      * @cfg {Object} disable  List of toolbar elements to disable
22960          
22961      */
22962     disable : false,
22963     
22964     
22965      /**
22966      * @cfg {String} createLinkText The default text for the create link prompt
22967      */
22968     createLinkText : 'Please enter the URL for the link:',
22969     /**
22970      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22971      */
22972     defaultLinkValue : 'http:/'+'/',
22973    
22974     
22975       /**
22976      * @cfg {Array} fontFamilies An array of available font families
22977      */
22978     fontFamilies : [
22979         'Arial',
22980         'Courier New',
22981         'Tahoma',
22982         'Times New Roman',
22983         'Verdana'
22984     ],
22985     
22986     specialChars : [
22987            "&#169;",
22988           "&#174;",     
22989           "&#8482;",    
22990           "&#163;" ,    
22991          // "&#8212;",    
22992           "&#8230;",    
22993           "&#247;" ,    
22994         //  "&#225;" ,     ?? a acute?
22995            "&#8364;"    , //Euro
22996        //   "&#8220;"    ,
22997         //  "&#8221;"    ,
22998         //  "&#8226;"    ,
22999           "&#176;"  //   , // degrees
23000
23001          // "&#233;"     , // e ecute
23002          // "&#250;"     , // u ecute?
23003     ],
23004     
23005     specialElements : [
23006         {
23007             text: "Insert Table",
23008             xtype: 'MenuItem',
23009             xns : Roo.Menu,
23010             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23011                 
23012         },
23013         {    
23014             text: "Insert Image",
23015             xtype: 'MenuItem',
23016             xns : Roo.Menu,
23017             ihtml : '<img src="about:blank"/>'
23018             
23019         }
23020         
23021          
23022     ],
23023     
23024     
23025     inputElements : [ 
23026             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23027             "input:submit", "input:button", "select", "textarea", "label" ],
23028     formats : [
23029         ["p"] ,  
23030         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23031         ["pre"],[ "code"], 
23032         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23033         ['div'],['span'],
23034         ['sup'],['sub']
23035     ],
23036     
23037     cleanStyles : [
23038         "font-size"
23039     ],
23040      /**
23041      * @cfg {String} defaultFont default font to use.
23042      */
23043     defaultFont: 'tahoma',
23044    
23045     fontSelect : false,
23046     
23047     
23048     formatCombo : false,
23049     
23050     init : function(editor)
23051     {
23052         this.editor = editor;
23053         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23054         var editorcore = this.editorcore;
23055         
23056         var _t = this;
23057         
23058         var fid = editorcore.frameId;
23059         var etb = this;
23060         function btn(id, toggle, handler){
23061             var xid = fid + '-'+ id ;
23062             return {
23063                 id : xid,
23064                 cmd : id,
23065                 cls : 'x-btn-icon x-edit-'+id,
23066                 enableToggle:toggle !== false,
23067                 scope: _t, // was editor...
23068                 handler:handler||_t.relayBtnCmd,
23069                 clickEvent:'mousedown',
23070                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23071                 tabIndex:-1
23072             };
23073         }
23074         
23075         
23076         
23077         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23078         this.tb = tb;
23079          // stop form submits
23080         tb.el.on('click', function(e){
23081             e.preventDefault(); // what does this do?
23082         });
23083
23084         if(!this.disable.font) { // && !Roo.isSafari){
23085             /* why no safari for fonts 
23086             editor.fontSelect = tb.el.createChild({
23087                 tag:'select',
23088                 tabIndex: -1,
23089                 cls:'x-font-select',
23090                 html: this.createFontOptions()
23091             });
23092             
23093             editor.fontSelect.on('change', function(){
23094                 var font = editor.fontSelect.dom.value;
23095                 editor.relayCmd('fontname', font);
23096                 editor.deferFocus();
23097             }, editor);
23098             
23099             tb.add(
23100                 editor.fontSelect.dom,
23101                 '-'
23102             );
23103             */
23104             
23105         };
23106         if(!this.disable.formats){
23107             this.formatCombo = new Roo.form.ComboBox({
23108                 store: new Roo.data.SimpleStore({
23109                     id : 'tag',
23110                     fields: ['tag'],
23111                     data : this.formats // from states.js
23112                 }),
23113                 blockFocus : true,
23114                 name : '',
23115                 //autoCreate : {tag: "div",  size: "20"},
23116                 displayField:'tag',
23117                 typeAhead: false,
23118                 mode: 'local',
23119                 editable : false,
23120                 triggerAction: 'all',
23121                 emptyText:'Add tag',
23122                 selectOnFocus:true,
23123                 width:135,
23124                 listeners : {
23125                     'select': function(c, r, i) {
23126                         editorcore.insertTag(r.get('tag'));
23127                         editor.focus();
23128                     }
23129                 }
23130
23131             });
23132             tb.addField(this.formatCombo);
23133             
23134         }
23135         
23136         if(!this.disable.format){
23137             tb.add(
23138                 btn('bold'),
23139                 btn('italic'),
23140                 btn('underline'),
23141                 btn('strikethrough')
23142             );
23143         };
23144         if(!this.disable.fontSize){
23145             tb.add(
23146                 '-',
23147                 
23148                 
23149                 btn('increasefontsize', false, editorcore.adjustFont),
23150                 btn('decreasefontsize', false, editorcore.adjustFont)
23151             );
23152         };
23153         
23154         
23155         if(!this.disable.colors){
23156             tb.add(
23157                 '-', {
23158                     id:editorcore.frameId +'-forecolor',
23159                     cls:'x-btn-icon x-edit-forecolor',
23160                     clickEvent:'mousedown',
23161                     tooltip: this.buttonTips['forecolor'] || undefined,
23162                     tabIndex:-1,
23163                     menu : new Roo.menu.ColorMenu({
23164                         allowReselect: true,
23165                         focus: Roo.emptyFn,
23166                         value:'000000',
23167                         plain:true,
23168                         selectHandler: function(cp, color){
23169                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23170                             editor.deferFocus();
23171                         },
23172                         scope: editorcore,
23173                         clickEvent:'mousedown'
23174                     })
23175                 }, {
23176                     id:editorcore.frameId +'backcolor',
23177                     cls:'x-btn-icon x-edit-backcolor',
23178                     clickEvent:'mousedown',
23179                     tooltip: this.buttonTips['backcolor'] || undefined,
23180                     tabIndex:-1,
23181                     menu : new Roo.menu.ColorMenu({
23182                         focus: Roo.emptyFn,
23183                         value:'FFFFFF',
23184                         plain:true,
23185                         allowReselect: true,
23186                         selectHandler: function(cp, color){
23187                             if(Roo.isGecko){
23188                                 editorcore.execCmd('useCSS', false);
23189                                 editorcore.execCmd('hilitecolor', color);
23190                                 editorcore.execCmd('useCSS', true);
23191                                 editor.deferFocus();
23192                             }else{
23193                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23194                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23195                                 editor.deferFocus();
23196                             }
23197                         },
23198                         scope:editorcore,
23199                         clickEvent:'mousedown'
23200                     })
23201                 }
23202             );
23203         };
23204         // now add all the items...
23205         
23206
23207         if(!this.disable.alignments){
23208             tb.add(
23209                 '-',
23210                 btn('justifyleft'),
23211                 btn('justifycenter'),
23212                 btn('justifyright')
23213             );
23214         };
23215
23216         //if(!Roo.isSafari){
23217             if(!this.disable.links){
23218                 tb.add(
23219                     '-',
23220                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23221                 );
23222             };
23223
23224             if(!this.disable.lists){
23225                 tb.add(
23226                     '-',
23227                     btn('insertorderedlist'),
23228                     btn('insertunorderedlist')
23229                 );
23230             }
23231             if(!this.disable.sourceEdit){
23232                 tb.add(
23233                     '-',
23234                     btn('sourceedit', true, function(btn){
23235                         this.toggleSourceEdit(btn.pressed);
23236                     })
23237                 );
23238             }
23239         //}
23240         
23241         var smenu = { };
23242         // special menu.. - needs to be tidied up..
23243         if (!this.disable.special) {
23244             smenu = {
23245                 text: "&#169;",
23246                 cls: 'x-edit-none',
23247                 
23248                 menu : {
23249                     items : []
23250                 }
23251             };
23252             for (var i =0; i < this.specialChars.length; i++) {
23253                 smenu.menu.items.push({
23254                     
23255                     html: this.specialChars[i],
23256                     handler: function(a,b) {
23257                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23258                         //editor.insertAtCursor(a.html);
23259                         
23260                     },
23261                     tabIndex:-1
23262                 });
23263             }
23264             
23265             
23266             tb.add(smenu);
23267             
23268             
23269         }
23270         
23271         var cmenu = { };
23272         if (!this.disable.cleanStyles) {
23273             cmenu = {
23274                 cls: 'x-btn-icon x-btn-clear',
23275                 
23276                 menu : {
23277                     items : []
23278                 }
23279             };
23280             for (var i =0; i < this.cleanStyles.length; i++) {
23281                 cmenu.menu.items.push({
23282                     actiontype : this.cleanStyles[i],
23283                     html: 'Remove ' + this.cleanStyles[i],
23284                     handler: function(a,b) {
23285 //                        Roo.log(a);
23286 //                        Roo.log(b);
23287                         var c = Roo.get(editorcore.doc.body);
23288                         c.select('[style]').each(function(s) {
23289                             s.dom.style.removeProperty(a.actiontype);
23290                         });
23291                         editorcore.syncValue();
23292                     },
23293                     tabIndex:-1
23294                 });
23295             }
23296              cmenu.menu.items.push({
23297                 actiontype : 'tablewidths',
23298                 html: 'Remove Table Widths',
23299                 handler: function(a,b) {
23300                     editorcore.cleanTableWidths();
23301                     editorcore.syncValue();
23302                 },
23303                 tabIndex:-1
23304             });
23305             cmenu.menu.items.push({
23306                 actiontype : 'word',
23307                 html: 'Remove MS Word Formating',
23308                 handler: function(a,b) {
23309                     editorcore.cleanWord();
23310                     editorcore.syncValue();
23311                 },
23312                 tabIndex:-1
23313             });
23314             
23315             cmenu.menu.items.push({
23316                 actiontype : 'all',
23317                 html: 'Remove All Styles',
23318                 handler: function(a,b) {
23319                     
23320                     var c = Roo.get(editorcore.doc.body);
23321                     c.select('[style]').each(function(s) {
23322                         s.dom.removeAttribute('style');
23323                     });
23324                     editorcore.syncValue();
23325                 },
23326                 tabIndex:-1
23327             });
23328             
23329             cmenu.menu.items.push({
23330                 actiontype : 'all',
23331                 html: 'Remove All CSS Classes',
23332                 handler: function(a,b) {
23333                     
23334                     var c = Roo.get(editorcore.doc.body);
23335                     c.select('[class]').each(function(s) {
23336                         s.dom.removeAttribute('class');
23337                     });
23338                     editorcore.cleanWord();
23339                     editorcore.syncValue();
23340                 },
23341                 tabIndex:-1
23342             });
23343             
23344              cmenu.menu.items.push({
23345                 actiontype : 'tidy',
23346                 html: 'Tidy HTML Source',
23347                 handler: function(a,b) {
23348                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23349                     editorcore.syncValue();
23350                 },
23351                 tabIndex:-1
23352             });
23353             
23354             
23355             tb.add(cmenu);
23356         }
23357          
23358         if (!this.disable.specialElements) {
23359             var semenu = {
23360                 text: "Other;",
23361                 cls: 'x-edit-none',
23362                 menu : {
23363                     items : []
23364                 }
23365             };
23366             for (var i =0; i < this.specialElements.length; i++) {
23367                 semenu.menu.items.push(
23368                     Roo.apply({ 
23369                         handler: function(a,b) {
23370                             editor.insertAtCursor(this.ihtml);
23371                         }
23372                     }, this.specialElements[i])
23373                 );
23374                     
23375             }
23376             
23377             tb.add(semenu);
23378             
23379             
23380         }
23381          
23382         
23383         if (this.btns) {
23384             for(var i =0; i< this.btns.length;i++) {
23385                 var b = Roo.factory(this.btns[i],Roo.form);
23386                 b.cls =  'x-edit-none';
23387                 
23388                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23389                     b.cls += ' x-init-enable';
23390                 }
23391                 
23392                 b.scope = editorcore;
23393                 tb.add(b);
23394             }
23395         
23396         }
23397         
23398         
23399         
23400         // disable everything...
23401         
23402         this.tb.items.each(function(item){
23403             
23404            if(
23405                 item.id != editorcore.frameId+ '-sourceedit' && 
23406                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23407             ){
23408                 
23409                 item.disable();
23410             }
23411         });
23412         this.rendered = true;
23413         
23414         // the all the btns;
23415         editor.on('editorevent', this.updateToolbar, this);
23416         // other toolbars need to implement this..
23417         //editor.on('editmodechange', this.updateToolbar, this);
23418     },
23419     
23420     
23421     relayBtnCmd : function(btn) {
23422         this.editorcore.relayCmd(btn.cmd);
23423     },
23424     // private used internally
23425     createLink : function(){
23426         Roo.log("create link?");
23427         var url = prompt(this.createLinkText, this.defaultLinkValue);
23428         if(url && url != 'http:/'+'/'){
23429             this.editorcore.relayCmd('createlink', url);
23430         }
23431     },
23432
23433     
23434     /**
23435      * Protected method that will not generally be called directly. It triggers
23436      * a toolbar update by reading the markup state of the current selection in the editor.
23437      */
23438     updateToolbar: function(){
23439
23440         if(!this.editorcore.activated){
23441             this.editor.onFirstFocus();
23442             return;
23443         }
23444
23445         var btns = this.tb.items.map, 
23446             doc = this.editorcore.doc,
23447             frameId = this.editorcore.frameId;
23448
23449         if(!this.disable.font && !Roo.isSafari){
23450             /*
23451             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23452             if(name != this.fontSelect.dom.value){
23453                 this.fontSelect.dom.value = name;
23454             }
23455             */
23456         }
23457         if(!this.disable.format){
23458             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23459             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23460             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23461             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23462         }
23463         if(!this.disable.alignments){
23464             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23465             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23466             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23467         }
23468         if(!Roo.isSafari && !this.disable.lists){
23469             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23470             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23471         }
23472         
23473         var ans = this.editorcore.getAllAncestors();
23474         if (this.formatCombo) {
23475             
23476             
23477             var store = this.formatCombo.store;
23478             this.formatCombo.setValue("");
23479             for (var i =0; i < ans.length;i++) {
23480                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23481                     // select it..
23482                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23483                     break;
23484                 }
23485             }
23486         }
23487         
23488         
23489         
23490         // hides menus... - so this cant be on a menu...
23491         Roo.menu.MenuMgr.hideAll();
23492
23493         //this.editorsyncValue();
23494     },
23495    
23496     
23497     createFontOptions : function(){
23498         var buf = [], fs = this.fontFamilies, ff, lc;
23499         
23500         
23501         
23502         for(var i = 0, len = fs.length; i< len; i++){
23503             ff = fs[i];
23504             lc = ff.toLowerCase();
23505             buf.push(
23506                 '<option value="',lc,'" style="font-family:',ff,';"',
23507                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23508                     ff,
23509                 '</option>'
23510             );
23511         }
23512         return buf.join('');
23513     },
23514     
23515     toggleSourceEdit : function(sourceEditMode){
23516         
23517         Roo.log("toolbar toogle");
23518         if(sourceEditMode === undefined){
23519             sourceEditMode = !this.sourceEditMode;
23520         }
23521         this.sourceEditMode = sourceEditMode === true;
23522         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23523         // just toggle the button?
23524         if(btn.pressed !== this.sourceEditMode){
23525             btn.toggle(this.sourceEditMode);
23526             return;
23527         }
23528         
23529         if(sourceEditMode){
23530             Roo.log("disabling buttons");
23531             this.tb.items.each(function(item){
23532                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23533                     item.disable();
23534                 }
23535             });
23536           
23537         }else{
23538             Roo.log("enabling buttons");
23539             if(this.editorcore.initialized){
23540                 this.tb.items.each(function(item){
23541                     item.enable();
23542                 });
23543             }
23544             
23545         }
23546         Roo.log("calling toggole on editor");
23547         // tell the editor that it's been pressed..
23548         this.editor.toggleSourceEdit(sourceEditMode);
23549        
23550     },
23551      /**
23552      * Object collection of toolbar tooltips for the buttons in the editor. The key
23553      * is the command id associated with that button and the value is a valid QuickTips object.
23554      * For example:
23555 <pre><code>
23556 {
23557     bold : {
23558         title: 'Bold (Ctrl+B)',
23559         text: 'Make the selected text bold.',
23560         cls: 'x-html-editor-tip'
23561     },
23562     italic : {
23563         title: 'Italic (Ctrl+I)',
23564         text: 'Make the selected text italic.',
23565         cls: 'x-html-editor-tip'
23566     },
23567     ...
23568 </code></pre>
23569     * @type Object
23570      */
23571     buttonTips : {
23572         bold : {
23573             title: 'Bold (Ctrl+B)',
23574             text: 'Make the selected text bold.',
23575             cls: 'x-html-editor-tip'
23576         },
23577         italic : {
23578             title: 'Italic (Ctrl+I)',
23579             text: 'Make the selected text italic.',
23580             cls: 'x-html-editor-tip'
23581         },
23582         underline : {
23583             title: 'Underline (Ctrl+U)',
23584             text: 'Underline the selected text.',
23585             cls: 'x-html-editor-tip'
23586         },
23587         strikethrough : {
23588             title: 'Strikethrough',
23589             text: 'Strikethrough the selected text.',
23590             cls: 'x-html-editor-tip'
23591         },
23592         increasefontsize : {
23593             title: 'Grow Text',
23594             text: 'Increase the font size.',
23595             cls: 'x-html-editor-tip'
23596         },
23597         decreasefontsize : {
23598             title: 'Shrink Text',
23599             text: 'Decrease the font size.',
23600             cls: 'x-html-editor-tip'
23601         },
23602         backcolor : {
23603             title: 'Text Highlight Color',
23604             text: 'Change the background color of the selected text.',
23605             cls: 'x-html-editor-tip'
23606         },
23607         forecolor : {
23608             title: 'Font Color',
23609             text: 'Change the color of the selected text.',
23610             cls: 'x-html-editor-tip'
23611         },
23612         justifyleft : {
23613             title: 'Align Text Left',
23614             text: 'Align text to the left.',
23615             cls: 'x-html-editor-tip'
23616         },
23617         justifycenter : {
23618             title: 'Center Text',
23619             text: 'Center text in the editor.',
23620             cls: 'x-html-editor-tip'
23621         },
23622         justifyright : {
23623             title: 'Align Text Right',
23624             text: 'Align text to the right.',
23625             cls: 'x-html-editor-tip'
23626         },
23627         insertunorderedlist : {
23628             title: 'Bullet List',
23629             text: 'Start a bulleted list.',
23630             cls: 'x-html-editor-tip'
23631         },
23632         insertorderedlist : {
23633             title: 'Numbered List',
23634             text: 'Start a numbered list.',
23635             cls: 'x-html-editor-tip'
23636         },
23637         createlink : {
23638             title: 'Hyperlink',
23639             text: 'Make the selected text a hyperlink.',
23640             cls: 'x-html-editor-tip'
23641         },
23642         sourceedit : {
23643             title: 'Source Edit',
23644             text: 'Switch to source editing mode.',
23645             cls: 'x-html-editor-tip'
23646         }
23647     },
23648     // private
23649     onDestroy : function(){
23650         if(this.rendered){
23651             
23652             this.tb.items.each(function(item){
23653                 if(item.menu){
23654                     item.menu.removeAll();
23655                     if(item.menu.el){
23656                         item.menu.el.destroy();
23657                     }
23658                 }
23659                 item.destroy();
23660             });
23661              
23662         }
23663     },
23664     onFirstFocus: function() {
23665         this.tb.items.each(function(item){
23666            item.enable();
23667         });
23668     }
23669 });
23670
23671
23672
23673
23674 // <script type="text/javascript">
23675 /*
23676  * Based on
23677  * Ext JS Library 1.1.1
23678  * Copyright(c) 2006-2007, Ext JS, LLC.
23679  *  
23680  
23681  */
23682
23683  
23684 /**
23685  * @class Roo.form.HtmlEditor.ToolbarContext
23686  * Context Toolbar
23687  * 
23688  * Usage:
23689  *
23690  new Roo.form.HtmlEditor({
23691     ....
23692     toolbars : [
23693         { xtype: 'ToolbarStandard', styles : {} }
23694         { xtype: 'ToolbarContext', disable : {} }
23695     ]
23696 })
23697
23698      
23699  * 
23700  * @config : {Object} disable List of elements to disable.. (not done yet.)
23701  * @config : {Object} styles  Map of styles available.
23702  * 
23703  */
23704
23705 Roo.form.HtmlEditor.ToolbarContext = function(config)
23706 {
23707     
23708     Roo.apply(this, config);
23709     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23710     // dont call parent... till later.
23711     this.styles = this.styles || {};
23712 }
23713
23714  
23715
23716 Roo.form.HtmlEditor.ToolbarContext.types = {
23717     'IMG' : {
23718         width : {
23719             title: "Width",
23720             width: 40
23721         },
23722         height:  {
23723             title: "Height",
23724             width: 40
23725         },
23726         align: {
23727             title: "Align",
23728             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23729             width : 80
23730             
23731         },
23732         border: {
23733             title: "Border",
23734             width: 40
23735         },
23736         alt: {
23737             title: "Alt",
23738             width: 120
23739         },
23740         src : {
23741             title: "Src",
23742             width: 220
23743         }
23744         
23745     },
23746     'A' : {
23747         name : {
23748             title: "Name",
23749             width: 50
23750         },
23751         target:  {
23752             title: "Target",
23753             width: 120
23754         },
23755         href:  {
23756             title: "Href",
23757             width: 220
23758         } // border?
23759         
23760     },
23761     'TABLE' : {
23762         rows : {
23763             title: "Rows",
23764             width: 20
23765         },
23766         cols : {
23767             title: "Cols",
23768             width: 20
23769         },
23770         width : {
23771             title: "Width",
23772             width: 40
23773         },
23774         height : {
23775             title: "Height",
23776             width: 40
23777         },
23778         border : {
23779             title: "Border",
23780             width: 20
23781         }
23782     },
23783     'TD' : {
23784         width : {
23785             title: "Width",
23786             width: 40
23787         },
23788         height : {
23789             title: "Height",
23790             width: 40
23791         },   
23792         align: {
23793             title: "Align",
23794             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23795             width: 80
23796         },
23797         valign: {
23798             title: "Valign",
23799             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23800             width: 80
23801         },
23802         colspan: {
23803             title: "Colspan",
23804             width: 20
23805             
23806         },
23807          'font-family'  : {
23808             title : "Font",
23809             style : 'fontFamily',
23810             displayField: 'display',
23811             optname : 'font-family',
23812             width: 140
23813         }
23814     },
23815     'INPUT' : {
23816         name : {
23817             title: "name",
23818             width: 120
23819         },
23820         value : {
23821             title: "Value",
23822             width: 120
23823         },
23824         width : {
23825             title: "Width",
23826             width: 40
23827         }
23828     },
23829     'LABEL' : {
23830         'for' : {
23831             title: "For",
23832             width: 120
23833         }
23834     },
23835     'TEXTAREA' : {
23836           name : {
23837             title: "name",
23838             width: 120
23839         },
23840         rows : {
23841             title: "Rows",
23842             width: 20
23843         },
23844         cols : {
23845             title: "Cols",
23846             width: 20
23847         }
23848     },
23849     'SELECT' : {
23850         name : {
23851             title: "name",
23852             width: 120
23853         },
23854         selectoptions : {
23855             title: "Options",
23856             width: 200
23857         }
23858     },
23859     
23860     // should we really allow this??
23861     // should this just be 
23862     'BODY' : {
23863         title : {
23864             title: "Title",
23865             width: 200,
23866             disabled : true
23867         }
23868     },
23869     'SPAN' : {
23870         'font-family'  : {
23871             title : "Font",
23872             style : 'fontFamily',
23873             displayField: 'display',
23874             optname : 'font-family',
23875             width: 140
23876         }
23877     },
23878     'DIV' : {
23879         'font-family'  : {
23880             title : "Font",
23881             style : 'fontFamily',
23882             displayField: 'display',
23883             optname : 'font-family',
23884             width: 140
23885         }
23886     },
23887      'P' : {
23888         'font-family'  : {
23889             title : "Font",
23890             style : 'fontFamily',
23891             displayField: 'display',
23892             optname : 'font-family',
23893             width: 140
23894         }
23895     },
23896     
23897     '*' : {
23898         // empty..
23899     }
23900
23901 };
23902
23903 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23904 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23905
23906 Roo.form.HtmlEditor.ToolbarContext.options = {
23907         'font-family'  : [ 
23908                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23909                 [ 'Courier New', 'Courier New'],
23910                 [ 'Tahoma', 'Tahoma'],
23911                 [ 'Times New Roman,serif', 'Times'],
23912                 [ 'Verdana','Verdana' ]
23913         ]
23914 };
23915
23916 // fixme - these need to be configurable..
23917  
23918
23919 //Roo.form.HtmlEditor.ToolbarContext.types
23920
23921
23922 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23923     
23924     tb: false,
23925     
23926     rendered: false,
23927     
23928     editor : false,
23929     editorcore : false,
23930     /**
23931      * @cfg {Object} disable  List of toolbar elements to disable
23932          
23933      */
23934     disable : false,
23935     /**
23936      * @cfg {Object} styles List of styles 
23937      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23938      *
23939      * These must be defined in the page, so they get rendered correctly..
23940      * .headline { }
23941      * TD.underline { }
23942      * 
23943      */
23944     styles : false,
23945     
23946     options: false,
23947     
23948     toolbars : false,
23949     
23950     init : function(editor)
23951     {
23952         this.editor = editor;
23953         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23954         var editorcore = this.editorcore;
23955         
23956         var fid = editorcore.frameId;
23957         var etb = this;
23958         function btn(id, toggle, handler){
23959             var xid = fid + '-'+ id ;
23960             return {
23961                 id : xid,
23962                 cmd : id,
23963                 cls : 'x-btn-icon x-edit-'+id,
23964                 enableToggle:toggle !== false,
23965                 scope: editorcore, // was editor...
23966                 handler:handler||editorcore.relayBtnCmd,
23967                 clickEvent:'mousedown',
23968                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23969                 tabIndex:-1
23970             };
23971         }
23972         // create a new element.
23973         var wdiv = editor.wrap.createChild({
23974                 tag: 'div'
23975             }, editor.wrap.dom.firstChild.nextSibling, true);
23976         
23977         // can we do this more than once??
23978         
23979          // stop form submits
23980       
23981  
23982         // disable everything...
23983         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23984         this.toolbars = {};
23985            
23986         for (var i in  ty) {
23987           
23988             this.toolbars[i] = this.buildToolbar(ty[i],i);
23989         }
23990         this.tb = this.toolbars.BODY;
23991         this.tb.el.show();
23992         this.buildFooter();
23993         this.footer.show();
23994         editor.on('hide', function( ) { this.footer.hide() }, this);
23995         editor.on('show', function( ) { this.footer.show() }, this);
23996         
23997          
23998         this.rendered = true;
23999         
24000         // the all the btns;
24001         editor.on('editorevent', this.updateToolbar, this);
24002         // other toolbars need to implement this..
24003         //editor.on('editmodechange', this.updateToolbar, this);
24004     },
24005     
24006     
24007     
24008     /**
24009      * Protected method that will not generally be called directly. It triggers
24010      * a toolbar update by reading the markup state of the current selection in the editor.
24011      *
24012      * Note you can force an update by calling on('editorevent', scope, false)
24013      */
24014     updateToolbar: function(editor,ev,sel){
24015
24016         //Roo.log(ev);
24017         // capture mouse up - this is handy for selecting images..
24018         // perhaps should go somewhere else...
24019         if(!this.editorcore.activated){
24020              this.editor.onFirstFocus();
24021             return;
24022         }
24023         
24024         
24025         
24026         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24027         // selectNode - might want to handle IE?
24028         if (ev &&
24029             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24030             ev.target && ev.target.tagName == 'IMG') {
24031             // they have click on an image...
24032             // let's see if we can change the selection...
24033             sel = ev.target;
24034          
24035               var nodeRange = sel.ownerDocument.createRange();
24036             try {
24037                 nodeRange.selectNode(sel);
24038             } catch (e) {
24039                 nodeRange.selectNodeContents(sel);
24040             }
24041             //nodeRange.collapse(true);
24042             var s = this.editorcore.win.getSelection();
24043             s.removeAllRanges();
24044             s.addRange(nodeRange);
24045         }  
24046         
24047       
24048         var updateFooter = sel ? false : true;
24049         
24050         
24051         var ans = this.editorcore.getAllAncestors();
24052         
24053         // pick
24054         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24055         
24056         if (!sel) { 
24057             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24058             sel = sel ? sel : this.editorcore.doc.body;
24059             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24060             
24061         }
24062         // pick a menu that exists..
24063         var tn = sel.tagName.toUpperCase();
24064         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24065         
24066         tn = sel.tagName.toUpperCase();
24067         
24068         var lastSel = this.tb.selectedNode;
24069         
24070         this.tb.selectedNode = sel;
24071         
24072         // if current menu does not match..
24073         
24074         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24075                 
24076             this.tb.el.hide();
24077             ///console.log("show: " + tn);
24078             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24079             this.tb.el.show();
24080             // update name
24081             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24082             
24083             
24084             // update attributes
24085             if (this.tb.fields) {
24086                 this.tb.fields.each(function(e) {
24087                     if (e.stylename) {
24088                         e.setValue(sel.style[e.stylename]);
24089                         return;
24090                     } 
24091                    e.setValue(sel.getAttribute(e.attrname));
24092                 });
24093             }
24094             
24095             var hasStyles = false;
24096             for(var i in this.styles) {
24097                 hasStyles = true;
24098                 break;
24099             }
24100             
24101             // update styles
24102             if (hasStyles) { 
24103                 var st = this.tb.fields.item(0);
24104                 
24105                 st.store.removeAll();
24106                
24107                 
24108                 var cn = sel.className.split(/\s+/);
24109                 
24110                 var avs = [];
24111                 if (this.styles['*']) {
24112                     
24113                     Roo.each(this.styles['*'], function(v) {
24114                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24115                     });
24116                 }
24117                 if (this.styles[tn]) { 
24118                     Roo.each(this.styles[tn], function(v) {
24119                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24120                     });
24121                 }
24122                 
24123                 st.store.loadData(avs);
24124                 st.collapse();
24125                 st.setValue(cn);
24126             }
24127             // flag our selected Node.
24128             this.tb.selectedNode = sel;
24129            
24130            
24131             Roo.menu.MenuMgr.hideAll();
24132
24133         }
24134         
24135         if (!updateFooter) {
24136             //this.footDisp.dom.innerHTML = ''; 
24137             return;
24138         }
24139         // update the footer
24140         //
24141         var html = '';
24142         
24143         this.footerEls = ans.reverse();
24144         Roo.each(this.footerEls, function(a,i) {
24145             if (!a) { return; }
24146             html += html.length ? ' &gt; '  :  '';
24147             
24148             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24149             
24150         });
24151        
24152         // 
24153         var sz = this.footDisp.up('td').getSize();
24154         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24155         this.footDisp.dom.style.marginLeft = '5px';
24156         
24157         this.footDisp.dom.style.overflow = 'hidden';
24158         
24159         this.footDisp.dom.innerHTML = html;
24160             
24161         //this.editorsyncValue();
24162     },
24163      
24164     
24165    
24166        
24167     // private
24168     onDestroy : function(){
24169         if(this.rendered){
24170             
24171             this.tb.items.each(function(item){
24172                 if(item.menu){
24173                     item.menu.removeAll();
24174                     if(item.menu.el){
24175                         item.menu.el.destroy();
24176                     }
24177                 }
24178                 item.destroy();
24179             });
24180              
24181         }
24182     },
24183     onFirstFocus: function() {
24184         // need to do this for all the toolbars..
24185         this.tb.items.each(function(item){
24186            item.enable();
24187         });
24188     },
24189     buildToolbar: function(tlist, nm)
24190     {
24191         var editor = this.editor;
24192         var editorcore = this.editorcore;
24193          // create a new element.
24194         var wdiv = editor.wrap.createChild({
24195                 tag: 'div'
24196             }, editor.wrap.dom.firstChild.nextSibling, true);
24197         
24198        
24199         var tb = new Roo.Toolbar(wdiv);
24200         // add the name..
24201         
24202         tb.add(nm+ ":&nbsp;");
24203         
24204         var styles = [];
24205         for(var i in this.styles) {
24206             styles.push(i);
24207         }
24208         
24209         // styles...
24210         if (styles && styles.length) {
24211             
24212             // this needs a multi-select checkbox...
24213             tb.addField( new Roo.form.ComboBox({
24214                 store: new Roo.data.SimpleStore({
24215                     id : 'val',
24216                     fields: ['val', 'selected'],
24217                     data : [] 
24218                 }),
24219                 name : '-roo-edit-className',
24220                 attrname : 'className',
24221                 displayField: 'val',
24222                 typeAhead: false,
24223                 mode: 'local',
24224                 editable : false,
24225                 triggerAction: 'all',
24226                 emptyText:'Select Style',
24227                 selectOnFocus:true,
24228                 width: 130,
24229                 listeners : {
24230                     'select': function(c, r, i) {
24231                         // initial support only for on class per el..
24232                         tb.selectedNode.className =  r ? r.get('val') : '';
24233                         editorcore.syncValue();
24234                     }
24235                 }
24236     
24237             }));
24238         }
24239         
24240         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24241         var tbops = tbc.options;
24242         
24243         for (var i in tlist) {
24244             
24245             var item = tlist[i];
24246             tb.add(item.title + ":&nbsp;");
24247             
24248             
24249             //optname == used so you can configure the options available..
24250             var opts = item.opts ? item.opts : false;
24251             if (item.optname) {
24252                 opts = tbops[item.optname];
24253            
24254             }
24255             
24256             if (opts) {
24257                 // opts == pulldown..
24258                 tb.addField( new Roo.form.ComboBox({
24259                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24260                         id : 'val',
24261                         fields: ['val', 'display'],
24262                         data : opts  
24263                     }),
24264                     name : '-roo-edit-' + i,
24265                     attrname : i,
24266                     stylename : item.style ? item.style : false,
24267                     displayField: item.displayField ? item.displayField : 'val',
24268                     valueField :  'val',
24269                     typeAhead: false,
24270                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24271                     editable : false,
24272                     triggerAction: 'all',
24273                     emptyText:'Select',
24274                     selectOnFocus:true,
24275                     width: item.width ? item.width  : 130,
24276                     listeners : {
24277                         'select': function(c, r, i) {
24278                             if (c.stylename) {
24279                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24280                                 return;
24281                             }
24282                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24283                         }
24284                     }
24285
24286                 }));
24287                 continue;
24288                     
24289                  
24290                 
24291                 tb.addField( new Roo.form.TextField({
24292                     name: i,
24293                     width: 100,
24294                     //allowBlank:false,
24295                     value: ''
24296                 }));
24297                 continue;
24298             }
24299             tb.addField( new Roo.form.TextField({
24300                 name: '-roo-edit-' + i,
24301                 attrname : i,
24302                 
24303                 width: item.width,
24304                 //allowBlank:true,
24305                 value: '',
24306                 listeners: {
24307                     'change' : function(f, nv, ov) {
24308                         tb.selectedNode.setAttribute(f.attrname, nv);
24309                         editorcore.syncValue();
24310                     }
24311                 }
24312             }));
24313              
24314         }
24315         
24316         var _this = this;
24317         
24318         if(nm == 'BODY'){
24319             tb.addSeparator();
24320         
24321             tb.addButton( {
24322                 text: 'Stylesheets',
24323
24324                 listeners : {
24325                     click : function ()
24326                     {
24327                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24328                     }
24329                 }
24330             });
24331         }
24332         
24333         tb.addFill();
24334         tb.addButton( {
24335             text: 'Remove Tag',
24336     
24337             listeners : {
24338                 click : function ()
24339                 {
24340                     // remove
24341                     // undo does not work.
24342                      
24343                     var sn = tb.selectedNode;
24344                     
24345                     var pn = sn.parentNode;
24346                     
24347                     var stn =  sn.childNodes[0];
24348                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24349                     while (sn.childNodes.length) {
24350                         var node = sn.childNodes[0];
24351                         sn.removeChild(node);
24352                         //Roo.log(node);
24353                         pn.insertBefore(node, sn);
24354                         
24355                     }
24356                     pn.removeChild(sn);
24357                     var range = editorcore.createRange();
24358         
24359                     range.setStart(stn,0);
24360                     range.setEnd(en,0); //????
24361                     //range.selectNode(sel);
24362                     
24363                     
24364                     var selection = editorcore.getSelection();
24365                     selection.removeAllRanges();
24366                     selection.addRange(range);
24367                     
24368                     
24369                     
24370                     //_this.updateToolbar(null, null, pn);
24371                     _this.updateToolbar(null, null, null);
24372                     _this.footDisp.dom.innerHTML = ''; 
24373                 }
24374             }
24375             
24376                     
24377                 
24378             
24379         });
24380         
24381         
24382         tb.el.on('click', function(e){
24383             e.preventDefault(); // what does this do?
24384         });
24385         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24386         tb.el.hide();
24387         tb.name = nm;
24388         // dont need to disable them... as they will get hidden
24389         return tb;
24390          
24391         
24392     },
24393     buildFooter : function()
24394     {
24395         
24396         var fel = this.editor.wrap.createChild();
24397         this.footer = new Roo.Toolbar(fel);
24398         // toolbar has scrolly on left / right?
24399         var footDisp= new Roo.Toolbar.Fill();
24400         var _t = this;
24401         this.footer.add(
24402             {
24403                 text : '&lt;',
24404                 xtype: 'Button',
24405                 handler : function() {
24406                     _t.footDisp.scrollTo('left',0,true)
24407                 }
24408             }
24409         );
24410         this.footer.add( footDisp );
24411         this.footer.add( 
24412             {
24413                 text : '&gt;',
24414                 xtype: 'Button',
24415                 handler : function() {
24416                     // no animation..
24417                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24418                 }
24419             }
24420         );
24421         var fel = Roo.get(footDisp.el);
24422         fel.addClass('x-editor-context');
24423         this.footDispWrap = fel; 
24424         this.footDispWrap.overflow  = 'hidden';
24425         
24426         this.footDisp = fel.createChild();
24427         this.footDispWrap.on('click', this.onContextClick, this)
24428         
24429         
24430     },
24431     onContextClick : function (ev,dom)
24432     {
24433         ev.preventDefault();
24434         var  cn = dom.className;
24435         //Roo.log(cn);
24436         if (!cn.match(/x-ed-loc-/)) {
24437             return;
24438         }
24439         var n = cn.split('-').pop();
24440         var ans = this.footerEls;
24441         var sel = ans[n];
24442         
24443          // pick
24444         var range = this.editorcore.createRange();
24445         
24446         range.selectNodeContents(sel);
24447         //range.selectNode(sel);
24448         
24449         
24450         var selection = this.editorcore.getSelection();
24451         selection.removeAllRanges();
24452         selection.addRange(range);
24453         
24454         
24455         
24456         this.updateToolbar(null, null, sel);
24457         
24458         
24459     }
24460     
24461     
24462     
24463     
24464     
24465 });
24466
24467
24468
24469
24470
24471 /*
24472  * Based on:
24473  * Ext JS Library 1.1.1
24474  * Copyright(c) 2006-2007, Ext JS, LLC.
24475  *
24476  * Originally Released Under LGPL - original licence link has changed is not relivant.
24477  *
24478  * Fork - LGPL
24479  * <script type="text/javascript">
24480  */
24481  
24482 /**
24483  * @class Roo.form.BasicForm
24484  * @extends Roo.util.Observable
24485  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24486  * @constructor
24487  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24488  * @param {Object} config Configuration options
24489  */
24490 Roo.form.BasicForm = function(el, config){
24491     this.allItems = [];
24492     this.childForms = [];
24493     Roo.apply(this, config);
24494     /*
24495      * The Roo.form.Field items in this form.
24496      * @type MixedCollection
24497      */
24498      
24499      
24500     this.items = new Roo.util.MixedCollection(false, function(o){
24501         return o.id || (o.id = Roo.id());
24502     });
24503     this.addEvents({
24504         /**
24505          * @event beforeaction
24506          * Fires before any action is performed. Return false to cancel the action.
24507          * @param {Form} this
24508          * @param {Action} action The action to be performed
24509          */
24510         beforeaction: true,
24511         /**
24512          * @event actionfailed
24513          * Fires when an action fails.
24514          * @param {Form} this
24515          * @param {Action} action The action that failed
24516          */
24517         actionfailed : true,
24518         /**
24519          * @event actioncomplete
24520          * Fires when an action is completed.
24521          * @param {Form} this
24522          * @param {Action} action The action that completed
24523          */
24524         actioncomplete : true
24525     });
24526     if(el){
24527         this.initEl(el);
24528     }
24529     Roo.form.BasicForm.superclass.constructor.call(this);
24530     
24531     Roo.form.BasicForm.popover.apply();
24532 };
24533
24534 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24535     /**
24536      * @cfg {String} method
24537      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24538      */
24539     /**
24540      * @cfg {DataReader} reader
24541      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24542      * This is optional as there is built-in support for processing JSON.
24543      */
24544     /**
24545      * @cfg {DataReader} errorReader
24546      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24547      * This is completely optional as there is built-in support for processing JSON.
24548      */
24549     /**
24550      * @cfg {String} url
24551      * The URL to use for form actions if one isn't supplied in the action options.
24552      */
24553     /**
24554      * @cfg {Boolean} fileUpload
24555      * Set to true if this form is a file upload.
24556      */
24557      
24558     /**
24559      * @cfg {Object} baseParams
24560      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24561      */
24562      /**
24563      
24564     /**
24565      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24566      */
24567     timeout: 30,
24568
24569     // private
24570     activeAction : null,
24571
24572     /**
24573      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24574      * or setValues() data instead of when the form was first created.
24575      */
24576     trackResetOnLoad : false,
24577     
24578     
24579     /**
24580      * childForms - used for multi-tab forms
24581      * @type {Array}
24582      */
24583     childForms : false,
24584     
24585     /**
24586      * allItems - full list of fields.
24587      * @type {Array}
24588      */
24589     allItems : false,
24590     
24591     /**
24592      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24593      * element by passing it or its id or mask the form itself by passing in true.
24594      * @type Mixed
24595      */
24596     waitMsgTarget : false,
24597     
24598     /**
24599      * @type Boolean
24600      */
24601     disableMask : false,
24602     
24603     /**
24604      * @cfg {Boolean} errorMask (true|false) default false
24605      */
24606     errorMask : false,
24607     
24608     /**
24609      * @cfg {Number} maskOffset Default 100
24610      */
24611     maskOffset : 100,
24612
24613     // private
24614     initEl : function(el){
24615         this.el = Roo.get(el);
24616         this.id = this.el.id || Roo.id();
24617         this.el.on('submit', this.onSubmit, this);
24618         this.el.addClass('x-form');
24619     },
24620
24621     // private
24622     onSubmit : function(e){
24623         e.stopEvent();
24624     },
24625
24626     /**
24627      * Returns true if client-side validation on the form is successful.
24628      * @return Boolean
24629      */
24630     isValid : function(){
24631         var valid = true;
24632         var target = false;
24633         this.items.each(function(f){
24634             if(f.validate()){
24635                 return;
24636             }
24637             
24638             valid = false;
24639                 
24640             if(!target && f.el.isVisible(true)){
24641                 target = f;
24642             }
24643         });
24644         
24645         if(this.errorMask && !valid){
24646             Roo.form.BasicForm.popover.mask(this, target);
24647         }
24648         
24649         return valid;
24650     },
24651     /**
24652      * Returns array of invalid form fields.
24653      * @return Array
24654      */
24655     
24656     invalidFields : function()
24657     {
24658         var ret = [];
24659         this.items.each(function(f){
24660             if(f.validate()){
24661                 return;
24662             }
24663             ret.push(f);
24664             
24665         });
24666         
24667         return ret;
24668     },
24669     
24670     
24671     /**
24672      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24673      * @return Boolean
24674      */
24675     isDirty : function(){
24676         var dirty = false;
24677         this.items.each(function(f){
24678            if(f.isDirty()){
24679                dirty = true;
24680                return false;
24681            }
24682         });
24683         return dirty;
24684     },
24685     
24686     /**
24687      * Returns true if any fields in this form have changed since their original load. (New version)
24688      * @return Boolean
24689      */
24690     
24691     hasChanged : function()
24692     {
24693         var dirty = false;
24694         this.items.each(function(f){
24695            if(f.hasChanged()){
24696                dirty = true;
24697                return false;
24698            }
24699         });
24700         return dirty;
24701         
24702     },
24703     /**
24704      * Resets all hasChanged to 'false' -
24705      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24706      * So hasChanged storage is only to be used for this purpose
24707      * @return Boolean
24708      */
24709     resetHasChanged : function()
24710     {
24711         this.items.each(function(f){
24712            f.resetHasChanged();
24713         });
24714         
24715     },
24716     
24717     
24718     /**
24719      * Performs a predefined action (submit or load) or custom actions you define on this form.
24720      * @param {String} actionName The name of the action type
24721      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24722      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24723      * accept other config options):
24724      * <pre>
24725 Property          Type             Description
24726 ----------------  ---------------  ----------------------------------------------------------------------------------
24727 url               String           The url for the action (defaults to the form's url)
24728 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24729 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24730 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24731                                    validate the form on the client (defaults to false)
24732      * </pre>
24733      * @return {BasicForm} this
24734      */
24735     doAction : function(action, options){
24736         if(typeof action == 'string'){
24737             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24738         }
24739         if(this.fireEvent('beforeaction', this, action) !== false){
24740             this.beforeAction(action);
24741             action.run.defer(100, action);
24742         }
24743         return this;
24744     },
24745
24746     /**
24747      * Shortcut to do a submit action.
24748      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24749      * @return {BasicForm} this
24750      */
24751     submit : function(options){
24752         this.doAction('submit', options);
24753         return this;
24754     },
24755
24756     /**
24757      * Shortcut to do a load action.
24758      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24759      * @return {BasicForm} this
24760      */
24761     load : function(options){
24762         this.doAction('load', options);
24763         return this;
24764     },
24765
24766     /**
24767      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24768      * @param {Record} record The record to edit
24769      * @return {BasicForm} this
24770      */
24771     updateRecord : function(record){
24772         record.beginEdit();
24773         var fs = record.fields;
24774         fs.each(function(f){
24775             var field = this.findField(f.name);
24776             if(field){
24777                 record.set(f.name, field.getValue());
24778             }
24779         }, this);
24780         record.endEdit();
24781         return this;
24782     },
24783
24784     /**
24785      * Loads an Roo.data.Record into this form.
24786      * @param {Record} record The record to load
24787      * @return {BasicForm} this
24788      */
24789     loadRecord : function(record){
24790         this.setValues(record.data);
24791         return this;
24792     },
24793
24794     // private
24795     beforeAction : function(action){
24796         var o = action.options;
24797         
24798         if(!this.disableMask) {
24799             if(this.waitMsgTarget === true){
24800                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24801             }else if(this.waitMsgTarget){
24802                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24803                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24804             }else {
24805                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24806             }
24807         }
24808         
24809          
24810     },
24811
24812     // private
24813     afterAction : function(action, success){
24814         this.activeAction = null;
24815         var o = action.options;
24816         
24817         if(!this.disableMask) {
24818             if(this.waitMsgTarget === true){
24819                 this.el.unmask();
24820             }else if(this.waitMsgTarget){
24821                 this.waitMsgTarget.unmask();
24822             }else{
24823                 Roo.MessageBox.updateProgress(1);
24824                 Roo.MessageBox.hide();
24825             }
24826         }
24827         
24828         if(success){
24829             if(o.reset){
24830                 this.reset();
24831             }
24832             Roo.callback(o.success, o.scope, [this, action]);
24833             this.fireEvent('actioncomplete', this, action);
24834             
24835         }else{
24836             
24837             // failure condition..
24838             // we have a scenario where updates need confirming.
24839             // eg. if a locking scenario exists..
24840             // we look for { errors : { needs_confirm : true }} in the response.
24841             if (
24842                 (typeof(action.result) != 'undefined')  &&
24843                 (typeof(action.result.errors) != 'undefined')  &&
24844                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24845            ){
24846                 var _t = this;
24847                 Roo.MessageBox.confirm(
24848                     "Change requires confirmation",
24849                     action.result.errorMsg,
24850                     function(r) {
24851                         if (r != 'yes') {
24852                             return;
24853                         }
24854                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24855                     }
24856                     
24857                 );
24858                 
24859                 
24860                 
24861                 return;
24862             }
24863             
24864             Roo.callback(o.failure, o.scope, [this, action]);
24865             // show an error message if no failed handler is set..
24866             if (!this.hasListener('actionfailed')) {
24867                 Roo.MessageBox.alert("Error",
24868                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24869                         action.result.errorMsg :
24870                         "Saving Failed, please check your entries or try again"
24871                 );
24872             }
24873             
24874             this.fireEvent('actionfailed', this, action);
24875         }
24876         
24877     },
24878
24879     /**
24880      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24881      * @param {String} id The value to search for
24882      * @return Field
24883      */
24884     findField : function(id){
24885         var field = this.items.get(id);
24886         if(!field){
24887             this.items.each(function(f){
24888                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24889                     field = f;
24890                     return false;
24891                 }
24892             });
24893         }
24894         return field || null;
24895     },
24896
24897     /**
24898      * Add a secondary form to this one, 
24899      * Used to provide tabbed forms. One form is primary, with hidden values 
24900      * which mirror the elements from the other forms.
24901      * 
24902      * @param {Roo.form.Form} form to add.
24903      * 
24904      */
24905     addForm : function(form)
24906     {
24907        
24908         if (this.childForms.indexOf(form) > -1) {
24909             // already added..
24910             return;
24911         }
24912         this.childForms.push(form);
24913         var n = '';
24914         Roo.each(form.allItems, function (fe) {
24915             
24916             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24917             if (this.findField(n)) { // already added..
24918                 return;
24919             }
24920             var add = new Roo.form.Hidden({
24921                 name : n
24922             });
24923             add.render(this.el);
24924             
24925             this.add( add );
24926         }, this);
24927         
24928     },
24929     /**
24930      * Mark fields in this form invalid in bulk.
24931      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24932      * @return {BasicForm} this
24933      */
24934     markInvalid : function(errors){
24935         if(errors instanceof Array){
24936             for(var i = 0, len = errors.length; i < len; i++){
24937                 var fieldError = errors[i];
24938                 var f = this.findField(fieldError.id);
24939                 if(f){
24940                     f.markInvalid(fieldError.msg);
24941                 }
24942             }
24943         }else{
24944             var field, id;
24945             for(id in errors){
24946                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24947                     field.markInvalid(errors[id]);
24948                 }
24949             }
24950         }
24951         Roo.each(this.childForms || [], function (f) {
24952             f.markInvalid(errors);
24953         });
24954         
24955         return this;
24956     },
24957
24958     /**
24959      * Set values for fields in this form in bulk.
24960      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24961      * @return {BasicForm} this
24962      */
24963     setValues : function(values){
24964         if(values instanceof Array){ // array of objects
24965             for(var i = 0, len = values.length; i < len; i++){
24966                 var v = values[i];
24967                 var f = this.findField(v.id);
24968                 if(f){
24969                     f.setValue(v.value);
24970                     if(this.trackResetOnLoad){
24971                         f.originalValue = f.getValue();
24972                     }
24973                 }
24974             }
24975         }else{ // object hash
24976             var field, id;
24977             for(id in values){
24978                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24979                     
24980                     if (field.setFromData && 
24981                         field.valueField && 
24982                         field.displayField &&
24983                         // combos' with local stores can 
24984                         // be queried via setValue()
24985                         // to set their value..
24986                         (field.store && !field.store.isLocal)
24987                         ) {
24988                         // it's a combo
24989                         var sd = { };
24990                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24991                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24992                         field.setFromData(sd);
24993                         
24994                     } else {
24995                         field.setValue(values[id]);
24996                     }
24997                     
24998                     
24999                     if(this.trackResetOnLoad){
25000                         field.originalValue = field.getValue();
25001                     }
25002                 }
25003             }
25004         }
25005         this.resetHasChanged();
25006         
25007         
25008         Roo.each(this.childForms || [], function (f) {
25009             f.setValues(values);
25010             f.resetHasChanged();
25011         });
25012                 
25013         return this;
25014     },
25015  
25016     /**
25017      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25018      * they are returned as an array.
25019      * @param {Boolean} asString
25020      * @return {Object}
25021      */
25022     getValues : function(asString){
25023         if (this.childForms) {
25024             // copy values from the child forms
25025             Roo.each(this.childForms, function (f) {
25026                 this.setValues(f.getValues());
25027             }, this);
25028         }
25029         
25030         // use formdata
25031         if (typeof(FormData) != 'undefined' && asString !== true) {
25032             // this relies on a 'recent' version of chrome apparently...
25033             try {
25034                 var fd = (new FormData(this.el.dom)).entries();
25035                 var ret = {};
25036                 var ent = fd.next();
25037                 while (!ent.done) {
25038                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25039                     ent = fd.next();
25040                 };
25041                 return ret;
25042             } catch(e) {
25043                 
25044             }
25045             
25046         }
25047         
25048         
25049         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25050         if(asString === true){
25051             return fs;
25052         }
25053         return Roo.urlDecode(fs);
25054     },
25055     
25056     /**
25057      * Returns the fields in this form as an object with key/value pairs. 
25058      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25059      * @return {Object}
25060      */
25061     getFieldValues : function(with_hidden)
25062     {
25063         if (this.childForms) {
25064             // copy values from the child forms
25065             // should this call getFieldValues - probably not as we do not currently copy
25066             // hidden fields when we generate..
25067             Roo.each(this.childForms, function (f) {
25068                 this.setValues(f.getValues());
25069             }, this);
25070         }
25071         
25072         var ret = {};
25073         this.items.each(function(f){
25074             if (!f.getName()) {
25075                 return;
25076             }
25077             var v = f.getValue();
25078             if (f.inputType =='radio') {
25079                 if (typeof(ret[f.getName()]) == 'undefined') {
25080                     ret[f.getName()] = ''; // empty..
25081                 }
25082                 
25083                 if (!f.el.dom.checked) {
25084                     return;
25085                     
25086                 }
25087                 v = f.el.dom.value;
25088                 
25089             }
25090             
25091             // not sure if this supported any more..
25092             if ((typeof(v) == 'object') && f.getRawValue) {
25093                 v = f.getRawValue() ; // dates..
25094             }
25095             // combo boxes where name != hiddenName...
25096             if (f.name != f.getName()) {
25097                 ret[f.name] = f.getRawValue();
25098             }
25099             ret[f.getName()] = v;
25100         });
25101         
25102         return ret;
25103     },
25104
25105     /**
25106      * Clears all invalid messages in this form.
25107      * @return {BasicForm} this
25108      */
25109     clearInvalid : function(){
25110         this.items.each(function(f){
25111            f.clearInvalid();
25112         });
25113         
25114         Roo.each(this.childForms || [], function (f) {
25115             f.clearInvalid();
25116         });
25117         
25118         
25119         return this;
25120     },
25121
25122     /**
25123      * Resets this form.
25124      * @return {BasicForm} this
25125      */
25126     reset : function(){
25127         this.items.each(function(f){
25128             f.reset();
25129         });
25130         
25131         Roo.each(this.childForms || [], function (f) {
25132             f.reset();
25133         });
25134         this.resetHasChanged();
25135         
25136         return this;
25137     },
25138
25139     /**
25140      * Add Roo.form components to this form.
25141      * @param {Field} field1
25142      * @param {Field} field2 (optional)
25143      * @param {Field} etc (optional)
25144      * @return {BasicForm} this
25145      */
25146     add : function(){
25147         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25148         return this;
25149     },
25150
25151
25152     /**
25153      * Removes a field from the items collection (does NOT remove its markup).
25154      * @param {Field} field
25155      * @return {BasicForm} this
25156      */
25157     remove : function(field){
25158         this.items.remove(field);
25159         return this;
25160     },
25161
25162     /**
25163      * Looks at the fields in this form, checks them for an id attribute,
25164      * and calls applyTo on the existing dom element with that id.
25165      * @return {BasicForm} this
25166      */
25167     render : function(){
25168         this.items.each(function(f){
25169             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25170                 f.applyTo(f.id);
25171             }
25172         });
25173         return this;
25174     },
25175
25176     /**
25177      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25178      * @param {Object} values
25179      * @return {BasicForm} this
25180      */
25181     applyToFields : function(o){
25182         this.items.each(function(f){
25183            Roo.apply(f, o);
25184         });
25185         return this;
25186     },
25187
25188     /**
25189      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25190      * @param {Object} values
25191      * @return {BasicForm} this
25192      */
25193     applyIfToFields : function(o){
25194         this.items.each(function(f){
25195            Roo.applyIf(f, o);
25196         });
25197         return this;
25198     }
25199 });
25200
25201 // back compat
25202 Roo.BasicForm = Roo.form.BasicForm;
25203
25204 Roo.apply(Roo.form.BasicForm, {
25205     
25206     popover : {
25207         
25208         padding : 5,
25209         
25210         isApplied : false,
25211         
25212         isMasked : false,
25213         
25214         form : false,
25215         
25216         target : false,
25217         
25218         intervalID : false,
25219         
25220         maskEl : false,
25221         
25222         apply : function()
25223         {
25224             if(this.isApplied){
25225                 return;
25226             }
25227             
25228             this.maskEl = {
25229                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25230                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25231                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25232                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25233             };
25234             
25235             this.maskEl.top.enableDisplayMode("block");
25236             this.maskEl.left.enableDisplayMode("block");
25237             this.maskEl.bottom.enableDisplayMode("block");
25238             this.maskEl.right.enableDisplayMode("block");
25239             
25240             Roo.get(document.body).on('click', function(){
25241                 this.unmask();
25242             }, this);
25243             
25244             Roo.get(document.body).on('touchstart', function(){
25245                 this.unmask();
25246             }, this);
25247             
25248             this.isApplied = true
25249         },
25250         
25251         mask : function(form, target)
25252         {
25253             this.form = form;
25254             
25255             this.target = target;
25256             
25257             if(!this.form.errorMask || !target.el){
25258                 return;
25259             }
25260             
25261             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25262             
25263             var ot = this.target.el.calcOffsetsTo(scrollable);
25264             
25265             var scrollTo = ot[1] - this.form.maskOffset;
25266             
25267             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25268             
25269             scrollable.scrollTo('top', scrollTo);
25270             
25271             var el = this.target.wrap || this.target.el;
25272             
25273             var box = el.getBox();
25274             
25275             this.maskEl.top.setStyle('position', 'absolute');
25276             this.maskEl.top.setStyle('z-index', 10000);
25277             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25278             this.maskEl.top.setLeft(0);
25279             this.maskEl.top.setTop(0);
25280             this.maskEl.top.show();
25281             
25282             this.maskEl.left.setStyle('position', 'absolute');
25283             this.maskEl.left.setStyle('z-index', 10000);
25284             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25285             this.maskEl.left.setLeft(0);
25286             this.maskEl.left.setTop(box.y - this.padding);
25287             this.maskEl.left.show();
25288
25289             this.maskEl.bottom.setStyle('position', 'absolute');
25290             this.maskEl.bottom.setStyle('z-index', 10000);
25291             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25292             this.maskEl.bottom.setLeft(0);
25293             this.maskEl.bottom.setTop(box.bottom + this.padding);
25294             this.maskEl.bottom.show();
25295
25296             this.maskEl.right.setStyle('position', 'absolute');
25297             this.maskEl.right.setStyle('z-index', 10000);
25298             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25299             this.maskEl.right.setLeft(box.right + this.padding);
25300             this.maskEl.right.setTop(box.y - this.padding);
25301             this.maskEl.right.show();
25302
25303             this.intervalID = window.setInterval(function() {
25304                 Roo.form.BasicForm.popover.unmask();
25305             }, 10000);
25306
25307             window.onwheel = function(){ return false;};
25308             
25309             (function(){ this.isMasked = true; }).defer(500, this);
25310             
25311         },
25312         
25313         unmask : function()
25314         {
25315             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25316                 return;
25317             }
25318             
25319             this.maskEl.top.setStyle('position', 'absolute');
25320             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25321             this.maskEl.top.hide();
25322
25323             this.maskEl.left.setStyle('position', 'absolute');
25324             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25325             this.maskEl.left.hide();
25326
25327             this.maskEl.bottom.setStyle('position', 'absolute');
25328             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25329             this.maskEl.bottom.hide();
25330
25331             this.maskEl.right.setStyle('position', 'absolute');
25332             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25333             this.maskEl.right.hide();
25334             
25335             window.onwheel = function(){ return true;};
25336             
25337             if(this.intervalID){
25338                 window.clearInterval(this.intervalID);
25339                 this.intervalID = false;
25340             }
25341             
25342             this.isMasked = false;
25343             
25344         }
25345         
25346     }
25347     
25348 });/*
25349  * Based on:
25350  * Ext JS Library 1.1.1
25351  * Copyright(c) 2006-2007, Ext JS, LLC.
25352  *
25353  * Originally Released Under LGPL - original licence link has changed is not relivant.
25354  *
25355  * Fork - LGPL
25356  * <script type="text/javascript">
25357  */
25358
25359 /**
25360  * @class Roo.form.Form
25361  * @extends Roo.form.BasicForm
25362  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25363  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25364  * @constructor
25365  * @param {Object} config Configuration options
25366  */
25367 Roo.form.Form = function(config){
25368     var xitems =  [];
25369     if (config.items) {
25370         xitems = config.items;
25371         delete config.items;
25372     }
25373    
25374     
25375     Roo.form.Form.superclass.constructor.call(this, null, config);
25376     this.url = this.url || this.action;
25377     if(!this.root){
25378         this.root = new Roo.form.Layout(Roo.applyIf({
25379             id: Roo.id()
25380         }, config));
25381     }
25382     this.active = this.root;
25383     /**
25384      * Array of all the buttons that have been added to this form via {@link addButton}
25385      * @type Array
25386      */
25387     this.buttons = [];
25388     this.allItems = [];
25389     this.addEvents({
25390         /**
25391          * @event clientvalidation
25392          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25393          * @param {Form} this
25394          * @param {Boolean} valid true if the form has passed client-side validation
25395          */
25396         clientvalidation: true,
25397         /**
25398          * @event rendered
25399          * Fires when the form is rendered
25400          * @param {Roo.form.Form} form
25401          */
25402         rendered : true
25403     });
25404     
25405     if (this.progressUrl) {
25406             // push a hidden field onto the list of fields..
25407             this.addxtype( {
25408                     xns: Roo.form, 
25409                     xtype : 'Hidden', 
25410                     name : 'UPLOAD_IDENTIFIER' 
25411             });
25412         }
25413         
25414     
25415     Roo.each(xitems, this.addxtype, this);
25416     
25417 };
25418
25419 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25420      /**
25421      * @cfg {Roo.Button} buttons[] buttons at bottom of form
25422      */
25423     
25424     /**
25425      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25426      */
25427     /**
25428      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25429      */
25430     /**
25431      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25432      */
25433     buttonAlign:'center',
25434
25435     /**
25436      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25437      */
25438     minButtonWidth:75,
25439
25440     /**
25441      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25442      * This property cascades to child containers if not set.
25443      */
25444     labelAlign:'left',
25445
25446     /**
25447      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25448      * fires a looping event with that state. This is required to bind buttons to the valid
25449      * state using the config value formBind:true on the button.
25450      */
25451     monitorValid : false,
25452
25453     /**
25454      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25455      */
25456     monitorPoll : 200,
25457     
25458     /**
25459      * @cfg {String} progressUrl - Url to return progress data 
25460      */
25461     
25462     progressUrl : false,
25463     /**
25464      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25465      * sending a formdata with extra parameters - eg uploaded elements.
25466      */
25467     
25468     formData : false,
25469     
25470     /**
25471      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25472      * fields are added and the column is closed. If no fields are passed the column remains open
25473      * until end() is called.
25474      * @param {Object} config The config to pass to the column
25475      * @param {Field} field1 (optional)
25476      * @param {Field} field2 (optional)
25477      * @param {Field} etc (optional)
25478      * @return Column The column container object
25479      */
25480     column : function(c){
25481         var col = new Roo.form.Column(c);
25482         this.start(col);
25483         if(arguments.length > 1){ // duplicate code required because of Opera
25484             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25485             this.end();
25486         }
25487         return col;
25488     },
25489
25490     /**
25491      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25492      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25493      * until end() is called.
25494      * @param {Object} config The config to pass to the fieldset
25495      * @param {Field} field1 (optional)
25496      * @param {Field} field2 (optional)
25497      * @param {Field} etc (optional)
25498      * @return FieldSet The fieldset container object
25499      */
25500     fieldset : function(c){
25501         var fs = new Roo.form.FieldSet(c);
25502         this.start(fs);
25503         if(arguments.length > 1){ // duplicate code required because of Opera
25504             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25505             this.end();
25506         }
25507         return fs;
25508     },
25509
25510     /**
25511      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25512      * fields are added and the container is closed. If no fields are passed the container remains open
25513      * until end() is called.
25514      * @param {Object} config The config to pass to the Layout
25515      * @param {Field} field1 (optional)
25516      * @param {Field} field2 (optional)
25517      * @param {Field} etc (optional)
25518      * @return Layout The container object
25519      */
25520     container : function(c){
25521         var l = new Roo.form.Layout(c);
25522         this.start(l);
25523         if(arguments.length > 1){ // duplicate code required because of Opera
25524             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25525             this.end();
25526         }
25527         return l;
25528     },
25529
25530     /**
25531      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25532      * @param {Object} container A Roo.form.Layout or subclass of Layout
25533      * @return {Form} this
25534      */
25535     start : function(c){
25536         // cascade label info
25537         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25538         this.active.stack.push(c);
25539         c.ownerCt = this.active;
25540         this.active = c;
25541         return this;
25542     },
25543
25544     /**
25545      * Closes the current open container
25546      * @return {Form} this
25547      */
25548     end : function(){
25549         if(this.active == this.root){
25550             return this;
25551         }
25552         this.active = this.active.ownerCt;
25553         return this;
25554     },
25555
25556     /**
25557      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25558      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25559      * as the label of the field.
25560      * @param {Field} field1
25561      * @param {Field} field2 (optional)
25562      * @param {Field} etc. (optional)
25563      * @return {Form} this
25564      */
25565     add : function(){
25566         this.active.stack.push.apply(this.active.stack, arguments);
25567         this.allItems.push.apply(this.allItems,arguments);
25568         var r = [];
25569         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25570             if(a[i].isFormField){
25571                 r.push(a[i]);
25572             }
25573         }
25574         if(r.length > 0){
25575             Roo.form.Form.superclass.add.apply(this, r);
25576         }
25577         return this;
25578     },
25579     
25580
25581     
25582     
25583     
25584      /**
25585      * Find any element that has been added to a form, using it's ID or name
25586      * This can include framesets, columns etc. along with regular fields..
25587      * @param {String} id - id or name to find.
25588      
25589      * @return {Element} e - or false if nothing found.
25590      */
25591     findbyId : function(id)
25592     {
25593         var ret = false;
25594         if (!id) {
25595             return ret;
25596         }
25597         Roo.each(this.allItems, function(f){
25598             if (f.id == id || f.name == id ){
25599                 ret = f;
25600                 return false;
25601             }
25602         });
25603         return ret;
25604     },
25605
25606     
25607     
25608     /**
25609      * Render this form into the passed container. This should only be called once!
25610      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25611      * @return {Form} this
25612      */
25613     render : function(ct)
25614     {
25615         
25616         
25617         
25618         ct = Roo.get(ct);
25619         var o = this.autoCreate || {
25620             tag: 'form',
25621             method : this.method || 'POST',
25622             id : this.id || Roo.id()
25623         };
25624         this.initEl(ct.createChild(o));
25625
25626         this.root.render(this.el);
25627         
25628        
25629              
25630         this.items.each(function(f){
25631             f.render('x-form-el-'+f.id);
25632         });
25633
25634         if(this.buttons.length > 0){
25635             // tables are required to maintain order and for correct IE layout
25636             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25637                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25638                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25639             }}, null, true);
25640             var tr = tb.getElementsByTagName('tr')[0];
25641             for(var i = 0, len = this.buttons.length; i < len; i++) {
25642                 var b = this.buttons[i];
25643                 var td = document.createElement('td');
25644                 td.className = 'x-form-btn-td';
25645                 b.render(tr.appendChild(td));
25646             }
25647         }
25648         if(this.monitorValid){ // initialize after render
25649             this.startMonitoring();
25650         }
25651         this.fireEvent('rendered', this);
25652         return this;
25653     },
25654
25655     /**
25656      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25657      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25658      * object or a valid Roo.DomHelper element config
25659      * @param {Function} handler The function called when the button is clicked
25660      * @param {Object} scope (optional) The scope of the handler function
25661      * @return {Roo.Button}
25662      */
25663     addButton : function(config, handler, scope){
25664         var bc = {
25665             handler: handler,
25666             scope: scope,
25667             minWidth: this.minButtonWidth,
25668             hideParent:true
25669         };
25670         if(typeof config == "string"){
25671             bc.text = config;
25672         }else{
25673             Roo.apply(bc, config);
25674         }
25675         var btn = new Roo.Button(null, bc);
25676         this.buttons.push(btn);
25677         return btn;
25678     },
25679
25680      /**
25681      * Adds a series of form elements (using the xtype property as the factory method.
25682      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25683      * @param {Object} config 
25684      */
25685     
25686     addxtype : function()
25687     {
25688         var ar = Array.prototype.slice.call(arguments, 0);
25689         var ret = false;
25690         for(var i = 0; i < ar.length; i++) {
25691             if (!ar[i]) {
25692                 continue; // skip -- if this happends something invalid got sent, we 
25693                 // should ignore it, as basically that interface element will not show up
25694                 // and that should be pretty obvious!!
25695             }
25696             
25697             if (Roo.form[ar[i].xtype]) {
25698                 ar[i].form = this;
25699                 var fe = Roo.factory(ar[i], Roo.form);
25700                 if (!ret) {
25701                     ret = fe;
25702                 }
25703                 fe.form = this;
25704                 if (fe.store) {
25705                     fe.store.form = this;
25706                 }
25707                 if (fe.isLayout) {  
25708                          
25709                     this.start(fe);
25710                     this.allItems.push(fe);
25711                     if (fe.items && fe.addxtype) {
25712                         fe.addxtype.apply(fe, fe.items);
25713                         delete fe.items;
25714                     }
25715                      this.end();
25716                     continue;
25717                 }
25718                 
25719                 
25720                  
25721                 this.add(fe);
25722               //  console.log('adding ' + ar[i].xtype);
25723             }
25724             if (ar[i].xtype == 'Button') {  
25725                 //console.log('adding button');
25726                 //console.log(ar[i]);
25727                 this.addButton(ar[i]);
25728                 this.allItems.push(fe);
25729                 continue;
25730             }
25731             
25732             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25733                 alert('end is not supported on xtype any more, use items');
25734             //    this.end();
25735             //    //console.log('adding end');
25736             }
25737             
25738         }
25739         return ret;
25740     },
25741     
25742     /**
25743      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25744      * option "monitorValid"
25745      */
25746     startMonitoring : function(){
25747         if(!this.bound){
25748             this.bound = true;
25749             Roo.TaskMgr.start({
25750                 run : this.bindHandler,
25751                 interval : this.monitorPoll || 200,
25752                 scope: this
25753             });
25754         }
25755     },
25756
25757     /**
25758      * Stops monitoring of the valid state of this form
25759      */
25760     stopMonitoring : function(){
25761         this.bound = false;
25762     },
25763
25764     // private
25765     bindHandler : function(){
25766         if(!this.bound){
25767             return false; // stops binding
25768         }
25769         var valid = true;
25770         this.items.each(function(f){
25771             if(!f.isValid(true)){
25772                 valid = false;
25773                 return false;
25774             }
25775         });
25776         for(var i = 0, len = this.buttons.length; i < len; i++){
25777             var btn = this.buttons[i];
25778             if(btn.formBind === true && btn.disabled === valid){
25779                 btn.setDisabled(!valid);
25780             }
25781         }
25782         this.fireEvent('clientvalidation', this, valid);
25783     }
25784     
25785     
25786     
25787     
25788     
25789     
25790     
25791     
25792 });
25793
25794
25795 // back compat
25796 Roo.Form = Roo.form.Form;
25797 /*
25798  * Based on:
25799  * Ext JS Library 1.1.1
25800  * Copyright(c) 2006-2007, Ext JS, LLC.
25801  *
25802  * Originally Released Under LGPL - original licence link has changed is not relivant.
25803  *
25804  * Fork - LGPL
25805  * <script type="text/javascript">
25806  */
25807
25808 // as we use this in bootstrap.
25809 Roo.namespace('Roo.form');
25810  /**
25811  * @class Roo.form.Action
25812  * Internal Class used to handle form actions
25813  * @constructor
25814  * @param {Roo.form.BasicForm} el The form element or its id
25815  * @param {Object} config Configuration options
25816  */
25817
25818  
25819  
25820 // define the action interface
25821 Roo.form.Action = function(form, options){
25822     this.form = form;
25823     this.options = options || {};
25824 };
25825 /**
25826  * Client Validation Failed
25827  * @const 
25828  */
25829 Roo.form.Action.CLIENT_INVALID = 'client';
25830 /**
25831  * Server Validation Failed
25832  * @const 
25833  */
25834 Roo.form.Action.SERVER_INVALID = 'server';
25835  /**
25836  * Connect to Server Failed
25837  * @const 
25838  */
25839 Roo.form.Action.CONNECT_FAILURE = 'connect';
25840 /**
25841  * Reading Data from Server Failed
25842  * @const 
25843  */
25844 Roo.form.Action.LOAD_FAILURE = 'load';
25845
25846 Roo.form.Action.prototype = {
25847     type : 'default',
25848     failureType : undefined,
25849     response : undefined,
25850     result : undefined,
25851
25852     // interface method
25853     run : function(options){
25854
25855     },
25856
25857     // interface method
25858     success : function(response){
25859
25860     },
25861
25862     // interface method
25863     handleResponse : function(response){
25864
25865     },
25866
25867     // default connection failure
25868     failure : function(response){
25869         
25870         this.response = response;
25871         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25872         this.form.afterAction(this, false);
25873     },
25874
25875     processResponse : function(response){
25876         this.response = response;
25877         if(!response.responseText){
25878             return true;
25879         }
25880         this.result = this.handleResponse(response);
25881         return this.result;
25882     },
25883
25884     // utility functions used internally
25885     getUrl : function(appendParams){
25886         var url = this.options.url || this.form.url || this.form.el.dom.action;
25887         if(appendParams){
25888             var p = this.getParams();
25889             if(p){
25890                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25891             }
25892         }
25893         return url;
25894     },
25895
25896     getMethod : function(){
25897         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25898     },
25899
25900     getParams : function(){
25901         var bp = this.form.baseParams;
25902         var p = this.options.params;
25903         if(p){
25904             if(typeof p == "object"){
25905                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25906             }else if(typeof p == 'string' && bp){
25907                 p += '&' + Roo.urlEncode(bp);
25908             }
25909         }else if(bp){
25910             p = Roo.urlEncode(bp);
25911         }
25912         return p;
25913     },
25914
25915     createCallback : function(){
25916         return {
25917             success: this.success,
25918             failure: this.failure,
25919             scope: this,
25920             timeout: (this.form.timeout*1000),
25921             upload: this.form.fileUpload ? this.success : undefined
25922         };
25923     }
25924 };
25925
25926 Roo.form.Action.Submit = function(form, options){
25927     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25928 };
25929
25930 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25931     type : 'submit',
25932
25933     haveProgress : false,
25934     uploadComplete : false,
25935     
25936     // uploadProgress indicator.
25937     uploadProgress : function()
25938     {
25939         if (!this.form.progressUrl) {
25940             return;
25941         }
25942         
25943         if (!this.haveProgress) {
25944             Roo.MessageBox.progress("Uploading", "Uploading");
25945         }
25946         if (this.uploadComplete) {
25947            Roo.MessageBox.hide();
25948            return;
25949         }
25950         
25951         this.haveProgress = true;
25952    
25953         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25954         
25955         var c = new Roo.data.Connection();
25956         c.request({
25957             url : this.form.progressUrl,
25958             params: {
25959                 id : uid
25960             },
25961             method: 'GET',
25962             success : function(req){
25963                //console.log(data);
25964                 var rdata = false;
25965                 var edata;
25966                 try  {
25967                    rdata = Roo.decode(req.responseText)
25968                 } catch (e) {
25969                     Roo.log("Invalid data from server..");
25970                     Roo.log(edata);
25971                     return;
25972                 }
25973                 if (!rdata || !rdata.success) {
25974                     Roo.log(rdata);
25975                     Roo.MessageBox.alert(Roo.encode(rdata));
25976                     return;
25977                 }
25978                 var data = rdata.data;
25979                 
25980                 if (this.uploadComplete) {
25981                    Roo.MessageBox.hide();
25982                    return;
25983                 }
25984                    
25985                 if (data){
25986                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25987                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25988                     );
25989                 }
25990                 this.uploadProgress.defer(2000,this);
25991             },
25992        
25993             failure: function(data) {
25994                 Roo.log('progress url failed ');
25995                 Roo.log(data);
25996             },
25997             scope : this
25998         });
25999            
26000     },
26001     
26002     
26003     run : function()
26004     {
26005         // run get Values on the form, so it syncs any secondary forms.
26006         this.form.getValues();
26007         
26008         var o = this.options;
26009         var method = this.getMethod();
26010         var isPost = method == 'POST';
26011         if(o.clientValidation === false || this.form.isValid()){
26012             
26013             if (this.form.progressUrl) {
26014                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26015                     (new Date() * 1) + '' + Math.random());
26016                     
26017             } 
26018             
26019             
26020             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26021                 form:this.form.el.dom,
26022                 url:this.getUrl(!isPost),
26023                 method: method,
26024                 params:isPost ? this.getParams() : null,
26025                 isUpload: this.form.fileUpload,
26026                 formData : this.form.formData
26027             }));
26028             
26029             this.uploadProgress();
26030
26031         }else if (o.clientValidation !== false){ // client validation failed
26032             this.failureType = Roo.form.Action.CLIENT_INVALID;
26033             this.form.afterAction(this, false);
26034         }
26035     },
26036
26037     success : function(response)
26038     {
26039         this.uploadComplete= true;
26040         if (this.haveProgress) {
26041             Roo.MessageBox.hide();
26042         }
26043         
26044         
26045         var result = this.processResponse(response);
26046         if(result === true || result.success){
26047             this.form.afterAction(this, true);
26048             return;
26049         }
26050         if(result.errors){
26051             this.form.markInvalid(result.errors);
26052             this.failureType = Roo.form.Action.SERVER_INVALID;
26053         }
26054         this.form.afterAction(this, false);
26055     },
26056     failure : function(response)
26057     {
26058         this.uploadComplete= true;
26059         if (this.haveProgress) {
26060             Roo.MessageBox.hide();
26061         }
26062         
26063         this.response = response;
26064         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26065         this.form.afterAction(this, false);
26066     },
26067     
26068     handleResponse : function(response){
26069         if(this.form.errorReader){
26070             var rs = this.form.errorReader.read(response);
26071             var errors = [];
26072             if(rs.records){
26073                 for(var i = 0, len = rs.records.length; i < len; i++) {
26074                     var r = rs.records[i];
26075                     errors[i] = r.data;
26076                 }
26077             }
26078             if(errors.length < 1){
26079                 errors = null;
26080             }
26081             return {
26082                 success : rs.success,
26083                 errors : errors
26084             };
26085         }
26086         var ret = false;
26087         try {
26088             ret = Roo.decode(response.responseText);
26089         } catch (e) {
26090             ret = {
26091                 success: false,
26092                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26093                 errors : []
26094             };
26095         }
26096         return ret;
26097         
26098     }
26099 });
26100
26101
26102 Roo.form.Action.Load = function(form, options){
26103     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26104     this.reader = this.form.reader;
26105 };
26106
26107 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26108     type : 'load',
26109
26110     run : function(){
26111         
26112         Roo.Ajax.request(Roo.apply(
26113                 this.createCallback(), {
26114                     method:this.getMethod(),
26115                     url:this.getUrl(false),
26116                     params:this.getParams()
26117         }));
26118     },
26119
26120     success : function(response){
26121         
26122         var result = this.processResponse(response);
26123         if(result === true || !result.success || !result.data){
26124             this.failureType = Roo.form.Action.LOAD_FAILURE;
26125             this.form.afterAction(this, false);
26126             return;
26127         }
26128         this.form.clearInvalid();
26129         this.form.setValues(result.data);
26130         this.form.afterAction(this, true);
26131     },
26132
26133     handleResponse : function(response){
26134         if(this.form.reader){
26135             var rs = this.form.reader.read(response);
26136             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26137             return {
26138                 success : rs.success,
26139                 data : data
26140             };
26141         }
26142         return Roo.decode(response.responseText);
26143     }
26144 });
26145
26146 Roo.form.Action.ACTION_TYPES = {
26147     'load' : Roo.form.Action.Load,
26148     'submit' : Roo.form.Action.Submit
26149 };/*
26150  * Based on:
26151  * Ext JS Library 1.1.1
26152  * Copyright(c) 2006-2007, Ext JS, LLC.
26153  *
26154  * Originally Released Under LGPL - original licence link has changed is not relivant.
26155  *
26156  * Fork - LGPL
26157  * <script type="text/javascript">
26158  */
26159  
26160 /**
26161  * @class Roo.form.Layout
26162  * @extends Roo.Component
26163  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26164  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26165  * @constructor
26166  * @param {Object} config Configuration options
26167  */
26168 Roo.form.Layout = function(config){
26169     var xitems = [];
26170     if (config.items) {
26171         xitems = config.items;
26172         delete config.items;
26173     }
26174     Roo.form.Layout.superclass.constructor.call(this, config);
26175     this.stack = [];
26176     Roo.each(xitems, this.addxtype, this);
26177      
26178 };
26179
26180 Roo.extend(Roo.form.Layout, Roo.Component, {
26181     /**
26182      * @cfg {String/Object} autoCreate
26183      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26184      */
26185     /**
26186      * @cfg {String/Object/Function} style
26187      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26188      * a function which returns such a specification.
26189      */
26190     /**
26191      * @cfg {String} labelAlign
26192      * Valid values are "left," "top" and "right" (defaults to "left")
26193      */
26194     /**
26195      * @cfg {Number} labelWidth
26196      * Fixed width in pixels of all field labels (defaults to undefined)
26197      */
26198     /**
26199      * @cfg {Boolean} clear
26200      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26201      */
26202     clear : true,
26203     /**
26204      * @cfg {String} labelSeparator
26205      * The separator to use after field labels (defaults to ':')
26206      */
26207     labelSeparator : ':',
26208     /**
26209      * @cfg {Boolean} hideLabels
26210      * True to suppress the display of field labels in this layout (defaults to false)
26211      */
26212     hideLabels : false,
26213
26214     // private
26215     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26216     
26217     isLayout : true,
26218     
26219     // private
26220     onRender : function(ct, position){
26221         if(this.el){ // from markup
26222             this.el = Roo.get(this.el);
26223         }else {  // generate
26224             var cfg = this.getAutoCreate();
26225             this.el = ct.createChild(cfg, position);
26226         }
26227         if(this.style){
26228             this.el.applyStyles(this.style);
26229         }
26230         if(this.labelAlign){
26231             this.el.addClass('x-form-label-'+this.labelAlign);
26232         }
26233         if(this.hideLabels){
26234             this.labelStyle = "display:none";
26235             this.elementStyle = "padding-left:0;";
26236         }else{
26237             if(typeof this.labelWidth == 'number'){
26238                 this.labelStyle = "width:"+this.labelWidth+"px;";
26239                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26240             }
26241             if(this.labelAlign == 'top'){
26242                 this.labelStyle = "width:auto;";
26243                 this.elementStyle = "padding-left:0;";
26244             }
26245         }
26246         var stack = this.stack;
26247         var slen = stack.length;
26248         if(slen > 0){
26249             if(!this.fieldTpl){
26250                 var t = new Roo.Template(
26251                     '<div class="x-form-item {5}">',
26252                         '<label for="{0}" style="{2}">{1}{4}</label>',
26253                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26254                         '</div>',
26255                     '</div><div class="x-form-clear-left"></div>'
26256                 );
26257                 t.disableFormats = true;
26258                 t.compile();
26259                 Roo.form.Layout.prototype.fieldTpl = t;
26260             }
26261             for(var i = 0; i < slen; i++) {
26262                 if(stack[i].isFormField){
26263                     this.renderField(stack[i]);
26264                 }else{
26265                     this.renderComponent(stack[i]);
26266                 }
26267             }
26268         }
26269         if(this.clear){
26270             this.el.createChild({cls:'x-form-clear'});
26271         }
26272     },
26273
26274     // private
26275     renderField : function(f){
26276         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26277                f.id, //0
26278                f.fieldLabel, //1
26279                f.labelStyle||this.labelStyle||'', //2
26280                this.elementStyle||'', //3
26281                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26282                f.itemCls||this.itemCls||''  //5
26283        ], true).getPrevSibling());
26284     },
26285
26286     // private
26287     renderComponent : function(c){
26288         c.render(c.isLayout ? this.el : this.el.createChild());    
26289     },
26290     /**
26291      * Adds a object form elements (using the xtype property as the factory method.)
26292      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26293      * @param {Object} config 
26294      */
26295     addxtype : function(o)
26296     {
26297         // create the lement.
26298         o.form = this.form;
26299         var fe = Roo.factory(o, Roo.form);
26300         this.form.allItems.push(fe);
26301         this.stack.push(fe);
26302         
26303         if (fe.isFormField) {
26304             this.form.items.add(fe);
26305         }
26306          
26307         return fe;
26308     }
26309 });
26310
26311 /**
26312  * @class Roo.form.Column
26313  * @extends Roo.form.Layout
26314  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26315  * @constructor
26316  * @param {Object} config Configuration options
26317  */
26318 Roo.form.Column = function(config){
26319     Roo.form.Column.superclass.constructor.call(this, config);
26320 };
26321
26322 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26323     /**
26324      * @cfg {Number/String} width
26325      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26326      */
26327     /**
26328      * @cfg {String/Object} autoCreate
26329      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26330      */
26331
26332     // private
26333     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26334
26335     // private
26336     onRender : function(ct, position){
26337         Roo.form.Column.superclass.onRender.call(this, ct, position);
26338         if(this.width){
26339             this.el.setWidth(this.width);
26340         }
26341     }
26342 });
26343
26344
26345 /**
26346  * @class Roo.form.Row
26347  * @extends Roo.form.Layout
26348  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26349  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26350  * @constructor
26351  * @param {Object} config Configuration options
26352  */
26353
26354  
26355 Roo.form.Row = function(config){
26356     Roo.form.Row.superclass.constructor.call(this, config);
26357 };
26358  
26359 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26360       /**
26361      * @cfg {Number/String} width
26362      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26363      */
26364     /**
26365      * @cfg {Number/String} height
26366      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26367      */
26368     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26369     
26370     padWidth : 20,
26371     // private
26372     onRender : function(ct, position){
26373         //console.log('row render');
26374         if(!this.rowTpl){
26375             var t = new Roo.Template(
26376                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26377                     '<label for="{0}" style="{2}">{1}{4}</label>',
26378                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26379                     '</div>',
26380                 '</div>'
26381             );
26382             t.disableFormats = true;
26383             t.compile();
26384             Roo.form.Layout.prototype.rowTpl = t;
26385         }
26386         this.fieldTpl = this.rowTpl;
26387         
26388         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26389         var labelWidth = 100;
26390         
26391         if ((this.labelAlign != 'top')) {
26392             if (typeof this.labelWidth == 'number') {
26393                 labelWidth = this.labelWidth
26394             }
26395             this.padWidth =  20 + labelWidth;
26396             
26397         }
26398         
26399         Roo.form.Column.superclass.onRender.call(this, ct, position);
26400         if(this.width){
26401             this.el.setWidth(this.width);
26402         }
26403         if(this.height){
26404             this.el.setHeight(this.height);
26405         }
26406     },
26407     
26408     // private
26409     renderField : function(f){
26410         f.fieldEl = this.fieldTpl.append(this.el, [
26411                f.id, f.fieldLabel,
26412                f.labelStyle||this.labelStyle||'',
26413                this.elementStyle||'',
26414                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26415                f.itemCls||this.itemCls||'',
26416                f.width ? f.width + this.padWidth : 160 + this.padWidth
26417        ],true);
26418     }
26419 });
26420  
26421
26422 /**
26423  * @class Roo.form.FieldSet
26424  * @extends Roo.form.Layout
26425  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26426  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26427  * @constructor
26428  * @param {Object} config Configuration options
26429  */
26430 Roo.form.FieldSet = function(config){
26431     Roo.form.FieldSet.superclass.constructor.call(this, config);
26432 };
26433
26434 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26435     /**
26436      * @cfg {String} legend
26437      * The text to display as the legend for the FieldSet (defaults to '')
26438      */
26439     /**
26440      * @cfg {String/Object} autoCreate
26441      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26442      */
26443
26444     // private
26445     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26446
26447     // private
26448     onRender : function(ct, position){
26449         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26450         if(this.legend){
26451             this.setLegend(this.legend);
26452         }
26453     },
26454
26455     // private
26456     setLegend : function(text){
26457         if(this.rendered){
26458             this.el.child('legend').update(text);
26459         }
26460     }
26461 });/*
26462  * Based on:
26463  * Ext JS Library 1.1.1
26464  * Copyright(c) 2006-2007, Ext JS, LLC.
26465  *
26466  * Originally Released Under LGPL - original licence link has changed is not relivant.
26467  *
26468  * Fork - LGPL
26469  * <script type="text/javascript">
26470  */
26471 /**
26472  * @class Roo.form.VTypes
26473  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26474  * @singleton
26475  */
26476 Roo.form.VTypes = function(){
26477     // closure these in so they are only created once.
26478     var alpha = /^[a-zA-Z_]+$/;
26479     var alphanum = /^[a-zA-Z0-9_]+$/;
26480     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26481     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26482
26483     // All these messages and functions are configurable
26484     return {
26485         /**
26486          * The function used to validate email addresses
26487          * @param {String} value The email address
26488          */
26489         'email' : function(v){
26490             return email.test(v);
26491         },
26492         /**
26493          * The error text to display when the email validation function returns false
26494          * @type String
26495          */
26496         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26497         /**
26498          * The keystroke filter mask to be applied on email input
26499          * @type RegExp
26500          */
26501         'emailMask' : /[a-z0-9_\.\-@]/i,
26502
26503         /**
26504          * The function used to validate URLs
26505          * @param {String} value The URL
26506          */
26507         'url' : function(v){
26508             return url.test(v);
26509         },
26510         /**
26511          * The error text to display when the url validation function returns false
26512          * @type String
26513          */
26514         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26515         
26516         /**
26517          * The function used to validate alpha values
26518          * @param {String} value The value
26519          */
26520         'alpha' : function(v){
26521             return alpha.test(v);
26522         },
26523         /**
26524          * The error text to display when the alpha validation function returns false
26525          * @type String
26526          */
26527         'alphaText' : 'This field should only contain letters and _',
26528         /**
26529          * The keystroke filter mask to be applied on alpha input
26530          * @type RegExp
26531          */
26532         'alphaMask' : /[a-z_]/i,
26533
26534         /**
26535          * The function used to validate alphanumeric values
26536          * @param {String} value The value
26537          */
26538         'alphanum' : function(v){
26539             return alphanum.test(v);
26540         },
26541         /**
26542          * The error text to display when the alphanumeric validation function returns false
26543          * @type String
26544          */
26545         'alphanumText' : 'This field should only contain letters, numbers and _',
26546         /**
26547          * The keystroke filter mask to be applied on alphanumeric input
26548          * @type RegExp
26549          */
26550         'alphanumMask' : /[a-z0-9_]/i
26551     };
26552 }();//<script type="text/javascript">
26553
26554 /**
26555  * @class Roo.form.FCKeditor
26556  * @extends Roo.form.TextArea
26557  * Wrapper around the FCKEditor http://www.fckeditor.net
26558  * @constructor
26559  * Creates a new FCKeditor
26560  * @param {Object} config Configuration options
26561  */
26562 Roo.form.FCKeditor = function(config){
26563     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26564     this.addEvents({
26565          /**
26566          * @event editorinit
26567          * Fired when the editor is initialized - you can add extra handlers here..
26568          * @param {FCKeditor} this
26569          * @param {Object} the FCK object.
26570          */
26571         editorinit : true
26572     });
26573     
26574     
26575 };
26576 Roo.form.FCKeditor.editors = { };
26577 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26578 {
26579     //defaultAutoCreate : {
26580     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26581     //},
26582     // private
26583     /**
26584      * @cfg {Object} fck options - see fck manual for details.
26585      */
26586     fckconfig : false,
26587     
26588     /**
26589      * @cfg {Object} fck toolbar set (Basic or Default)
26590      */
26591     toolbarSet : 'Basic',
26592     /**
26593      * @cfg {Object} fck BasePath
26594      */ 
26595     basePath : '/fckeditor/',
26596     
26597     
26598     frame : false,
26599     
26600     value : '',
26601     
26602    
26603     onRender : function(ct, position)
26604     {
26605         if(!this.el){
26606             this.defaultAutoCreate = {
26607                 tag: "textarea",
26608                 style:"width:300px;height:60px;",
26609                 autocomplete: "new-password"
26610             };
26611         }
26612         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26613         /*
26614         if(this.grow){
26615             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26616             if(this.preventScrollbars){
26617                 this.el.setStyle("overflow", "hidden");
26618             }
26619             this.el.setHeight(this.growMin);
26620         }
26621         */
26622         //console.log('onrender' + this.getId() );
26623         Roo.form.FCKeditor.editors[this.getId()] = this;
26624          
26625
26626         this.replaceTextarea() ;
26627         
26628     },
26629     
26630     getEditor : function() {
26631         return this.fckEditor;
26632     },
26633     /**
26634      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26635      * @param {Mixed} value The value to set
26636      */
26637     
26638     
26639     setValue : function(value)
26640     {
26641         //console.log('setValue: ' + value);
26642         
26643         if(typeof(value) == 'undefined') { // not sure why this is happending...
26644             return;
26645         }
26646         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26647         
26648         //if(!this.el || !this.getEditor()) {
26649         //    this.value = value;
26650             //this.setValue.defer(100,this,[value]);    
26651         //    return;
26652         //} 
26653         
26654         if(!this.getEditor()) {
26655             return;
26656         }
26657         
26658         this.getEditor().SetData(value);
26659         
26660         //
26661
26662     },
26663
26664     /**
26665      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26666      * @return {Mixed} value The field value
26667      */
26668     getValue : function()
26669     {
26670         
26671         if (this.frame && this.frame.dom.style.display == 'none') {
26672             return Roo.form.FCKeditor.superclass.getValue.call(this);
26673         }
26674         
26675         if(!this.el || !this.getEditor()) {
26676            
26677            // this.getValue.defer(100,this); 
26678             return this.value;
26679         }
26680        
26681         
26682         var value=this.getEditor().GetData();
26683         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26684         return Roo.form.FCKeditor.superclass.getValue.call(this);
26685         
26686
26687     },
26688
26689     /**
26690      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26691      * @return {Mixed} value The field value
26692      */
26693     getRawValue : function()
26694     {
26695         if (this.frame && this.frame.dom.style.display == 'none') {
26696             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26697         }
26698         
26699         if(!this.el || !this.getEditor()) {
26700             //this.getRawValue.defer(100,this); 
26701             return this.value;
26702             return;
26703         }
26704         
26705         
26706         
26707         var value=this.getEditor().GetData();
26708         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26709         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26710          
26711     },
26712     
26713     setSize : function(w,h) {
26714         
26715         
26716         
26717         //if (this.frame && this.frame.dom.style.display == 'none') {
26718         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26719         //    return;
26720         //}
26721         //if(!this.el || !this.getEditor()) {
26722         //    this.setSize.defer(100,this, [w,h]); 
26723         //    return;
26724         //}
26725         
26726         
26727         
26728         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26729         
26730         this.frame.dom.setAttribute('width', w);
26731         this.frame.dom.setAttribute('height', h);
26732         this.frame.setSize(w,h);
26733         
26734     },
26735     
26736     toggleSourceEdit : function(value) {
26737         
26738       
26739          
26740         this.el.dom.style.display = value ? '' : 'none';
26741         this.frame.dom.style.display = value ?  'none' : '';
26742         
26743     },
26744     
26745     
26746     focus: function(tag)
26747     {
26748         if (this.frame.dom.style.display == 'none') {
26749             return Roo.form.FCKeditor.superclass.focus.call(this);
26750         }
26751         if(!this.el || !this.getEditor()) {
26752             this.focus.defer(100,this, [tag]); 
26753             return;
26754         }
26755         
26756         
26757         
26758         
26759         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26760         this.getEditor().Focus();
26761         if (tgs.length) {
26762             if (!this.getEditor().Selection.GetSelection()) {
26763                 this.focus.defer(100,this, [tag]); 
26764                 return;
26765             }
26766             
26767             
26768             var r = this.getEditor().EditorDocument.createRange();
26769             r.setStart(tgs[0],0);
26770             r.setEnd(tgs[0],0);
26771             this.getEditor().Selection.GetSelection().removeAllRanges();
26772             this.getEditor().Selection.GetSelection().addRange(r);
26773             this.getEditor().Focus();
26774         }
26775         
26776     },
26777     
26778     
26779     
26780     replaceTextarea : function()
26781     {
26782         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26783             return ;
26784         }
26785         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26786         //{
26787             // We must check the elements firstly using the Id and then the name.
26788         var oTextarea = document.getElementById( this.getId() );
26789         
26790         var colElementsByName = document.getElementsByName( this.getId() ) ;
26791          
26792         oTextarea.style.display = 'none' ;
26793
26794         if ( oTextarea.tabIndex ) {            
26795             this.TabIndex = oTextarea.tabIndex ;
26796         }
26797         
26798         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26799         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26800         this.frame = Roo.get(this.getId() + '___Frame')
26801     },
26802     
26803     _getConfigHtml : function()
26804     {
26805         var sConfig = '' ;
26806
26807         for ( var o in this.fckconfig ) {
26808             sConfig += sConfig.length > 0  ? '&amp;' : '';
26809             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26810         }
26811
26812         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26813     },
26814     
26815     
26816     _getIFrameHtml : function()
26817     {
26818         var sFile = 'fckeditor.html' ;
26819         /* no idea what this is about..
26820         try
26821         {
26822             if ( (/fcksource=true/i).test( window.top.location.search ) )
26823                 sFile = 'fckeditor.original.html' ;
26824         }
26825         catch (e) { 
26826         */
26827
26828         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26829         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26830         
26831         
26832         var html = '<iframe id="' + this.getId() +
26833             '___Frame" src="' + sLink +
26834             '" width="' + this.width +
26835             '" height="' + this.height + '"' +
26836             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26837             ' frameborder="0" scrolling="no"></iframe>' ;
26838
26839         return html ;
26840     },
26841     
26842     _insertHtmlBefore : function( html, element )
26843     {
26844         if ( element.insertAdjacentHTML )       {
26845             // IE
26846             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26847         } else { // Gecko
26848             var oRange = document.createRange() ;
26849             oRange.setStartBefore( element ) ;
26850             var oFragment = oRange.createContextualFragment( html );
26851             element.parentNode.insertBefore( oFragment, element ) ;
26852         }
26853     }
26854     
26855     
26856   
26857     
26858     
26859     
26860     
26861
26862 });
26863
26864 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26865
26866 function FCKeditor_OnComplete(editorInstance){
26867     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26868     f.fckEditor = editorInstance;
26869     //console.log("loaded");
26870     f.fireEvent('editorinit', f, editorInstance);
26871
26872   
26873
26874  
26875
26876
26877
26878
26879
26880
26881
26882
26883
26884
26885
26886
26887
26888
26889
26890 //<script type="text/javascript">
26891 /**
26892  * @class Roo.form.GridField
26893  * @extends Roo.form.Field
26894  * Embed a grid (or editable grid into a form)
26895  * STATUS ALPHA
26896  * 
26897  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26898  * it needs 
26899  * xgrid.store = Roo.data.Store
26900  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26901  * xgrid.store.reader = Roo.data.JsonReader 
26902  * 
26903  * 
26904  * @constructor
26905  * Creates a new GridField
26906  * @param {Object} config Configuration options
26907  */
26908 Roo.form.GridField = function(config){
26909     Roo.form.GridField.superclass.constructor.call(this, config);
26910      
26911 };
26912
26913 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26914     /**
26915      * @cfg {Number} width  - used to restrict width of grid..
26916      */
26917     width : 100,
26918     /**
26919      * @cfg {Number} height - used to restrict height of grid..
26920      */
26921     height : 50,
26922      /**
26923      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26924          * 
26925          *}
26926      */
26927     xgrid : false, 
26928     /**
26929      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26930      * {tag: "input", type: "checkbox", autocomplete: "off"})
26931      */
26932    // defaultAutoCreate : { tag: 'div' },
26933     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26934     /**
26935      * @cfg {String} addTitle Text to include for adding a title.
26936      */
26937     addTitle : false,
26938     //
26939     onResize : function(){
26940         Roo.form.Field.superclass.onResize.apply(this, arguments);
26941     },
26942
26943     initEvents : function(){
26944         // Roo.form.Checkbox.superclass.initEvents.call(this);
26945         // has no events...
26946        
26947     },
26948
26949
26950     getResizeEl : function(){
26951         return this.wrap;
26952     },
26953
26954     getPositionEl : function(){
26955         return this.wrap;
26956     },
26957
26958     // private
26959     onRender : function(ct, position){
26960         
26961         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26962         var style = this.style;
26963         delete this.style;
26964         
26965         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26966         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26967         this.viewEl = this.wrap.createChild({ tag: 'div' });
26968         if (style) {
26969             this.viewEl.applyStyles(style);
26970         }
26971         if (this.width) {
26972             this.viewEl.setWidth(this.width);
26973         }
26974         if (this.height) {
26975             this.viewEl.setHeight(this.height);
26976         }
26977         //if(this.inputValue !== undefined){
26978         //this.setValue(this.value);
26979         
26980         
26981         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26982         
26983         
26984         this.grid.render();
26985         this.grid.getDataSource().on('remove', this.refreshValue, this);
26986         this.grid.getDataSource().on('update', this.refreshValue, this);
26987         this.grid.on('afteredit', this.refreshValue, this);
26988  
26989     },
26990      
26991     
26992     /**
26993      * Sets the value of the item. 
26994      * @param {String} either an object  or a string..
26995      */
26996     setValue : function(v){
26997         //this.value = v;
26998         v = v || []; // empty set..
26999         // this does not seem smart - it really only affects memoryproxy grids..
27000         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27001             var ds = this.grid.getDataSource();
27002             // assumes a json reader..
27003             var data = {}
27004             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27005             ds.loadData( data);
27006         }
27007         // clear selection so it does not get stale.
27008         if (this.grid.sm) { 
27009             this.grid.sm.clearSelections();
27010         }
27011         
27012         Roo.form.GridField.superclass.setValue.call(this, v);
27013         this.refreshValue();
27014         // should load data in the grid really....
27015     },
27016     
27017     // private
27018     refreshValue: function() {
27019          var val = [];
27020         this.grid.getDataSource().each(function(r) {
27021             val.push(r.data);
27022         });
27023         this.el.dom.value = Roo.encode(val);
27024     }
27025     
27026      
27027     
27028     
27029 });/*
27030  * Based on:
27031  * Ext JS Library 1.1.1
27032  * Copyright(c) 2006-2007, Ext JS, LLC.
27033  *
27034  * Originally Released Under LGPL - original licence link has changed is not relivant.
27035  *
27036  * Fork - LGPL
27037  * <script type="text/javascript">
27038  */
27039 /**
27040  * @class Roo.form.DisplayField
27041  * @extends Roo.form.Field
27042  * A generic Field to display non-editable data.
27043  * @cfg {Boolean} closable (true|false) default false
27044  * @constructor
27045  * Creates a new Display Field item.
27046  * @param {Object} config Configuration options
27047  */
27048 Roo.form.DisplayField = function(config){
27049     Roo.form.DisplayField.superclass.constructor.call(this, config);
27050     
27051     this.addEvents({
27052         /**
27053          * @event close
27054          * Fires after the click the close btn
27055              * @param {Roo.form.DisplayField} this
27056              */
27057         close : true
27058     });
27059 };
27060
27061 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27062     inputType:      'hidden',
27063     allowBlank:     true,
27064     readOnly:         true,
27065     
27066  
27067     /**
27068      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27069      */
27070     focusClass : undefined,
27071     /**
27072      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27073      */
27074     fieldClass: 'x-form-field',
27075     
27076      /**
27077      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27078      */
27079     valueRenderer: undefined,
27080     
27081     width: 100,
27082     /**
27083      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27084      * {tag: "input", type: "checkbox", autocomplete: "off"})
27085      */
27086      
27087  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27088  
27089     closable : false,
27090     
27091     onResize : function(){
27092         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27093         
27094     },
27095
27096     initEvents : function(){
27097         // Roo.form.Checkbox.superclass.initEvents.call(this);
27098         // has no events...
27099         
27100         if(this.closable){
27101             this.closeEl.on('click', this.onClose, this);
27102         }
27103        
27104     },
27105
27106
27107     getResizeEl : function(){
27108         return this.wrap;
27109     },
27110
27111     getPositionEl : function(){
27112         return this.wrap;
27113     },
27114
27115     // private
27116     onRender : function(ct, position){
27117         
27118         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27119         //if(this.inputValue !== undefined){
27120         this.wrap = this.el.wrap();
27121         
27122         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27123         
27124         if(this.closable){
27125             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27126         }
27127         
27128         if (this.bodyStyle) {
27129             this.viewEl.applyStyles(this.bodyStyle);
27130         }
27131         //this.viewEl.setStyle('padding', '2px');
27132         
27133         this.setValue(this.value);
27134         
27135     },
27136 /*
27137     // private
27138     initValue : Roo.emptyFn,
27139
27140   */
27141
27142         // private
27143     onClick : function(){
27144         
27145     },
27146
27147     /**
27148      * Sets the checked state of the checkbox.
27149      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27150      */
27151     setValue : function(v){
27152         this.value = v;
27153         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27154         // this might be called before we have a dom element..
27155         if (!this.viewEl) {
27156             return;
27157         }
27158         this.viewEl.dom.innerHTML = html;
27159         Roo.form.DisplayField.superclass.setValue.call(this, v);
27160
27161     },
27162     
27163     onClose : function(e)
27164     {
27165         e.preventDefault();
27166         
27167         this.fireEvent('close', this);
27168     }
27169 });/*
27170  * 
27171  * Licence- LGPL
27172  * 
27173  */
27174
27175 /**
27176  * @class Roo.form.DayPicker
27177  * @extends Roo.form.Field
27178  * A Day picker show [M] [T] [W] ....
27179  * @constructor
27180  * Creates a new Day Picker
27181  * @param {Object} config Configuration options
27182  */
27183 Roo.form.DayPicker= function(config){
27184     Roo.form.DayPicker.superclass.constructor.call(this, config);
27185      
27186 };
27187
27188 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27189     /**
27190      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27191      */
27192     focusClass : undefined,
27193     /**
27194      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27195      */
27196     fieldClass: "x-form-field",
27197    
27198     /**
27199      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27200      * {tag: "input", type: "checkbox", autocomplete: "off"})
27201      */
27202     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27203     
27204    
27205     actionMode : 'viewEl', 
27206     //
27207     // private
27208  
27209     inputType : 'hidden',
27210     
27211      
27212     inputElement: false, // real input element?
27213     basedOn: false, // ????
27214     
27215     isFormField: true, // not sure where this is needed!!!!
27216
27217     onResize : function(){
27218         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27219         if(!this.boxLabel){
27220             this.el.alignTo(this.wrap, 'c-c');
27221         }
27222     },
27223
27224     initEvents : function(){
27225         Roo.form.Checkbox.superclass.initEvents.call(this);
27226         this.el.on("click", this.onClick,  this);
27227         this.el.on("change", this.onClick,  this);
27228     },
27229
27230
27231     getResizeEl : function(){
27232         return this.wrap;
27233     },
27234
27235     getPositionEl : function(){
27236         return this.wrap;
27237     },
27238
27239     
27240     // private
27241     onRender : function(ct, position){
27242         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27243        
27244         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27245         
27246         var r1 = '<table><tr>';
27247         var r2 = '<tr class="x-form-daypick-icons">';
27248         for (var i=0; i < 7; i++) {
27249             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27250             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27251         }
27252         
27253         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27254         viewEl.select('img').on('click', this.onClick, this);
27255         this.viewEl = viewEl;   
27256         
27257         
27258         // this will not work on Chrome!!!
27259         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27260         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27261         
27262         
27263           
27264
27265     },
27266
27267     // private
27268     initValue : Roo.emptyFn,
27269
27270     /**
27271      * Returns the checked state of the checkbox.
27272      * @return {Boolean} True if checked, else false
27273      */
27274     getValue : function(){
27275         return this.el.dom.value;
27276         
27277     },
27278
27279         // private
27280     onClick : function(e){ 
27281         //this.setChecked(!this.checked);
27282         Roo.get(e.target).toggleClass('x-menu-item-checked');
27283         this.refreshValue();
27284         //if(this.el.dom.checked != this.checked){
27285         //    this.setValue(this.el.dom.checked);
27286        // }
27287     },
27288     
27289     // private
27290     refreshValue : function()
27291     {
27292         var val = '';
27293         this.viewEl.select('img',true).each(function(e,i,n)  {
27294             val += e.is(".x-menu-item-checked") ? String(n) : '';
27295         });
27296         this.setValue(val, true);
27297     },
27298
27299     /**
27300      * Sets the checked state of the checkbox.
27301      * On is always based on a string comparison between inputValue and the param.
27302      * @param {Boolean/String} value - the value to set 
27303      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27304      */
27305     setValue : function(v,suppressEvent){
27306         if (!this.el.dom) {
27307             return;
27308         }
27309         var old = this.el.dom.value ;
27310         this.el.dom.value = v;
27311         if (suppressEvent) {
27312             return ;
27313         }
27314          
27315         // update display..
27316         this.viewEl.select('img',true).each(function(e,i,n)  {
27317             
27318             var on = e.is(".x-menu-item-checked");
27319             var newv = v.indexOf(String(n)) > -1;
27320             if (on != newv) {
27321                 e.toggleClass('x-menu-item-checked');
27322             }
27323             
27324         });
27325         
27326         
27327         this.fireEvent('change', this, v, old);
27328         
27329         
27330     },
27331    
27332     // handle setting of hidden value by some other method!!?!?
27333     setFromHidden: function()
27334     {
27335         if(!this.el){
27336             return;
27337         }
27338         //console.log("SET FROM HIDDEN");
27339         //alert('setFrom hidden');
27340         this.setValue(this.el.dom.value);
27341     },
27342     
27343     onDestroy : function()
27344     {
27345         if(this.viewEl){
27346             Roo.get(this.viewEl).remove();
27347         }
27348          
27349         Roo.form.DayPicker.superclass.onDestroy.call(this);
27350     }
27351
27352 });/*
27353  * RooJS Library 1.1.1
27354  * Copyright(c) 2008-2011  Alan Knowles
27355  *
27356  * License - LGPL
27357  */
27358  
27359
27360 /**
27361  * @class Roo.form.ComboCheck
27362  * @extends Roo.form.ComboBox
27363  * A combobox for multiple select items.
27364  *
27365  * FIXME - could do with a reset button..
27366  * 
27367  * @constructor
27368  * Create a new ComboCheck
27369  * @param {Object} config Configuration options
27370  */
27371 Roo.form.ComboCheck = function(config){
27372     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27373     // should verify some data...
27374     // like
27375     // hiddenName = required..
27376     // displayField = required
27377     // valudField == required
27378     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27379     var _t = this;
27380     Roo.each(req, function(e) {
27381         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27382             throw "Roo.form.ComboCheck : missing value for: " + e;
27383         }
27384     });
27385     
27386     
27387 };
27388
27389 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27390      
27391      
27392     editable : false,
27393      
27394     selectedClass: 'x-menu-item-checked', 
27395     
27396     // private
27397     onRender : function(ct, position){
27398         var _t = this;
27399         
27400         
27401         
27402         if(!this.tpl){
27403             var cls = 'x-combo-list';
27404
27405             
27406             this.tpl =  new Roo.Template({
27407                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27408                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27409                    '<span>{' + this.displayField + '}</span>' +
27410                     '</div>' 
27411                 
27412             });
27413         }
27414  
27415         
27416         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27417         this.view.singleSelect = false;
27418         this.view.multiSelect = true;
27419         this.view.toggleSelect = true;
27420         this.pageTb.add(new Roo.Toolbar.Fill(), {
27421             
27422             text: 'Done',
27423             handler: function()
27424             {
27425                 _t.collapse();
27426             }
27427         });
27428     },
27429     
27430     onViewOver : function(e, t){
27431         // do nothing...
27432         return;
27433         
27434     },
27435     
27436     onViewClick : function(doFocus,index){
27437         return;
27438         
27439     },
27440     select: function () {
27441         //Roo.log("SELECT CALLED");
27442     },
27443      
27444     selectByValue : function(xv, scrollIntoView){
27445         var ar = this.getValueArray();
27446         var sels = [];
27447         
27448         Roo.each(ar, function(v) {
27449             if(v === undefined || v === null){
27450                 return;
27451             }
27452             var r = this.findRecord(this.valueField, v);
27453             if(r){
27454                 sels.push(this.store.indexOf(r))
27455                 
27456             }
27457         },this);
27458         this.view.select(sels);
27459         return false;
27460     },
27461     
27462     
27463     
27464     onSelect : function(record, index){
27465        // Roo.log("onselect Called");
27466        // this is only called by the clear button now..
27467         this.view.clearSelections();
27468         this.setValue('[]');
27469         if (this.value != this.valueBefore) {
27470             this.fireEvent('change', this, this.value, this.valueBefore);
27471             this.valueBefore = this.value;
27472         }
27473     },
27474     getValueArray : function()
27475     {
27476         var ar = [] ;
27477         
27478         try {
27479             //Roo.log(this.value);
27480             if (typeof(this.value) == 'undefined') {
27481                 return [];
27482             }
27483             var ar = Roo.decode(this.value);
27484             return  ar instanceof Array ? ar : []; //?? valid?
27485             
27486         } catch(e) {
27487             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27488             return [];
27489         }
27490          
27491     },
27492     expand : function ()
27493     {
27494         
27495         Roo.form.ComboCheck.superclass.expand.call(this);
27496         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27497         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27498         
27499
27500     },
27501     
27502     collapse : function(){
27503         Roo.form.ComboCheck.superclass.collapse.call(this);
27504         var sl = this.view.getSelectedIndexes();
27505         var st = this.store;
27506         var nv = [];
27507         var tv = [];
27508         var r;
27509         Roo.each(sl, function(i) {
27510             r = st.getAt(i);
27511             nv.push(r.get(this.valueField));
27512         },this);
27513         this.setValue(Roo.encode(nv));
27514         if (this.value != this.valueBefore) {
27515
27516             this.fireEvent('change', this, this.value, this.valueBefore);
27517             this.valueBefore = this.value;
27518         }
27519         
27520     },
27521     
27522     setValue : function(v){
27523         // Roo.log(v);
27524         this.value = v;
27525         
27526         var vals = this.getValueArray();
27527         var tv = [];
27528         Roo.each(vals, function(k) {
27529             var r = this.findRecord(this.valueField, k);
27530             if(r){
27531                 tv.push(r.data[this.displayField]);
27532             }else if(this.valueNotFoundText !== undefined){
27533                 tv.push( this.valueNotFoundText );
27534             }
27535         },this);
27536        // Roo.log(tv);
27537         
27538         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27539         this.hiddenField.value = v;
27540         this.value = v;
27541     }
27542     
27543 });/*
27544  * Based on:
27545  * Ext JS Library 1.1.1
27546  * Copyright(c) 2006-2007, Ext JS, LLC.
27547  *
27548  * Originally Released Under LGPL - original licence link has changed is not relivant.
27549  *
27550  * Fork - LGPL
27551  * <script type="text/javascript">
27552  */
27553  
27554 /**
27555  * @class Roo.form.Signature
27556  * @extends Roo.form.Field
27557  * Signature field.  
27558  * @constructor
27559  * 
27560  * @param {Object} config Configuration options
27561  */
27562
27563 Roo.form.Signature = function(config){
27564     Roo.form.Signature.superclass.constructor.call(this, config);
27565     
27566     this.addEvents({// not in used??
27567          /**
27568          * @event confirm
27569          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27570              * @param {Roo.form.Signature} combo This combo box
27571              */
27572         'confirm' : true,
27573         /**
27574          * @event reset
27575          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27576              * @param {Roo.form.ComboBox} combo This combo box
27577              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27578              */
27579         'reset' : true
27580     });
27581 };
27582
27583 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27584     /**
27585      * @cfg {Object} labels Label to use when rendering a form.
27586      * defaults to 
27587      * labels : { 
27588      *      clear : "Clear",
27589      *      confirm : "Confirm"
27590      *  }
27591      */
27592     labels : { 
27593         clear : "Clear",
27594         confirm : "Confirm"
27595     },
27596     /**
27597      * @cfg {Number} width The signature panel width (defaults to 300)
27598      */
27599     width: 300,
27600     /**
27601      * @cfg {Number} height The signature panel height (defaults to 100)
27602      */
27603     height : 100,
27604     /**
27605      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27606      */
27607     allowBlank : false,
27608     
27609     //private
27610     // {Object} signPanel The signature SVG panel element (defaults to {})
27611     signPanel : {},
27612     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27613     isMouseDown : false,
27614     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27615     isConfirmed : false,
27616     // {String} signatureTmp SVG mapping string (defaults to empty string)
27617     signatureTmp : '',
27618     
27619     
27620     defaultAutoCreate : { // modified by initCompnoent..
27621         tag: "input",
27622         type:"hidden"
27623     },
27624
27625     // private
27626     onRender : function(ct, position){
27627         
27628         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27629         
27630         this.wrap = this.el.wrap({
27631             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27632         });
27633         
27634         this.createToolbar(this);
27635         this.signPanel = this.wrap.createChild({
27636                 tag: 'div',
27637                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27638             }, this.el
27639         );
27640             
27641         this.svgID = Roo.id();
27642         this.svgEl = this.signPanel.createChild({
27643               xmlns : 'http://www.w3.org/2000/svg',
27644               tag : 'svg',
27645               id : this.svgID + "-svg",
27646               width: this.width,
27647               height: this.height,
27648               viewBox: '0 0 '+this.width+' '+this.height,
27649               cn : [
27650                 {
27651                     tag: "rect",
27652                     id: this.svgID + "-svg-r",
27653                     width: this.width,
27654                     height: this.height,
27655                     fill: "#ffa"
27656                 },
27657                 {
27658                     tag: "line",
27659                     id: this.svgID + "-svg-l",
27660                     x1: "0", // start
27661                     y1: (this.height*0.8), // start set the line in 80% of height
27662                     x2: this.width, // end
27663                     y2: (this.height*0.8), // end set the line in 80% of height
27664                     'stroke': "#666",
27665                     'stroke-width': "1",
27666                     'stroke-dasharray': "3",
27667                     'shape-rendering': "crispEdges",
27668                     'pointer-events': "none"
27669                 },
27670                 {
27671                     tag: "path",
27672                     id: this.svgID + "-svg-p",
27673                     'stroke': "navy",
27674                     'stroke-width': "3",
27675                     'fill': "none",
27676                     'pointer-events': 'none'
27677                 }
27678               ]
27679         });
27680         this.createSVG();
27681         this.svgBox = this.svgEl.dom.getScreenCTM();
27682     },
27683     createSVG : function(){ 
27684         var svg = this.signPanel;
27685         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27686         var t = this;
27687
27688         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27689         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27690         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27691         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27692         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27693         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27694         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27695         
27696     },
27697     isTouchEvent : function(e){
27698         return e.type.match(/^touch/);
27699     },
27700     getCoords : function (e) {
27701         var pt    = this.svgEl.dom.createSVGPoint();
27702         pt.x = e.clientX; 
27703         pt.y = e.clientY;
27704         if (this.isTouchEvent(e)) {
27705             pt.x =  e.targetTouches[0].clientX;
27706             pt.y = e.targetTouches[0].clientY;
27707         }
27708         var a = this.svgEl.dom.getScreenCTM();
27709         var b = a.inverse();
27710         var mx = pt.matrixTransform(b);
27711         return mx.x + ',' + mx.y;
27712     },
27713     //mouse event headler 
27714     down : function (e) {
27715         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27716         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27717         
27718         this.isMouseDown = true;
27719         
27720         e.preventDefault();
27721     },
27722     move : function (e) {
27723         if (this.isMouseDown) {
27724             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27725             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27726         }
27727         
27728         e.preventDefault();
27729     },
27730     up : function (e) {
27731         this.isMouseDown = false;
27732         var sp = this.signatureTmp.split(' ');
27733         
27734         if(sp.length > 1){
27735             if(!sp[sp.length-2].match(/^L/)){
27736                 sp.pop();
27737                 sp.pop();
27738                 sp.push("");
27739                 this.signatureTmp = sp.join(" ");
27740             }
27741         }
27742         if(this.getValue() != this.signatureTmp){
27743             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27744             this.isConfirmed = false;
27745         }
27746         e.preventDefault();
27747     },
27748     
27749     /**
27750      * Protected method that will not generally be called directly. It
27751      * is called when the editor creates its toolbar. Override this method if you need to
27752      * add custom toolbar buttons.
27753      * @param {HtmlEditor} editor
27754      */
27755     createToolbar : function(editor){
27756          function btn(id, toggle, handler){
27757             var xid = fid + '-'+ id ;
27758             return {
27759                 id : xid,
27760                 cmd : id,
27761                 cls : 'x-btn-icon x-edit-'+id,
27762                 enableToggle:toggle !== false,
27763                 scope: editor, // was editor...
27764                 handler:handler||editor.relayBtnCmd,
27765                 clickEvent:'mousedown',
27766                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27767                 tabIndex:-1
27768             };
27769         }
27770         
27771         
27772         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27773         this.tb = tb;
27774         this.tb.add(
27775            {
27776                 cls : ' x-signature-btn x-signature-'+id,
27777                 scope: editor, // was editor...
27778                 handler: this.reset,
27779                 clickEvent:'mousedown',
27780                 text: this.labels.clear
27781             },
27782             {
27783                  xtype : 'Fill',
27784                  xns: Roo.Toolbar
27785             }, 
27786             {
27787                 cls : '  x-signature-btn x-signature-'+id,
27788                 scope: editor, // was editor...
27789                 handler: this.confirmHandler,
27790                 clickEvent:'mousedown',
27791                 text: this.labels.confirm
27792             }
27793         );
27794     
27795     },
27796     //public
27797     /**
27798      * when user is clicked confirm then show this image.....
27799      * 
27800      * @return {String} Image Data URI
27801      */
27802     getImageDataURI : function(){
27803         var svg = this.svgEl.dom.parentNode.innerHTML;
27804         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27805         return src; 
27806     },
27807     /**
27808      * 
27809      * @return {Boolean} this.isConfirmed
27810      */
27811     getConfirmed : function(){
27812         return this.isConfirmed;
27813     },
27814     /**
27815      * 
27816      * @return {Number} this.width
27817      */
27818     getWidth : function(){
27819         return this.width;
27820     },
27821     /**
27822      * 
27823      * @return {Number} this.height
27824      */
27825     getHeight : function(){
27826         return this.height;
27827     },
27828     // private
27829     getSignature : function(){
27830         return this.signatureTmp;
27831     },
27832     // private
27833     reset : function(){
27834         this.signatureTmp = '';
27835         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27836         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27837         this.isConfirmed = false;
27838         Roo.form.Signature.superclass.reset.call(this);
27839     },
27840     setSignature : function(s){
27841         this.signatureTmp = s;
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', s);
27844         this.setValue(s);
27845         this.isConfirmed = false;
27846         Roo.form.Signature.superclass.reset.call(this);
27847     }, 
27848     test : function(){
27849 //        Roo.log(this.signPanel.dom.contentWindow.up())
27850     },
27851     //private
27852     setConfirmed : function(){
27853         
27854         
27855         
27856 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27857     },
27858     // private
27859     confirmHandler : function(){
27860         if(!this.getSignature()){
27861             return;
27862         }
27863         
27864         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27865         this.setValue(this.getSignature());
27866         this.isConfirmed = true;
27867         
27868         this.fireEvent('confirm', this);
27869     },
27870     // private
27871     // Subclasses should provide the validation implementation by overriding this
27872     validateValue : function(value){
27873         if(this.allowBlank){
27874             return true;
27875         }
27876         
27877         if(this.isConfirmed){
27878             return true;
27879         }
27880         return false;
27881     }
27882 });/*
27883  * Based on:
27884  * Ext JS Library 1.1.1
27885  * Copyright(c) 2006-2007, Ext JS, LLC.
27886  *
27887  * Originally Released Under LGPL - original licence link has changed is not relivant.
27888  *
27889  * Fork - LGPL
27890  * <script type="text/javascript">
27891  */
27892  
27893
27894 /**
27895  * @class Roo.form.ComboBox
27896  * @extends Roo.form.TriggerField
27897  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27898  * @constructor
27899  * Create a new ComboBox.
27900  * @param {Object} config Configuration options
27901  */
27902 Roo.form.Select = function(config){
27903     Roo.form.Select.superclass.constructor.call(this, config);
27904      
27905 };
27906
27907 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27908     /**
27909      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27910      */
27911     /**
27912      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27913      * rendering into an Roo.Editor, defaults to false)
27914      */
27915     /**
27916      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27917      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27918      */
27919     /**
27920      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27921      */
27922     /**
27923      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27924      * the dropdown list (defaults to undefined, with no header element)
27925      */
27926
27927      /**
27928      * @cfg {String/Roo.Template} tpl The template to use to render the output
27929      */
27930      
27931     // private
27932     defaultAutoCreate : {tag: "select"  },
27933     /**
27934      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27935      */
27936     listWidth: undefined,
27937     /**
27938      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27939      * mode = 'remote' or 'text' if mode = 'local')
27940      */
27941     displayField: undefined,
27942     /**
27943      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27944      * mode = 'remote' or 'value' if mode = 'local'). 
27945      * Note: use of a valueField requires the user make a selection
27946      * in order for a value to be mapped.
27947      */
27948     valueField: undefined,
27949     
27950     
27951     /**
27952      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27953      * field's data value (defaults to the underlying DOM element's name)
27954      */
27955     hiddenName: undefined,
27956     /**
27957      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27958      */
27959     listClass: '',
27960     /**
27961      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27962      */
27963     selectedClass: 'x-combo-selected',
27964     /**
27965      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27966      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27967      * which displays a downward arrow icon).
27968      */
27969     triggerClass : 'x-form-arrow-trigger',
27970     /**
27971      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27972      */
27973     shadow:'sides',
27974     /**
27975      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27976      * anchor positions (defaults to 'tl-bl')
27977      */
27978     listAlign: 'tl-bl?',
27979     /**
27980      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27981      */
27982     maxHeight: 300,
27983     /**
27984      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27985      * query specified by the allQuery config option (defaults to 'query')
27986      */
27987     triggerAction: 'query',
27988     /**
27989      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27990      * (defaults to 4, does not apply if editable = false)
27991      */
27992     minChars : 4,
27993     /**
27994      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27995      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27996      */
27997     typeAhead: false,
27998     /**
27999      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28000      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28001      */
28002     queryDelay: 500,
28003     /**
28004      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28005      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28006      */
28007     pageSize: 0,
28008     /**
28009      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28010      * when editable = true (defaults to false)
28011      */
28012     selectOnFocus:false,
28013     /**
28014      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28015      */
28016     queryParam: 'query',
28017     /**
28018      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28019      * when mode = 'remote' (defaults to 'Loading...')
28020      */
28021     loadingText: 'Loading...',
28022     /**
28023      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28024      */
28025     resizable: false,
28026     /**
28027      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28028      */
28029     handleHeight : 8,
28030     /**
28031      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28032      * traditional select (defaults to true)
28033      */
28034     editable: true,
28035     /**
28036      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28037      */
28038     allQuery: '',
28039     /**
28040      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28041      */
28042     mode: 'remote',
28043     /**
28044      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28045      * listWidth has a higher value)
28046      */
28047     minListWidth : 70,
28048     /**
28049      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28050      * allow the user to set arbitrary text into the field (defaults to false)
28051      */
28052     forceSelection:false,
28053     /**
28054      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28055      * if typeAhead = true (defaults to 250)
28056      */
28057     typeAheadDelay : 250,
28058     /**
28059      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28060      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28061      */
28062     valueNotFoundText : undefined,
28063     
28064     /**
28065      * @cfg {String} defaultValue The value displayed after loading the store.
28066      */
28067     defaultValue: '',
28068     
28069     /**
28070      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28071      */
28072     blockFocus : false,
28073     
28074     /**
28075      * @cfg {Boolean} disableClear Disable showing of clear button.
28076      */
28077     disableClear : false,
28078     /**
28079      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28080      */
28081     alwaysQuery : false,
28082     
28083     //private
28084     addicon : false,
28085     editicon: false,
28086     
28087     // element that contains real text value.. (when hidden is used..)
28088      
28089     // private
28090     onRender : function(ct, position){
28091         Roo.form.Field.prototype.onRender.call(this, ct, position);
28092         
28093         if(this.store){
28094             this.store.on('beforeload', this.onBeforeLoad, this);
28095             this.store.on('load', this.onLoad, this);
28096             this.store.on('loadexception', this.onLoadException, this);
28097             this.store.load({});
28098         }
28099         
28100         
28101         
28102     },
28103
28104     // private
28105     initEvents : function(){
28106         //Roo.form.ComboBox.superclass.initEvents.call(this);
28107  
28108     },
28109
28110     onDestroy : function(){
28111        
28112         if(this.store){
28113             this.store.un('beforeload', this.onBeforeLoad, this);
28114             this.store.un('load', this.onLoad, this);
28115             this.store.un('loadexception', this.onLoadException, this);
28116         }
28117         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28118     },
28119
28120     // private
28121     fireKey : function(e){
28122         if(e.isNavKeyPress() && !this.list.isVisible()){
28123             this.fireEvent("specialkey", this, e);
28124         }
28125     },
28126
28127     // private
28128     onResize: function(w, h){
28129         
28130         return; 
28131     
28132         
28133     },
28134
28135     /**
28136      * Allow or prevent the user from directly editing the field text.  If false is passed,
28137      * the user will only be able to select from the items defined in the dropdown list.  This method
28138      * is the runtime equivalent of setting the 'editable' config option at config time.
28139      * @param {Boolean} value True to allow the user to directly edit the field text
28140      */
28141     setEditable : function(value){
28142          
28143     },
28144
28145     // private
28146     onBeforeLoad : function(){
28147         
28148         Roo.log("Select before load");
28149         return;
28150     
28151         this.innerList.update(this.loadingText ?
28152                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28153         //this.restrictHeight();
28154         this.selectedIndex = -1;
28155     },
28156
28157     // private
28158     onLoad : function(){
28159
28160     
28161         var dom = this.el.dom;
28162         dom.innerHTML = '';
28163          var od = dom.ownerDocument;
28164          
28165         if (this.emptyText) {
28166             var op = od.createElement('option');
28167             op.setAttribute('value', '');
28168             op.innerHTML = String.format('{0}', this.emptyText);
28169             dom.appendChild(op);
28170         }
28171         if(this.store.getCount() > 0){
28172            
28173             var vf = this.valueField;
28174             var df = this.displayField;
28175             this.store.data.each(function(r) {
28176                 // which colmsn to use... testing - cdoe / title..
28177                 var op = od.createElement('option');
28178                 op.setAttribute('value', r.data[vf]);
28179                 op.innerHTML = String.format('{0}', r.data[df]);
28180                 dom.appendChild(op);
28181             });
28182             if (typeof(this.defaultValue != 'undefined')) {
28183                 this.setValue(this.defaultValue);
28184             }
28185             
28186              
28187         }else{
28188             //this.onEmptyResults();
28189         }
28190         //this.el.focus();
28191     },
28192     // private
28193     onLoadException : function()
28194     {
28195         dom.innerHTML = '';
28196             
28197         Roo.log("Select on load exception");
28198         return;
28199     
28200         this.collapse();
28201         Roo.log(this.store.reader.jsonData);
28202         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28203             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28204         }
28205         
28206         
28207     },
28208     // private
28209     onTypeAhead : function(){
28210          
28211     },
28212
28213     // private
28214     onSelect : function(record, index){
28215         Roo.log('on select?');
28216         return;
28217         if(this.fireEvent('beforeselect', this, record, index) !== false){
28218             this.setFromData(index > -1 ? record.data : false);
28219             this.collapse();
28220             this.fireEvent('select', this, record, index);
28221         }
28222     },
28223
28224     /**
28225      * Returns the currently selected field value or empty string if no value is set.
28226      * @return {String} value The selected value
28227      */
28228     getValue : function(){
28229         var dom = this.el.dom;
28230         this.value = dom.options[dom.selectedIndex].value;
28231         return this.value;
28232         
28233     },
28234
28235     /**
28236      * Clears any text/value currently set in the field
28237      */
28238     clearValue : function(){
28239         this.value = '';
28240         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28241         
28242     },
28243
28244     /**
28245      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28246      * will be displayed in the field.  If the value does not match the data value of an existing item,
28247      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28248      * Otherwise the field will be blank (although the value will still be set).
28249      * @param {String} value The value to match
28250      */
28251     setValue : function(v){
28252         var d = this.el.dom;
28253         for (var i =0; i < d.options.length;i++) {
28254             if (v == d.options[i].value) {
28255                 d.selectedIndex = i;
28256                 this.value = v;
28257                 return;
28258             }
28259         }
28260         this.clearValue();
28261     },
28262     /**
28263      * @property {Object} the last set data for the element
28264      */
28265     
28266     lastData : false,
28267     /**
28268      * Sets the value of the field based on a object which is related to the record format for the store.
28269      * @param {Object} value the value to set as. or false on reset?
28270      */
28271     setFromData : function(o){
28272         Roo.log('setfrom data?');
28273          
28274         
28275         
28276     },
28277     // private
28278     reset : function(){
28279         this.clearValue();
28280     },
28281     // private
28282     findRecord : function(prop, value){
28283         
28284         return false;
28285     
28286         var record;
28287         if(this.store.getCount() > 0){
28288             this.store.each(function(r){
28289                 if(r.data[prop] == value){
28290                     record = r;
28291                     return false;
28292                 }
28293                 return true;
28294             });
28295         }
28296         return record;
28297     },
28298     
28299     getName: function()
28300     {
28301         // returns hidden if it's set..
28302         if (!this.rendered) {return ''};
28303         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28304         
28305     },
28306      
28307
28308     
28309
28310     // private
28311     onEmptyResults : function(){
28312         Roo.log('empty results');
28313         //this.collapse();
28314     },
28315
28316     /**
28317      * Returns true if the dropdown list is expanded, else false.
28318      */
28319     isExpanded : function(){
28320         return false;
28321     },
28322
28323     /**
28324      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28325      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28326      * @param {String} value The data value of the item to select
28327      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28328      * selected item if it is not currently in view (defaults to true)
28329      * @return {Boolean} True if the value matched an item in the list, else false
28330      */
28331     selectByValue : function(v, scrollIntoView){
28332         Roo.log('select By Value');
28333         return false;
28334     
28335         if(v !== undefined && v !== null){
28336             var r = this.findRecord(this.valueField || this.displayField, v);
28337             if(r){
28338                 this.select(this.store.indexOf(r), scrollIntoView);
28339                 return true;
28340             }
28341         }
28342         return false;
28343     },
28344
28345     /**
28346      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28347      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28348      * @param {Number} index The zero-based index of the list item to select
28349      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28350      * selected item if it is not currently in view (defaults to true)
28351      */
28352     select : function(index, scrollIntoView){
28353         Roo.log('select ');
28354         return  ;
28355         
28356         this.selectedIndex = index;
28357         this.view.select(index);
28358         if(scrollIntoView !== false){
28359             var el = this.view.getNode(index);
28360             if(el){
28361                 this.innerList.scrollChildIntoView(el, false);
28362             }
28363         }
28364     },
28365
28366       
28367
28368     // private
28369     validateBlur : function(){
28370         
28371         return;
28372         
28373     },
28374
28375     // private
28376     initQuery : function(){
28377         this.doQuery(this.getRawValue());
28378     },
28379
28380     // private
28381     doForce : function(){
28382         if(this.el.dom.value.length > 0){
28383             this.el.dom.value =
28384                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28385              
28386         }
28387     },
28388
28389     /**
28390      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28391      * query allowing the query action to be canceled if needed.
28392      * @param {String} query The SQL query to execute
28393      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28394      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28395      * saved in the current store (defaults to false)
28396      */
28397     doQuery : function(q, forceAll){
28398         
28399         Roo.log('doQuery?');
28400         if(q === undefined || q === null){
28401             q = '';
28402         }
28403         var qe = {
28404             query: q,
28405             forceAll: forceAll,
28406             combo: this,
28407             cancel:false
28408         };
28409         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28410             return false;
28411         }
28412         q = qe.query;
28413         forceAll = qe.forceAll;
28414         if(forceAll === true || (q.length >= this.minChars)){
28415             if(this.lastQuery != q || this.alwaysQuery){
28416                 this.lastQuery = q;
28417                 if(this.mode == 'local'){
28418                     this.selectedIndex = -1;
28419                     if(forceAll){
28420                         this.store.clearFilter();
28421                     }else{
28422                         this.store.filter(this.displayField, q);
28423                     }
28424                     this.onLoad();
28425                 }else{
28426                     this.store.baseParams[this.queryParam] = q;
28427                     this.store.load({
28428                         params: this.getParams(q)
28429                     });
28430                     this.expand();
28431                 }
28432             }else{
28433                 this.selectedIndex = -1;
28434                 this.onLoad();   
28435             }
28436         }
28437     },
28438
28439     // private
28440     getParams : function(q){
28441         var p = {};
28442         //p[this.queryParam] = q;
28443         if(this.pageSize){
28444             p.start = 0;
28445             p.limit = this.pageSize;
28446         }
28447         return p;
28448     },
28449
28450     /**
28451      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28452      */
28453     collapse : function(){
28454         
28455     },
28456
28457     // private
28458     collapseIf : function(e){
28459         
28460     },
28461
28462     /**
28463      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28464      */
28465     expand : function(){
28466         
28467     } ,
28468
28469     // private
28470      
28471
28472     /** 
28473     * @cfg {Boolean} grow 
28474     * @hide 
28475     */
28476     /** 
28477     * @cfg {Number} growMin 
28478     * @hide 
28479     */
28480     /** 
28481     * @cfg {Number} growMax 
28482     * @hide 
28483     */
28484     /**
28485      * @hide
28486      * @method autoSize
28487      */
28488     
28489     setWidth : function()
28490     {
28491         
28492     },
28493     getResizeEl : function(){
28494         return this.el;
28495     }
28496 });//<script type="text/javasscript">
28497  
28498
28499 /**
28500  * @class Roo.DDView
28501  * A DnD enabled version of Roo.View.
28502  * @param {Element/String} container The Element in which to create the View.
28503  * @param {String} tpl The template string used to create the markup for each element of the View
28504  * @param {Object} config The configuration properties. These include all the config options of
28505  * {@link Roo.View} plus some specific to this class.<br>
28506  * <p>
28507  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28508  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28509  * <p>
28510  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28511 .x-view-drag-insert-above {
28512         border-top:1px dotted #3366cc;
28513 }
28514 .x-view-drag-insert-below {
28515         border-bottom:1px dotted #3366cc;
28516 }
28517 </code></pre>
28518  * 
28519  */
28520  
28521 Roo.DDView = function(container, tpl, config) {
28522     Roo.DDView.superclass.constructor.apply(this, arguments);
28523     this.getEl().setStyle("outline", "0px none");
28524     this.getEl().unselectable();
28525     if (this.dragGroup) {
28526         this.setDraggable(this.dragGroup.split(","));
28527     }
28528     if (this.dropGroup) {
28529         this.setDroppable(this.dropGroup.split(","));
28530     }
28531     if (this.deletable) {
28532         this.setDeletable();
28533     }
28534     this.isDirtyFlag = false;
28535         this.addEvents({
28536                 "drop" : true
28537         });
28538 };
28539
28540 Roo.extend(Roo.DDView, Roo.View, {
28541 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28542 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28543 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28544 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28545
28546         isFormField: true,
28547
28548         reset: Roo.emptyFn,
28549         
28550         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28551
28552         validate: function() {
28553                 return true;
28554         },
28555         
28556         destroy: function() {
28557                 this.purgeListeners();
28558                 this.getEl.removeAllListeners();
28559                 this.getEl().remove();
28560                 if (this.dragZone) {
28561                         if (this.dragZone.destroy) {
28562                                 this.dragZone.destroy();
28563                         }
28564                 }
28565                 if (this.dropZone) {
28566                         if (this.dropZone.destroy) {
28567                                 this.dropZone.destroy();
28568                         }
28569                 }
28570         },
28571
28572 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28573         getName: function() {
28574                 return this.name;
28575         },
28576
28577 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28578         setValue: function(v) {
28579                 if (!this.store) {
28580                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28581                 }
28582                 var data = {};
28583                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28584                 this.store.proxy = new Roo.data.MemoryProxy(data);
28585                 this.store.load();
28586         },
28587
28588 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28589         getValue: function() {
28590                 var result = '(';
28591                 this.store.each(function(rec) {
28592                         result += rec.id + ',';
28593                 });
28594                 return result.substr(0, result.length - 1) + ')';
28595         },
28596         
28597         getIds: function() {
28598                 var i = 0, result = new Array(this.store.getCount());
28599                 this.store.each(function(rec) {
28600                         result[i++] = rec.id;
28601                 });
28602                 return result;
28603         },
28604         
28605         isDirty: function() {
28606                 return this.isDirtyFlag;
28607         },
28608
28609 /**
28610  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28611  *      whole Element becomes the target, and this causes the drop gesture to append.
28612  */
28613     getTargetFromEvent : function(e) {
28614                 var target = e.getTarget();
28615                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28616                 target = target.parentNode;
28617                 }
28618                 if (!target) {
28619                         target = this.el.dom.lastChild || this.el.dom;
28620                 }
28621                 return target;
28622     },
28623
28624 /**
28625  *      Create the drag data which consists of an object which has the property "ddel" as
28626  *      the drag proxy element. 
28627  */
28628     getDragData : function(e) {
28629         var target = this.findItemFromChild(e.getTarget());
28630                 if(target) {
28631                         this.handleSelection(e);
28632                         var selNodes = this.getSelectedNodes();
28633             var dragData = {
28634                 source: this,
28635                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28636                 nodes: selNodes,
28637                 records: []
28638                         };
28639                         var selectedIndices = this.getSelectedIndexes();
28640                         for (var i = 0; i < selectedIndices.length; i++) {
28641                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28642                         }
28643                         if (selNodes.length == 1) {
28644                                 dragData.ddel = target.cloneNode(true); // the div element
28645                         } else {
28646                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28647                                 div.className = 'multi-proxy';
28648                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28649                                         div.appendChild(selNodes[i].cloneNode(true));
28650                                 }
28651                                 dragData.ddel = div;
28652                         }
28653             //console.log(dragData)
28654             //console.log(dragData.ddel.innerHTML)
28655                         return dragData;
28656                 }
28657         //console.log('nodragData')
28658                 return false;
28659     },
28660     
28661 /**     Specify to which ddGroup items in this DDView may be dragged. */
28662     setDraggable: function(ddGroup) {
28663         if (ddGroup instanceof Array) {
28664                 Roo.each(ddGroup, this.setDraggable, this);
28665                 return;
28666         }
28667         if (this.dragZone) {
28668                 this.dragZone.addToGroup(ddGroup);
28669         } else {
28670                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28671                                 containerScroll: true,
28672                                 ddGroup: ddGroup 
28673
28674                         });
28675 //                      Draggability implies selection. DragZone's mousedown selects the element.
28676                         if (!this.multiSelect) { this.singleSelect = true; }
28677
28678 //                      Wire the DragZone's handlers up to methods in *this*
28679                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28680                 }
28681     },
28682
28683 /**     Specify from which ddGroup this DDView accepts drops. */
28684     setDroppable: function(ddGroup) {
28685         if (ddGroup instanceof Array) {
28686                 Roo.each(ddGroup, this.setDroppable, this);
28687                 return;
28688         }
28689         if (this.dropZone) {
28690                 this.dropZone.addToGroup(ddGroup);
28691         } else {
28692                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28693                                 containerScroll: true,
28694                                 ddGroup: ddGroup
28695                         });
28696
28697 //                      Wire the DropZone's handlers up to methods in *this*
28698                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28699                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28700                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28701                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28702                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28703                 }
28704     },
28705
28706 /**     Decide whether to drop above or below a View node. */
28707     getDropPoint : function(e, n, dd){
28708         if (n == this.el.dom) { return "above"; }
28709                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28710                 var c = t + (b - t) / 2;
28711                 var y = Roo.lib.Event.getPageY(e);
28712                 if(y <= c) {
28713                         return "above";
28714                 }else{
28715                         return "below";
28716                 }
28717     },
28718
28719     onNodeEnter : function(n, dd, e, data){
28720                 return false;
28721     },
28722     
28723     onNodeOver : function(n, dd, e, data){
28724                 var pt = this.getDropPoint(e, n, dd);
28725                 // set the insert point style on the target node
28726                 var dragElClass = this.dropNotAllowed;
28727                 if (pt) {
28728                         var targetElClass;
28729                         if (pt == "above"){
28730                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28731                                 targetElClass = "x-view-drag-insert-above";
28732                         } else {
28733                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28734                                 targetElClass = "x-view-drag-insert-below";
28735                         }
28736                         if (this.lastInsertClass != targetElClass){
28737                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28738                                 this.lastInsertClass = targetElClass;
28739                         }
28740                 }
28741                 return dragElClass;
28742         },
28743
28744     onNodeOut : function(n, dd, e, data){
28745                 this.removeDropIndicators(n);
28746     },
28747
28748     onNodeDrop : function(n, dd, e, data){
28749         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28750                 return false;
28751         }
28752         var pt = this.getDropPoint(e, n, dd);
28753                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28754                 if (pt == "below") { insertAt++; }
28755                 for (var i = 0; i < data.records.length; i++) {
28756                         var r = data.records[i];
28757                         var dup = this.store.getById(r.id);
28758                         if (dup && (dd != this.dragZone)) {
28759                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28760                         } else {
28761                                 if (data.copy) {
28762                                         this.store.insert(insertAt++, r.copy());
28763                                 } else {
28764                                         data.source.isDirtyFlag = true;
28765                                         r.store.remove(r);
28766                                         this.store.insert(insertAt++, r);
28767                                 }
28768                                 this.isDirtyFlag = true;
28769                         }
28770                 }
28771                 this.dragZone.cachedTarget = null;
28772                 return true;
28773     },
28774
28775     removeDropIndicators : function(n){
28776                 if(n){
28777                         Roo.fly(n).removeClass([
28778                                 "x-view-drag-insert-above",
28779                                 "x-view-drag-insert-below"]);
28780                         this.lastInsertClass = "_noclass";
28781                 }
28782     },
28783
28784 /**
28785  *      Utility method. Add a delete option to the DDView's context menu.
28786  *      @param {String} imageUrl The URL of the "delete" icon image.
28787  */
28788         setDeletable: function(imageUrl) {
28789                 if (!this.singleSelect && !this.multiSelect) {
28790                         this.singleSelect = true;
28791                 }
28792                 var c = this.getContextMenu();
28793                 this.contextMenu.on("itemclick", function(item) {
28794                         switch (item.id) {
28795                                 case "delete":
28796                                         this.remove(this.getSelectedIndexes());
28797                                         break;
28798                         }
28799                 }, this);
28800                 this.contextMenu.add({
28801                         icon: imageUrl,
28802                         id: "delete",
28803                         text: 'Delete'
28804                 });
28805         },
28806         
28807 /**     Return the context menu for this DDView. */
28808         getContextMenu: function() {
28809                 if (!this.contextMenu) {
28810 //                      Create the View's context menu
28811                         this.contextMenu = new Roo.menu.Menu({
28812                                 id: this.id + "-contextmenu"
28813                         });
28814                         this.el.on("contextmenu", this.showContextMenu, this);
28815                 }
28816                 return this.contextMenu;
28817         },
28818         
28819         disableContextMenu: function() {
28820                 if (this.contextMenu) {
28821                         this.el.un("contextmenu", this.showContextMenu, this);
28822                 }
28823         },
28824
28825         showContextMenu: function(e, item) {
28826         item = this.findItemFromChild(e.getTarget());
28827                 if (item) {
28828                         e.stopEvent();
28829                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28830                         this.contextMenu.showAt(e.getXY());
28831             }
28832     },
28833
28834 /**
28835  *      Remove {@link Roo.data.Record}s at the specified indices.
28836  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28837  */
28838     remove: function(selectedIndices) {
28839                 selectedIndices = [].concat(selectedIndices);
28840                 for (var i = 0; i < selectedIndices.length; i++) {
28841                         var rec = this.store.getAt(selectedIndices[i]);
28842                         this.store.remove(rec);
28843                 }
28844     },
28845
28846 /**
28847  *      Double click fires the event, but also, if this is draggable, and there is only one other
28848  *      related DropZone, it transfers the selected node.
28849  */
28850     onDblClick : function(e){
28851         var item = this.findItemFromChild(e.getTarget());
28852         if(item){
28853             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28854                 return false;
28855             }
28856             if (this.dragGroup) {
28857                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28858                     while (targets.indexOf(this.dropZone) > -1) {
28859                             targets.remove(this.dropZone);
28860                                 }
28861                     if (targets.length == 1) {
28862                                         this.dragZone.cachedTarget = null;
28863                         var el = Roo.get(targets[0].getEl());
28864                         var box = el.getBox(true);
28865                         targets[0].onNodeDrop(el.dom, {
28866                                 target: el.dom,
28867                                 xy: [box.x, box.y + box.height - 1]
28868                         }, null, this.getDragData(e));
28869                     }
28870                 }
28871         }
28872     },
28873     
28874     handleSelection: function(e) {
28875                 this.dragZone.cachedTarget = null;
28876         var item = this.findItemFromChild(e.getTarget());
28877         if (!item) {
28878                 this.clearSelections(true);
28879                 return;
28880         }
28881                 if (item && (this.multiSelect || this.singleSelect)){
28882                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28883                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28884                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28885                                 this.unselect(item);
28886                         } else {
28887                                 this.select(item, this.multiSelect && e.ctrlKey);
28888                                 this.lastSelection = item;
28889                         }
28890                 }
28891     },
28892
28893     onItemClick : function(item, index, e){
28894                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28895                         return false;
28896                 }
28897                 return true;
28898     },
28899
28900     unselect : function(nodeInfo, suppressEvent){
28901                 var node = this.getNode(nodeInfo);
28902                 if(node && this.isSelected(node)){
28903                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28904                                 Roo.fly(node).removeClass(this.selectedClass);
28905                                 this.selections.remove(node);
28906                                 if(!suppressEvent){
28907                                         this.fireEvent("selectionchange", this, this.selections);
28908                                 }
28909                         }
28910                 }
28911     }
28912 });
28913 /*
28914  * Based on:
28915  * Ext JS Library 1.1.1
28916  * Copyright(c) 2006-2007, Ext JS, LLC.
28917  *
28918  * Originally Released Under LGPL - original licence link has changed is not relivant.
28919  *
28920  * Fork - LGPL
28921  * <script type="text/javascript">
28922  */
28923  
28924 /**
28925  * @class Roo.LayoutManager
28926  * @extends Roo.util.Observable
28927  * Base class for layout managers.
28928  */
28929 Roo.LayoutManager = function(container, config){
28930     Roo.LayoutManager.superclass.constructor.call(this);
28931     this.el = Roo.get(container);
28932     // ie scrollbar fix
28933     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28934         document.body.scroll = "no";
28935     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28936         this.el.position('relative');
28937     }
28938     this.id = this.el.id;
28939     this.el.addClass("x-layout-container");
28940     /** false to disable window resize monitoring @type Boolean */
28941     this.monitorWindowResize = true;
28942     this.regions = {};
28943     this.addEvents({
28944         /**
28945          * @event layout
28946          * Fires when a layout is performed. 
28947          * @param {Roo.LayoutManager} this
28948          */
28949         "layout" : true,
28950         /**
28951          * @event regionresized
28952          * Fires when the user resizes a region. 
28953          * @param {Roo.LayoutRegion} region The resized region
28954          * @param {Number} newSize The new size (width for east/west, height for north/south)
28955          */
28956         "regionresized" : true,
28957         /**
28958          * @event regioncollapsed
28959          * Fires when a region is collapsed. 
28960          * @param {Roo.LayoutRegion} region The collapsed region
28961          */
28962         "regioncollapsed" : true,
28963         /**
28964          * @event regionexpanded
28965          * Fires when a region is expanded.  
28966          * @param {Roo.LayoutRegion} region The expanded region
28967          */
28968         "regionexpanded" : true
28969     });
28970     this.updating = false;
28971     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28972 };
28973
28974 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28975     /**
28976      * Returns true if this layout is currently being updated
28977      * @return {Boolean}
28978      */
28979     isUpdating : function(){
28980         return this.updating; 
28981     },
28982     
28983     /**
28984      * Suspend the LayoutManager from doing auto-layouts while
28985      * making multiple add or remove calls
28986      */
28987     beginUpdate : function(){
28988         this.updating = true;    
28989     },
28990     
28991     /**
28992      * Restore auto-layouts and optionally disable the manager from performing a layout
28993      * @param {Boolean} noLayout true to disable a layout update 
28994      */
28995     endUpdate : function(noLayout){
28996         this.updating = false;
28997         if(!noLayout){
28998             this.layout();
28999         }    
29000     },
29001     
29002     layout: function(){
29003         
29004     },
29005     
29006     onRegionResized : function(region, newSize){
29007         this.fireEvent("regionresized", region, newSize);
29008         this.layout();
29009     },
29010     
29011     onRegionCollapsed : function(region){
29012         this.fireEvent("regioncollapsed", region);
29013     },
29014     
29015     onRegionExpanded : function(region){
29016         this.fireEvent("regionexpanded", region);
29017     },
29018         
29019     /**
29020      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29021      * performs box-model adjustments.
29022      * @return {Object} The size as an object {width: (the width), height: (the height)}
29023      */
29024     getViewSize : function(){
29025         var size;
29026         if(this.el.dom != document.body){
29027             size = this.el.getSize();
29028         }else{
29029             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29030         }
29031         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29032         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29033         return size;
29034     },
29035     
29036     /**
29037      * Returns the Element this layout is bound to.
29038      * @return {Roo.Element}
29039      */
29040     getEl : function(){
29041         return this.el;
29042     },
29043     
29044     /**
29045      * Returns the specified region.
29046      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29047      * @return {Roo.LayoutRegion}
29048      */
29049     getRegion : function(target){
29050         return this.regions[target.toLowerCase()];
29051     },
29052     
29053     onWindowResize : function(){
29054         if(this.monitorWindowResize){
29055             this.layout();
29056         }
29057     }
29058 });/*
29059  * Based on:
29060  * Ext JS Library 1.1.1
29061  * Copyright(c) 2006-2007, Ext JS, LLC.
29062  *
29063  * Originally Released Under LGPL - original licence link has changed is not relivant.
29064  *
29065  * Fork - LGPL
29066  * <script type="text/javascript">
29067  */
29068 /**
29069  * @class Roo.BorderLayout
29070  * @extends Roo.LayoutManager
29071  * @children Roo.ContentPanel
29072  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29073  * please see: <br><br>
29074  * <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>
29075  * <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>
29076  * Example:
29077  <pre><code>
29078  var layout = new Roo.BorderLayout(document.body, {
29079     north: {
29080         initialSize: 25,
29081         titlebar: false
29082     },
29083     west: {
29084         split:true,
29085         initialSize: 200,
29086         minSize: 175,
29087         maxSize: 400,
29088         titlebar: true,
29089         collapsible: true
29090     },
29091     east: {
29092         split:true,
29093         initialSize: 202,
29094         minSize: 175,
29095         maxSize: 400,
29096         titlebar: true,
29097         collapsible: true
29098     },
29099     south: {
29100         split:true,
29101         initialSize: 100,
29102         minSize: 100,
29103         maxSize: 200,
29104         titlebar: true,
29105         collapsible: true
29106     },
29107     center: {
29108         titlebar: true,
29109         autoScroll:true,
29110         resizeTabs: true,
29111         minTabWidth: 50,
29112         preferredTabWidth: 150
29113     }
29114 });
29115
29116 // shorthand
29117 var CP = Roo.ContentPanel;
29118
29119 layout.beginUpdate();
29120 layout.add("north", new CP("north", "North"));
29121 layout.add("south", new CP("south", {title: "South", closable: true}));
29122 layout.add("west", new CP("west", {title: "West"}));
29123 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29124 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29125 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29126 layout.getRegion("center").showPanel("center1");
29127 layout.endUpdate();
29128 </code></pre>
29129
29130 <b>The container the layout is rendered into can be either the body element or any other element.
29131 If it is not the body element, the container needs to either be an absolute positioned element,
29132 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29133 the container size if it is not the body element.</b>
29134
29135 * @constructor
29136 * Create a new BorderLayout
29137 * @param {String/HTMLElement/Element} container The container this layout is bound to
29138 * @param {Object} config Configuration options
29139  */
29140 Roo.BorderLayout = function(container, config){
29141     config = config || {};
29142     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29143     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29144     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29145         var target = this.factory.validRegions[i];
29146         if(config[target]){
29147             this.addRegion(target, config[target]);
29148         }
29149     }
29150 };
29151
29152 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29153         
29154         /**
29155          * @cfg {Roo.LayoutRegion} east
29156          */
29157         /**
29158          * @cfg {Roo.LayoutRegion} west
29159          */
29160         /**
29161          * @cfg {Roo.LayoutRegion} north
29162          */
29163         /**
29164          * @cfg {Roo.LayoutRegion} south
29165          */
29166         /**
29167          * @cfg {Roo.LayoutRegion} center
29168          */
29169     /**
29170      * Creates and adds a new region if it doesn't already exist.
29171      * @param {String} target The target region key (north, south, east, west or center).
29172      * @param {Object} config The regions config object
29173      * @return {BorderLayoutRegion} The new region
29174      */
29175     addRegion : function(target, config){
29176         if(!this.regions[target]){
29177             var r = this.factory.create(target, this, config);
29178             this.bindRegion(target, r);
29179         }
29180         return this.regions[target];
29181     },
29182
29183     // private (kinda)
29184     bindRegion : function(name, r){
29185         this.regions[name] = r;
29186         r.on("visibilitychange", this.layout, this);
29187         r.on("paneladded", this.layout, this);
29188         r.on("panelremoved", this.layout, this);
29189         r.on("invalidated", this.layout, this);
29190         r.on("resized", this.onRegionResized, this);
29191         r.on("collapsed", this.onRegionCollapsed, this);
29192         r.on("expanded", this.onRegionExpanded, this);
29193     },
29194
29195     /**
29196      * Performs a layout update.
29197      */
29198     layout : function(){
29199         if(this.updating) {
29200             return;
29201         }
29202         var size = this.getViewSize();
29203         var w = size.width;
29204         var h = size.height;
29205         var centerW = w;
29206         var centerH = h;
29207         var centerY = 0;
29208         var centerX = 0;
29209         //var x = 0, y = 0;
29210
29211         var rs = this.regions;
29212         var north = rs["north"];
29213         var south = rs["south"]; 
29214         var west = rs["west"];
29215         var east = rs["east"];
29216         var center = rs["center"];
29217         //if(this.hideOnLayout){ // not supported anymore
29218             //c.el.setStyle("display", "none");
29219         //}
29220         if(north && north.isVisible()){
29221             var b = north.getBox();
29222             var m = north.getMargins();
29223             b.width = w - (m.left+m.right);
29224             b.x = m.left;
29225             b.y = m.top;
29226             centerY = b.height + b.y + m.bottom;
29227             centerH -= centerY;
29228             north.updateBox(this.safeBox(b));
29229         }
29230         if(south && south.isVisible()){
29231             var b = south.getBox();
29232             var m = south.getMargins();
29233             b.width = w - (m.left+m.right);
29234             b.x = m.left;
29235             var totalHeight = (b.height + m.top + m.bottom);
29236             b.y = h - totalHeight + m.top;
29237             centerH -= totalHeight;
29238             south.updateBox(this.safeBox(b));
29239         }
29240         if(west && west.isVisible()){
29241             var b = west.getBox();
29242             var m = west.getMargins();
29243             b.height = centerH - (m.top+m.bottom);
29244             b.x = m.left;
29245             b.y = centerY + m.top;
29246             var totalWidth = (b.width + m.left + m.right);
29247             centerX += totalWidth;
29248             centerW -= totalWidth;
29249             west.updateBox(this.safeBox(b));
29250         }
29251         if(east && east.isVisible()){
29252             var b = east.getBox();
29253             var m = east.getMargins();
29254             b.height = centerH - (m.top+m.bottom);
29255             var totalWidth = (b.width + m.left + m.right);
29256             b.x = w - totalWidth + m.left;
29257             b.y = centerY + m.top;
29258             centerW -= totalWidth;
29259             east.updateBox(this.safeBox(b));
29260         }
29261         if(center){
29262             var m = center.getMargins();
29263             var centerBox = {
29264                 x: centerX + m.left,
29265                 y: centerY + m.top,
29266                 width: centerW - (m.left+m.right),
29267                 height: centerH - (m.top+m.bottom)
29268             };
29269             //if(this.hideOnLayout){
29270                 //center.el.setStyle("display", "block");
29271             //}
29272             center.updateBox(this.safeBox(centerBox));
29273         }
29274         this.el.repaint();
29275         this.fireEvent("layout", this);
29276     },
29277
29278     // private
29279     safeBox : function(box){
29280         box.width = Math.max(0, box.width);
29281         box.height = Math.max(0, box.height);
29282         return box;
29283     },
29284
29285     /**
29286      * Adds a ContentPanel (or subclass) to this layout.
29287      * @param {String} target The target region key (north, south, east, west or center).
29288      * @param {Roo.ContentPanel} panel The panel to add
29289      * @return {Roo.ContentPanel} The added panel
29290      */
29291     add : function(target, panel){
29292          
29293         target = target.toLowerCase();
29294         return this.regions[target].add(panel);
29295     },
29296
29297     /**
29298      * Remove a ContentPanel (or subclass) to this layout.
29299      * @param {String} target The target region key (north, south, east, west or center).
29300      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29301      * @return {Roo.ContentPanel} The removed panel
29302      */
29303     remove : function(target, panel){
29304         target = target.toLowerCase();
29305         return this.regions[target].remove(panel);
29306     },
29307
29308     /**
29309      * Searches all regions for a panel with the specified id
29310      * @param {String} panelId
29311      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29312      */
29313     findPanel : function(panelId){
29314         var rs = this.regions;
29315         for(var target in rs){
29316             if(typeof rs[target] != "function"){
29317                 var p = rs[target].getPanel(panelId);
29318                 if(p){
29319                     return p;
29320                 }
29321             }
29322         }
29323         return null;
29324     },
29325
29326     /**
29327      * Searches all regions for a panel with the specified id and activates (shows) it.
29328      * @param {String/ContentPanel} panelId The panels id or the panel itself
29329      * @return {Roo.ContentPanel} The shown panel or null
29330      */
29331     showPanel : function(panelId) {
29332       var rs = this.regions;
29333       for(var target in rs){
29334          var r = rs[target];
29335          if(typeof r != "function"){
29336             if(r.hasPanel(panelId)){
29337                return r.showPanel(panelId);
29338             }
29339          }
29340       }
29341       return null;
29342    },
29343
29344    /**
29345      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29346      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29347      */
29348     restoreState : function(provider){
29349         if(!provider){
29350             provider = Roo.state.Manager;
29351         }
29352         var sm = new Roo.LayoutStateManager();
29353         sm.init(this, provider);
29354     },
29355
29356     /**
29357      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29358      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29359      * a valid ContentPanel config object.  Example:
29360      * <pre><code>
29361 // Create the main layout
29362 var layout = new Roo.BorderLayout('main-ct', {
29363     west: {
29364         split:true,
29365         minSize: 175,
29366         titlebar: true
29367     },
29368     center: {
29369         title:'Components'
29370     }
29371 }, 'main-ct');
29372
29373 // Create and add multiple ContentPanels at once via configs
29374 layout.batchAdd({
29375    west: {
29376        id: 'source-files',
29377        autoCreate:true,
29378        title:'Ext Source Files',
29379        autoScroll:true,
29380        fitToFrame:true
29381    },
29382    center : {
29383        el: cview,
29384        autoScroll:true,
29385        fitToFrame:true,
29386        toolbar: tb,
29387        resizeEl:'cbody'
29388    }
29389 });
29390 </code></pre>
29391      * @param {Object} regions An object containing ContentPanel configs by region name
29392      */
29393     batchAdd : function(regions){
29394         this.beginUpdate();
29395         for(var rname in regions){
29396             var lr = this.regions[rname];
29397             if(lr){
29398                 this.addTypedPanels(lr, regions[rname]);
29399             }
29400         }
29401         this.endUpdate();
29402     },
29403
29404     // private
29405     addTypedPanels : function(lr, ps){
29406         if(typeof ps == 'string'){
29407             lr.add(new Roo.ContentPanel(ps));
29408         }
29409         else if(ps instanceof Array){
29410             for(var i =0, len = ps.length; i < len; i++){
29411                 this.addTypedPanels(lr, ps[i]);
29412             }
29413         }
29414         else if(!ps.events){ // raw config?
29415             var el = ps.el;
29416             delete ps.el; // prevent conflict
29417             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29418         }
29419         else {  // panel object assumed!
29420             lr.add(ps);
29421         }
29422     },
29423     /**
29424      * Adds a xtype elements to the layout.
29425      * <pre><code>
29426
29427 layout.addxtype({
29428        xtype : 'ContentPanel',
29429        region: 'west',
29430        items: [ .... ]
29431    }
29432 );
29433
29434 layout.addxtype({
29435         xtype : 'NestedLayoutPanel',
29436         region: 'west',
29437         layout: {
29438            center: { },
29439            west: { }   
29440         },
29441         items : [ ... list of content panels or nested layout panels.. ]
29442    }
29443 );
29444 </code></pre>
29445      * @param {Object} cfg Xtype definition of item to add.
29446      */
29447     addxtype : function(cfg)
29448     {
29449         // basically accepts a pannel...
29450         // can accept a layout region..!?!?
29451         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29452         
29453         if (!cfg.xtype.match(/Panel$/)) {
29454             return false;
29455         }
29456         var ret = false;
29457         
29458         if (typeof(cfg.region) == 'undefined') {
29459             Roo.log("Failed to add Panel, region was not set");
29460             Roo.log(cfg);
29461             return false;
29462         }
29463         var region = cfg.region;
29464         delete cfg.region;
29465         
29466           
29467         var xitems = [];
29468         if (cfg.items) {
29469             xitems = cfg.items;
29470             delete cfg.items;
29471         }
29472         var nb = false;
29473         
29474         switch(cfg.xtype) 
29475         {
29476             case 'ContentPanel':  // ContentPanel (el, cfg)
29477             case 'ScrollPanel':  // ContentPanel (el, cfg)
29478             case 'ViewPanel': 
29479                 if(cfg.autoCreate) {
29480                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29481                 } else {
29482                     var el = this.el.createChild();
29483                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29484                 }
29485                 
29486                 this.add(region, ret);
29487                 break;
29488             
29489             
29490             case 'TreePanel': // our new panel!
29491                 cfg.el = this.el.createChild();
29492                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29493                 this.add(region, ret);
29494                 break;
29495             
29496             case 'NestedLayoutPanel': 
29497                 // create a new Layout (which is  a Border Layout...
29498                 var el = this.el.createChild();
29499                 var clayout = cfg.layout;
29500                 delete cfg.layout;
29501                 clayout.items   = clayout.items  || [];
29502                 // replace this exitems with the clayout ones..
29503                 xitems = clayout.items;
29504                  
29505                 
29506                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29507                     cfg.background = false;
29508                 }
29509                 var layout = new Roo.BorderLayout(el, clayout);
29510                 
29511                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29512                 //console.log('adding nested layout panel '  + cfg.toSource());
29513                 this.add(region, ret);
29514                 nb = {}; /// find first...
29515                 break;
29516                 
29517             case 'GridPanel': 
29518             
29519                 // needs grid and region
29520                 
29521                 //var el = this.getRegion(region).el.createChild();
29522                 var el = this.el.createChild();
29523                 // create the grid first...
29524                 
29525                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29526                 delete cfg.grid;
29527                 if (region == 'center' && this.active ) {
29528                     cfg.background = false;
29529                 }
29530                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29531                 
29532                 this.add(region, ret);
29533                 if (cfg.background) {
29534                     ret.on('activate', function(gp) {
29535                         if (!gp.grid.rendered) {
29536                             gp.grid.render();
29537                         }
29538                     });
29539                 } else {
29540                     grid.render();
29541                 }
29542                 break;
29543            
29544            
29545            
29546                 
29547                 
29548                 
29549             default:
29550                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29551                     
29552                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29553                     this.add(region, ret);
29554                 } else {
29555                 
29556                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29557                     return null;
29558                 }
29559                 
29560              // GridPanel (grid, cfg)
29561             
29562         }
29563         this.beginUpdate();
29564         // add children..
29565         var region = '';
29566         var abn = {};
29567         Roo.each(xitems, function(i)  {
29568             region = nb && i.region ? i.region : false;
29569             
29570             var add = ret.addxtype(i);
29571            
29572             if (region) {
29573                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29574                 if (!i.background) {
29575                     abn[region] = nb[region] ;
29576                 }
29577             }
29578             
29579         });
29580         this.endUpdate();
29581
29582         // make the last non-background panel active..
29583         //if (nb) { Roo.log(abn); }
29584         if (nb) {
29585             
29586             for(var r in abn) {
29587                 region = this.getRegion(r);
29588                 if (region) {
29589                     // tried using nb[r], but it does not work..
29590                      
29591                     region.showPanel(abn[r]);
29592                    
29593                 }
29594             }
29595         }
29596         return ret;
29597         
29598     }
29599 });
29600
29601 /**
29602  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29603  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29604  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29605  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29606  * <pre><code>
29607 // shorthand
29608 var CP = Roo.ContentPanel;
29609
29610 var layout = Roo.BorderLayout.create({
29611     north: {
29612         initialSize: 25,
29613         titlebar: false,
29614         panels: [new CP("north", "North")]
29615     },
29616     west: {
29617         split:true,
29618         initialSize: 200,
29619         minSize: 175,
29620         maxSize: 400,
29621         titlebar: true,
29622         collapsible: true,
29623         panels: [new CP("west", {title: "West"})]
29624     },
29625     east: {
29626         split:true,
29627         initialSize: 202,
29628         minSize: 175,
29629         maxSize: 400,
29630         titlebar: true,
29631         collapsible: true,
29632         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29633     },
29634     south: {
29635         split:true,
29636         initialSize: 100,
29637         minSize: 100,
29638         maxSize: 200,
29639         titlebar: true,
29640         collapsible: true,
29641         panels: [new CP("south", {title: "South", closable: true})]
29642     },
29643     center: {
29644         titlebar: true,
29645         autoScroll:true,
29646         resizeTabs: true,
29647         minTabWidth: 50,
29648         preferredTabWidth: 150,
29649         panels: [
29650             new CP("center1", {title: "Close Me", closable: true}),
29651             new CP("center2", {title: "Center Panel", closable: false})
29652         ]
29653     }
29654 }, document.body);
29655
29656 layout.getRegion("center").showPanel("center1");
29657 </code></pre>
29658  * @param config
29659  * @param targetEl
29660  */
29661 Roo.BorderLayout.create = function(config, targetEl){
29662     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29663     layout.beginUpdate();
29664     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29665     for(var j = 0, jlen = regions.length; j < jlen; j++){
29666         var lr = regions[j];
29667         if(layout.regions[lr] && config[lr].panels){
29668             var r = layout.regions[lr];
29669             var ps = config[lr].panels;
29670             layout.addTypedPanels(r, ps);
29671         }
29672     }
29673     layout.endUpdate();
29674     return layout;
29675 };
29676
29677 // private
29678 Roo.BorderLayout.RegionFactory = {
29679     // private
29680     validRegions : ["north","south","east","west","center"],
29681
29682     // private
29683     create : function(target, mgr, config){
29684         target = target.toLowerCase();
29685         if(config.lightweight || config.basic){
29686             return new Roo.BasicLayoutRegion(mgr, config, target);
29687         }
29688         switch(target){
29689             case "north":
29690                 return new Roo.NorthLayoutRegion(mgr, config);
29691             case "south":
29692                 return new Roo.SouthLayoutRegion(mgr, config);
29693             case "east":
29694                 return new Roo.EastLayoutRegion(mgr, config);
29695             case "west":
29696                 return new Roo.WestLayoutRegion(mgr, config);
29697             case "center":
29698                 return new Roo.CenterLayoutRegion(mgr, config);
29699         }
29700         throw 'Layout region "'+target+'" not supported.';
29701     }
29702 };/*
29703  * Based on:
29704  * Ext JS Library 1.1.1
29705  * Copyright(c) 2006-2007, Ext JS, LLC.
29706  *
29707  * Originally Released Under LGPL - original licence link has changed is not relivant.
29708  *
29709  * Fork - LGPL
29710  * <script type="text/javascript">
29711  */
29712  
29713 /**
29714  * @class Roo.BasicLayoutRegion
29715  * @extends Roo.util.Observable
29716  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29717  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29718  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29719  */
29720 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29721     this.mgr = mgr;
29722     this.position  = pos;
29723     this.events = {
29724         /**
29725          * @scope Roo.BasicLayoutRegion
29726          */
29727         
29728         /**
29729          * @event beforeremove
29730          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29731          * @param {Roo.LayoutRegion} this
29732          * @param {Roo.ContentPanel} panel The panel
29733          * @param {Object} e The cancel event object
29734          */
29735         "beforeremove" : true,
29736         /**
29737          * @event invalidated
29738          * Fires when the layout for this region is changed.
29739          * @param {Roo.LayoutRegion} this
29740          */
29741         "invalidated" : true,
29742         /**
29743          * @event visibilitychange
29744          * Fires when this region is shown or hidden 
29745          * @param {Roo.LayoutRegion} this
29746          * @param {Boolean} visibility true or false
29747          */
29748         "visibilitychange" : true,
29749         /**
29750          * @event paneladded
29751          * Fires when a panel is added. 
29752          * @param {Roo.LayoutRegion} this
29753          * @param {Roo.ContentPanel} panel The panel
29754          */
29755         "paneladded" : true,
29756         /**
29757          * @event panelremoved
29758          * Fires when a panel is removed. 
29759          * @param {Roo.LayoutRegion} this
29760          * @param {Roo.ContentPanel} panel The panel
29761          */
29762         "panelremoved" : true,
29763         /**
29764          * @event beforecollapse
29765          * Fires when this region before collapse.
29766          * @param {Roo.LayoutRegion} this
29767          */
29768         "beforecollapse" : true,
29769         /**
29770          * @event collapsed
29771          * Fires when this region is collapsed.
29772          * @param {Roo.LayoutRegion} this
29773          */
29774         "collapsed" : true,
29775         /**
29776          * @event expanded
29777          * Fires when this region is expanded.
29778          * @param {Roo.LayoutRegion} this
29779          */
29780         "expanded" : true,
29781         /**
29782          * @event slideshow
29783          * Fires when this region is slid into view.
29784          * @param {Roo.LayoutRegion} this
29785          */
29786         "slideshow" : true,
29787         /**
29788          * @event slidehide
29789          * Fires when this region slides out of view. 
29790          * @param {Roo.LayoutRegion} this
29791          */
29792         "slidehide" : true,
29793         /**
29794          * @event panelactivated
29795          * Fires when a panel is activated. 
29796          * @param {Roo.LayoutRegion} this
29797          * @param {Roo.ContentPanel} panel The activated panel
29798          */
29799         "panelactivated" : true,
29800         /**
29801          * @event resized
29802          * Fires when the user resizes this region. 
29803          * @param {Roo.LayoutRegion} this
29804          * @param {Number} newSize The new size (width for east/west, height for north/south)
29805          */
29806         "resized" : true
29807     };
29808     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29809     this.panels = new Roo.util.MixedCollection();
29810     this.panels.getKey = this.getPanelId.createDelegate(this);
29811     this.box = null;
29812     this.activePanel = null;
29813     // ensure listeners are added...
29814     
29815     if (config.listeners || config.events) {
29816         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29817             listeners : config.listeners || {},
29818             events : config.events || {}
29819         });
29820     }
29821     
29822     if(skipConfig !== true){
29823         this.applyConfig(config);
29824     }
29825 };
29826
29827 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29828     getPanelId : function(p){
29829         return p.getId();
29830     },
29831     
29832     applyConfig : function(config){
29833         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29834         this.config = config;
29835         
29836     },
29837     
29838     /**
29839      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29840      * the width, for horizontal (north, south) the height.
29841      * @param {Number} newSize The new width or height
29842      */
29843     resizeTo : function(newSize){
29844         var el = this.el ? this.el :
29845                  (this.activePanel ? this.activePanel.getEl() : null);
29846         if(el){
29847             switch(this.position){
29848                 case "east":
29849                 case "west":
29850                     el.setWidth(newSize);
29851                     this.fireEvent("resized", this, newSize);
29852                 break;
29853                 case "north":
29854                 case "south":
29855                     el.setHeight(newSize);
29856                     this.fireEvent("resized", this, newSize);
29857                 break;                
29858             }
29859         }
29860     },
29861     
29862     getBox : function(){
29863         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29864     },
29865     
29866     getMargins : function(){
29867         return this.margins;
29868     },
29869     
29870     updateBox : function(box){
29871         this.box = box;
29872         var el = this.activePanel.getEl();
29873         el.dom.style.left = box.x + "px";
29874         el.dom.style.top = box.y + "px";
29875         this.activePanel.setSize(box.width, box.height);
29876     },
29877     
29878     /**
29879      * Returns the container element for this region.
29880      * @return {Roo.Element}
29881      */
29882     getEl : function(){
29883         return this.activePanel;
29884     },
29885     
29886     /**
29887      * Returns true if this region is currently visible.
29888      * @return {Boolean}
29889      */
29890     isVisible : function(){
29891         return this.activePanel ? true : false;
29892     },
29893     
29894     setActivePanel : function(panel){
29895         panel = this.getPanel(panel);
29896         if(this.activePanel && this.activePanel != panel){
29897             this.activePanel.setActiveState(false);
29898             this.activePanel.getEl().setLeftTop(-10000,-10000);
29899         }
29900         this.activePanel = panel;
29901         panel.setActiveState(true);
29902         if(this.box){
29903             panel.setSize(this.box.width, this.box.height);
29904         }
29905         this.fireEvent("panelactivated", this, panel);
29906         this.fireEvent("invalidated");
29907     },
29908     
29909     /**
29910      * Show the specified panel.
29911      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29912      * @return {Roo.ContentPanel} The shown panel or null
29913      */
29914     showPanel : function(panel){
29915         if(panel = this.getPanel(panel)){
29916             this.setActivePanel(panel);
29917         }
29918         return panel;
29919     },
29920     
29921     /**
29922      * Get the active panel for this region.
29923      * @return {Roo.ContentPanel} The active panel or null
29924      */
29925     getActivePanel : function(){
29926         return this.activePanel;
29927     },
29928     
29929     /**
29930      * Add the passed ContentPanel(s)
29931      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29932      * @return {Roo.ContentPanel} The panel added (if only one was added)
29933      */
29934     add : function(panel){
29935         if(arguments.length > 1){
29936             for(var i = 0, len = arguments.length; i < len; i++) {
29937                 this.add(arguments[i]);
29938             }
29939             return null;
29940         }
29941         if(this.hasPanel(panel)){
29942             this.showPanel(panel);
29943             return panel;
29944         }
29945         var el = panel.getEl();
29946         if(el.dom.parentNode != this.mgr.el.dom){
29947             this.mgr.el.dom.appendChild(el.dom);
29948         }
29949         if(panel.setRegion){
29950             panel.setRegion(this);
29951         }
29952         this.panels.add(panel);
29953         el.setStyle("position", "absolute");
29954         if(!panel.background){
29955             this.setActivePanel(panel);
29956             if(this.config.initialSize && this.panels.getCount()==1){
29957                 this.resizeTo(this.config.initialSize);
29958             }
29959         }
29960         this.fireEvent("paneladded", this, panel);
29961         return panel;
29962     },
29963     
29964     /**
29965      * Returns true if the panel is in this region.
29966      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29967      * @return {Boolean}
29968      */
29969     hasPanel : function(panel){
29970         if(typeof panel == "object"){ // must be panel obj
29971             panel = panel.getId();
29972         }
29973         return this.getPanel(panel) ? true : false;
29974     },
29975     
29976     /**
29977      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29978      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29979      * @param {Boolean} preservePanel Overrides the config preservePanel option
29980      * @return {Roo.ContentPanel} The panel that was removed
29981      */
29982     remove : function(panel, preservePanel){
29983         panel = this.getPanel(panel);
29984         if(!panel){
29985             return null;
29986         }
29987         var e = {};
29988         this.fireEvent("beforeremove", this, panel, e);
29989         if(e.cancel === true){
29990             return null;
29991         }
29992         var panelId = panel.getId();
29993         this.panels.removeKey(panelId);
29994         return panel;
29995     },
29996     
29997     /**
29998      * Returns the panel specified or null if it's not in this region.
29999      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30000      * @return {Roo.ContentPanel}
30001      */
30002     getPanel : function(id){
30003         if(typeof id == "object"){ // must be panel obj
30004             return id;
30005         }
30006         return this.panels.get(id);
30007     },
30008     
30009     /**
30010      * Returns this regions position (north/south/east/west/center).
30011      * @return {String} 
30012      */
30013     getPosition: function(){
30014         return this.position;    
30015     }
30016 });/*
30017  * Based on:
30018  * Ext JS Library 1.1.1
30019  * Copyright(c) 2006-2007, Ext JS, LLC.
30020  *
30021  * Originally Released Under LGPL - original licence link has changed is not relivant.
30022  *
30023  * Fork - LGPL
30024  * <script type="text/javascript">
30025  */
30026  
30027 /**
30028  * @class Roo.LayoutRegion
30029  * @extends Roo.BasicLayoutRegion
30030  * This class represents a region in a layout manager.
30031  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30032  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30033  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30034  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30035  * @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})
30036  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30037  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30038  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30039  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30040  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30041  * @cfg {String}    title           The title for the region (overrides panel titles)
30042  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30043  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30044  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30045  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30046  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30047  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30048  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30049  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30050  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30051  * @cfg {Boolean}   showPin         True to show a pin button
30052  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30053  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30054  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30055  * @cfg {Number}    width           For East/West panels
30056  * @cfg {Number}    height          For North/South panels
30057  * @cfg {Boolean}   split           To show the splitter
30058  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30059  */
30060 Roo.LayoutRegion = function(mgr, config, pos){
30061     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30062     var dh = Roo.DomHelper;
30063     /** This region's container element 
30064     * @type Roo.Element */
30065     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30066     /** This region's title element 
30067     * @type Roo.Element */
30068
30069     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30070         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30071         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30072     ]}, true);
30073     this.titleEl.enableDisplayMode();
30074     /** This region's title text element 
30075     * @type HTMLElement */
30076     this.titleTextEl = this.titleEl.dom.firstChild;
30077     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30078     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30079     this.closeBtn.enableDisplayMode();
30080     this.closeBtn.on("click", this.closeClicked, this);
30081     this.closeBtn.hide();
30082
30083     this.createBody(config);
30084     this.visible = true;
30085     this.collapsed = false;
30086
30087     if(config.hideWhenEmpty){
30088         this.hide();
30089         this.on("paneladded", this.validateVisibility, this);
30090         this.on("panelremoved", this.validateVisibility, this);
30091     }
30092     this.applyConfig(config);
30093 };
30094
30095 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30096
30097     createBody : function(){
30098         /** This region's body element 
30099         * @type Roo.Element */
30100         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30101     },
30102
30103     applyConfig : function(c){
30104         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30105             var dh = Roo.DomHelper;
30106             if(c.titlebar !== false){
30107                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30108                 this.collapseBtn.on("click", this.collapse, this);
30109                 this.collapseBtn.enableDisplayMode();
30110
30111                 if(c.showPin === true || this.showPin){
30112                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30113                     this.stickBtn.enableDisplayMode();
30114                     this.stickBtn.on("click", this.expand, this);
30115                     this.stickBtn.hide();
30116                 }
30117             }
30118             /** This region's collapsed element
30119             * @type Roo.Element */
30120             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30121                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30122             ]}, true);
30123             if(c.floatable !== false){
30124                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30125                this.collapsedEl.on("click", this.collapseClick, this);
30126             }
30127
30128             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30129                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30130                    id: "message", unselectable: "on", style:{"float":"left"}});
30131                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30132              }
30133             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30134             this.expandBtn.on("click", this.expand, this);
30135         }
30136         if(this.collapseBtn){
30137             this.collapseBtn.setVisible(c.collapsible == true);
30138         }
30139         this.cmargins = c.cmargins || this.cmargins ||
30140                          (this.position == "west" || this.position == "east" ?
30141                              {top: 0, left: 2, right:2, bottom: 0} :
30142                              {top: 2, left: 0, right:0, bottom: 2});
30143         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30144         this.bottomTabs = c.tabPosition != "top";
30145         this.autoScroll = c.autoScroll || false;
30146         if(this.autoScroll){
30147             this.bodyEl.setStyle("overflow", "auto");
30148         }else{
30149             this.bodyEl.setStyle("overflow", "hidden");
30150         }
30151         //if(c.titlebar !== false){
30152             if((!c.titlebar && !c.title) || c.titlebar === false){
30153                 this.titleEl.hide();
30154             }else{
30155                 this.titleEl.show();
30156                 if(c.title){
30157                     this.titleTextEl.innerHTML = c.title;
30158                 }
30159             }
30160         //}
30161         this.duration = c.duration || .30;
30162         this.slideDuration = c.slideDuration || .45;
30163         this.config = c;
30164         if(c.collapsed){
30165             this.collapse(true);
30166         }
30167         if(c.hidden){
30168             this.hide();
30169         }
30170     },
30171     /**
30172      * Returns true if this region is currently visible.
30173      * @return {Boolean}
30174      */
30175     isVisible : function(){
30176         return this.visible;
30177     },
30178
30179     /**
30180      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30181      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30182      */
30183     setCollapsedTitle : function(title){
30184         title = title || "&#160;";
30185         if(this.collapsedTitleTextEl){
30186             this.collapsedTitleTextEl.innerHTML = title;
30187         }
30188     },
30189
30190     getBox : function(){
30191         var b;
30192         if(!this.collapsed){
30193             b = this.el.getBox(false, true);
30194         }else{
30195             b = this.collapsedEl.getBox(false, true);
30196         }
30197         return b;
30198     },
30199
30200     getMargins : function(){
30201         return this.collapsed ? this.cmargins : this.margins;
30202     },
30203
30204     highlight : function(){
30205         this.el.addClass("x-layout-panel-dragover");
30206     },
30207
30208     unhighlight : function(){
30209         this.el.removeClass("x-layout-panel-dragover");
30210     },
30211
30212     updateBox : function(box){
30213         this.box = box;
30214         if(!this.collapsed){
30215             this.el.dom.style.left = box.x + "px";
30216             this.el.dom.style.top = box.y + "px";
30217             this.updateBody(box.width, box.height);
30218         }else{
30219             this.collapsedEl.dom.style.left = box.x + "px";
30220             this.collapsedEl.dom.style.top = box.y + "px";
30221             this.collapsedEl.setSize(box.width, box.height);
30222         }
30223         if(this.tabs){
30224             this.tabs.autoSizeTabs();
30225         }
30226     },
30227
30228     updateBody : function(w, h){
30229         if(w !== null){
30230             this.el.setWidth(w);
30231             w -= this.el.getBorderWidth("rl");
30232             if(this.config.adjustments){
30233                 w += this.config.adjustments[0];
30234             }
30235         }
30236         if(h !== null){
30237             this.el.setHeight(h);
30238             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30239             h -= this.el.getBorderWidth("tb");
30240             if(this.config.adjustments){
30241                 h += this.config.adjustments[1];
30242             }
30243             this.bodyEl.setHeight(h);
30244             if(this.tabs){
30245                 h = this.tabs.syncHeight(h);
30246             }
30247         }
30248         if(this.panelSize){
30249             w = w !== null ? w : this.panelSize.width;
30250             h = h !== null ? h : this.panelSize.height;
30251         }
30252         if(this.activePanel){
30253             var el = this.activePanel.getEl();
30254             w = w !== null ? w : el.getWidth();
30255             h = h !== null ? h : el.getHeight();
30256             this.panelSize = {width: w, height: h};
30257             this.activePanel.setSize(w, h);
30258         }
30259         if(Roo.isIE && this.tabs){
30260             this.tabs.el.repaint();
30261         }
30262     },
30263
30264     /**
30265      * Returns the container element for this region.
30266      * @return {Roo.Element}
30267      */
30268     getEl : function(){
30269         return this.el;
30270     },
30271
30272     /**
30273      * Hides this region.
30274      */
30275     hide : function(){
30276         if(!this.collapsed){
30277             this.el.dom.style.left = "-2000px";
30278             this.el.hide();
30279         }else{
30280             this.collapsedEl.dom.style.left = "-2000px";
30281             this.collapsedEl.hide();
30282         }
30283         this.visible = false;
30284         this.fireEvent("visibilitychange", this, false);
30285     },
30286
30287     /**
30288      * Shows this region if it was previously hidden.
30289      */
30290     show : function(){
30291         if(!this.collapsed){
30292             this.el.show();
30293         }else{
30294             this.collapsedEl.show();
30295         }
30296         this.visible = true;
30297         this.fireEvent("visibilitychange", this, true);
30298     },
30299
30300     closeClicked : function(){
30301         if(this.activePanel){
30302             this.remove(this.activePanel);
30303         }
30304     },
30305
30306     collapseClick : function(e){
30307         if(this.isSlid){
30308            e.stopPropagation();
30309            this.slideIn();
30310         }else{
30311            e.stopPropagation();
30312            this.slideOut();
30313         }
30314     },
30315
30316     /**
30317      * Collapses this region.
30318      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30319      */
30320     collapse : function(skipAnim, skipCheck){
30321         if(this.collapsed) {
30322             return;
30323         }
30324         
30325         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30326             
30327             this.collapsed = true;
30328             if(this.split){
30329                 this.split.el.hide();
30330             }
30331             if(this.config.animate && skipAnim !== true){
30332                 this.fireEvent("invalidated", this);
30333                 this.animateCollapse();
30334             }else{
30335                 this.el.setLocation(-20000,-20000);
30336                 this.el.hide();
30337                 this.collapsedEl.show();
30338                 this.fireEvent("collapsed", this);
30339                 this.fireEvent("invalidated", this);
30340             }
30341         }
30342         
30343     },
30344
30345     animateCollapse : function(){
30346         // overridden
30347     },
30348
30349     /**
30350      * Expands this region if it was previously collapsed.
30351      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30352      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30353      */
30354     expand : function(e, skipAnim){
30355         if(e) {
30356             e.stopPropagation();
30357         }
30358         if(!this.collapsed || this.el.hasActiveFx()) {
30359             return;
30360         }
30361         if(this.isSlid){
30362             this.afterSlideIn();
30363             skipAnim = true;
30364         }
30365         this.collapsed = false;
30366         if(this.config.animate && skipAnim !== true){
30367             this.animateExpand();
30368         }else{
30369             this.el.show();
30370             if(this.split){
30371                 this.split.el.show();
30372             }
30373             this.collapsedEl.setLocation(-2000,-2000);
30374             this.collapsedEl.hide();
30375             this.fireEvent("invalidated", this);
30376             this.fireEvent("expanded", this);
30377         }
30378     },
30379
30380     animateExpand : function(){
30381         // overridden
30382     },
30383
30384     initTabs : function()
30385     {
30386         this.bodyEl.setStyle("overflow", "hidden");
30387         var ts = new Roo.TabPanel(
30388                 this.bodyEl.dom,
30389                 {
30390                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30391                     disableTooltips: this.config.disableTabTips,
30392                     toolbar : this.config.toolbar
30393                 }
30394         );
30395         if(this.config.hideTabs){
30396             ts.stripWrap.setDisplayed(false);
30397         }
30398         this.tabs = ts;
30399         ts.resizeTabs = this.config.resizeTabs === true;
30400         ts.minTabWidth = this.config.minTabWidth || 40;
30401         ts.maxTabWidth = this.config.maxTabWidth || 250;
30402         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30403         ts.monitorResize = false;
30404         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30405         ts.bodyEl.addClass('x-layout-tabs-body');
30406         this.panels.each(this.initPanelAsTab, this);
30407     },
30408
30409     initPanelAsTab : function(panel){
30410         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30411                     this.config.closeOnTab && panel.isClosable());
30412         if(panel.tabTip !== undefined){
30413             ti.setTooltip(panel.tabTip);
30414         }
30415         ti.on("activate", function(){
30416               this.setActivePanel(panel);
30417         }, this);
30418         if(this.config.closeOnTab){
30419             ti.on("beforeclose", function(t, e){
30420                 e.cancel = true;
30421                 this.remove(panel);
30422             }, this);
30423         }
30424         return ti;
30425     },
30426
30427     updatePanelTitle : function(panel, title){
30428         if(this.activePanel == panel){
30429             this.updateTitle(title);
30430         }
30431         if(this.tabs){
30432             var ti = this.tabs.getTab(panel.getEl().id);
30433             ti.setText(title);
30434             if(panel.tabTip !== undefined){
30435                 ti.setTooltip(panel.tabTip);
30436             }
30437         }
30438     },
30439
30440     updateTitle : function(title){
30441         if(this.titleTextEl && !this.config.title){
30442             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30443         }
30444     },
30445
30446     setActivePanel : function(panel){
30447         panel = this.getPanel(panel);
30448         if(this.activePanel && this.activePanel != panel){
30449             this.activePanel.setActiveState(false);
30450         }
30451         this.activePanel = panel;
30452         panel.setActiveState(true);
30453         if(this.panelSize){
30454             panel.setSize(this.panelSize.width, this.panelSize.height);
30455         }
30456         if(this.closeBtn){
30457             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30458         }
30459         this.updateTitle(panel.getTitle());
30460         if(this.tabs){
30461             this.fireEvent("invalidated", this);
30462         }
30463         this.fireEvent("panelactivated", this, panel);
30464     },
30465
30466     /**
30467      * Shows the specified panel.
30468      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30469      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30470      */
30471     showPanel : function(panel)
30472     {
30473         panel = this.getPanel(panel);
30474         if(panel){
30475             if(this.tabs){
30476                 var tab = this.tabs.getTab(panel.getEl().id);
30477                 if(tab.isHidden()){
30478                     this.tabs.unhideTab(tab.id);
30479                 }
30480                 tab.activate();
30481             }else{
30482                 this.setActivePanel(panel);
30483             }
30484         }
30485         return panel;
30486     },
30487
30488     /**
30489      * Get the active panel for this region.
30490      * @return {Roo.ContentPanel} The active panel or null
30491      */
30492     getActivePanel : function(){
30493         return this.activePanel;
30494     },
30495
30496     validateVisibility : function(){
30497         if(this.panels.getCount() < 1){
30498             this.updateTitle("&#160;");
30499             this.closeBtn.hide();
30500             this.hide();
30501         }else{
30502             if(!this.isVisible()){
30503                 this.show();
30504             }
30505         }
30506     },
30507
30508     /**
30509      * Adds the passed ContentPanel(s) to this region.
30510      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30511      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30512      */
30513     add : function(panel){
30514         if(arguments.length > 1){
30515             for(var i = 0, len = arguments.length; i < len; i++) {
30516                 this.add(arguments[i]);
30517             }
30518             return null;
30519         }
30520         if(this.hasPanel(panel)){
30521             this.showPanel(panel);
30522             return panel;
30523         }
30524         panel.setRegion(this);
30525         this.panels.add(panel);
30526         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30527             this.bodyEl.dom.appendChild(panel.getEl().dom);
30528             if(panel.background !== true){
30529                 this.setActivePanel(panel);
30530             }
30531             this.fireEvent("paneladded", this, panel);
30532             return panel;
30533         }
30534         if(!this.tabs){
30535             this.initTabs();
30536         }else{
30537             this.initPanelAsTab(panel);
30538         }
30539         if(panel.background !== true){
30540             this.tabs.activate(panel.getEl().id);
30541         }
30542         this.fireEvent("paneladded", this, panel);
30543         return panel;
30544     },
30545
30546     /**
30547      * Hides the tab for the specified panel.
30548      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30549      */
30550     hidePanel : function(panel){
30551         if(this.tabs && (panel = this.getPanel(panel))){
30552             this.tabs.hideTab(panel.getEl().id);
30553         }
30554     },
30555
30556     /**
30557      * Unhides the tab for a previously hidden panel.
30558      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30559      */
30560     unhidePanel : function(panel){
30561         if(this.tabs && (panel = this.getPanel(panel))){
30562             this.tabs.unhideTab(panel.getEl().id);
30563         }
30564     },
30565
30566     clearPanels : function(){
30567         while(this.panels.getCount() > 0){
30568              this.remove(this.panels.first());
30569         }
30570     },
30571
30572     /**
30573      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30574      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30575      * @param {Boolean} preservePanel Overrides the config preservePanel option
30576      * @return {Roo.ContentPanel} The panel that was removed
30577      */
30578     remove : function(panel, preservePanel){
30579         panel = this.getPanel(panel);
30580         if(!panel){
30581             return null;
30582         }
30583         var e = {};
30584         this.fireEvent("beforeremove", this, panel, e);
30585         if(e.cancel === true){
30586             return null;
30587         }
30588         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30589         var panelId = panel.getId();
30590         this.panels.removeKey(panelId);
30591         if(preservePanel){
30592             document.body.appendChild(panel.getEl().dom);
30593         }
30594         if(this.tabs){
30595             this.tabs.removeTab(panel.getEl().id);
30596         }else if (!preservePanel){
30597             this.bodyEl.dom.removeChild(panel.getEl().dom);
30598         }
30599         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30600             var p = this.panels.first();
30601             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30602             tempEl.appendChild(p.getEl().dom);
30603             this.bodyEl.update("");
30604             this.bodyEl.dom.appendChild(p.getEl().dom);
30605             tempEl = null;
30606             this.updateTitle(p.getTitle());
30607             this.tabs = null;
30608             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30609             this.setActivePanel(p);
30610         }
30611         panel.setRegion(null);
30612         if(this.activePanel == panel){
30613             this.activePanel = null;
30614         }
30615         if(this.config.autoDestroy !== false && preservePanel !== true){
30616             try{panel.destroy();}catch(e){}
30617         }
30618         this.fireEvent("panelremoved", this, panel);
30619         return panel;
30620     },
30621
30622     /**
30623      * Returns the TabPanel component used by this region
30624      * @return {Roo.TabPanel}
30625      */
30626     getTabs : function(){
30627         return this.tabs;
30628     },
30629
30630     createTool : function(parentEl, className){
30631         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30632             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30633         btn.addClassOnOver("x-layout-tools-button-over");
30634         return btn;
30635     }
30636 });/*
30637  * Based on:
30638  * Ext JS Library 1.1.1
30639  * Copyright(c) 2006-2007, Ext JS, LLC.
30640  *
30641  * Originally Released Under LGPL - original licence link has changed is not relivant.
30642  *
30643  * Fork - LGPL
30644  * <script type="text/javascript">
30645  */
30646  
30647
30648
30649 /**
30650  * @class Roo.SplitLayoutRegion
30651  * @extends Roo.LayoutRegion
30652  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30653  */
30654 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30655     this.cursor = cursor;
30656     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30657 };
30658
30659 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30660     splitTip : "Drag to resize.",
30661     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30662     useSplitTips : false,
30663
30664     applyConfig : function(config){
30665         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30666         if(config.split){
30667             if(!this.split){
30668                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30669                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30670                 /** The SplitBar for this region 
30671                 * @type Roo.SplitBar */
30672                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30673                 this.split.on("moved", this.onSplitMove, this);
30674                 this.split.useShim = config.useShim === true;
30675                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30676                 if(this.useSplitTips){
30677                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30678                 }
30679                 if(config.collapsible){
30680                     this.split.el.on("dblclick", this.collapse,  this);
30681                 }
30682             }
30683             if(typeof config.minSize != "undefined"){
30684                 this.split.minSize = config.minSize;
30685             }
30686             if(typeof config.maxSize != "undefined"){
30687                 this.split.maxSize = config.maxSize;
30688             }
30689             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30690                 this.hideSplitter();
30691             }
30692         }
30693     },
30694
30695     getHMaxSize : function(){
30696          var cmax = this.config.maxSize || 10000;
30697          var center = this.mgr.getRegion("center");
30698          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30699     },
30700
30701     getVMaxSize : function(){
30702          var cmax = this.config.maxSize || 10000;
30703          var center = this.mgr.getRegion("center");
30704          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30705     },
30706
30707     onSplitMove : function(split, newSize){
30708         this.fireEvent("resized", this, newSize);
30709     },
30710     
30711     /** 
30712      * Returns the {@link Roo.SplitBar} for this region.
30713      * @return {Roo.SplitBar}
30714      */
30715     getSplitBar : function(){
30716         return this.split;
30717     },
30718     
30719     hide : function(){
30720         this.hideSplitter();
30721         Roo.SplitLayoutRegion.superclass.hide.call(this);
30722     },
30723
30724     hideSplitter : function(){
30725         if(this.split){
30726             this.split.el.setLocation(-2000,-2000);
30727             this.split.el.hide();
30728         }
30729     },
30730
30731     show : function(){
30732         if(this.split){
30733             this.split.el.show();
30734         }
30735         Roo.SplitLayoutRegion.superclass.show.call(this);
30736     },
30737     
30738     beforeSlide: function(){
30739         if(Roo.isGecko){// firefox overflow auto bug workaround
30740             this.bodyEl.clip();
30741             if(this.tabs) {
30742                 this.tabs.bodyEl.clip();
30743             }
30744             if(this.activePanel){
30745                 this.activePanel.getEl().clip();
30746                 
30747                 if(this.activePanel.beforeSlide){
30748                     this.activePanel.beforeSlide();
30749                 }
30750             }
30751         }
30752     },
30753     
30754     afterSlide : function(){
30755         if(Roo.isGecko){// firefox overflow auto bug workaround
30756             this.bodyEl.unclip();
30757             if(this.tabs) {
30758                 this.tabs.bodyEl.unclip();
30759             }
30760             if(this.activePanel){
30761                 this.activePanel.getEl().unclip();
30762                 if(this.activePanel.afterSlide){
30763                     this.activePanel.afterSlide();
30764                 }
30765             }
30766         }
30767     },
30768
30769     initAutoHide : function(){
30770         if(this.autoHide !== false){
30771             if(!this.autoHideHd){
30772                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30773                 this.autoHideHd = {
30774                     "mouseout": function(e){
30775                         if(!e.within(this.el, true)){
30776                             st.delay(500);
30777                         }
30778                     },
30779                     "mouseover" : function(e){
30780                         st.cancel();
30781                     },
30782                     scope : this
30783                 };
30784             }
30785             this.el.on(this.autoHideHd);
30786         }
30787     },
30788
30789     clearAutoHide : function(){
30790         if(this.autoHide !== false){
30791             this.el.un("mouseout", this.autoHideHd.mouseout);
30792             this.el.un("mouseover", this.autoHideHd.mouseover);
30793         }
30794     },
30795
30796     clearMonitor : function(){
30797         Roo.get(document).un("click", this.slideInIf, this);
30798     },
30799
30800     // these names are backwards but not changed for compat
30801     slideOut : function(){
30802         if(this.isSlid || this.el.hasActiveFx()){
30803             return;
30804         }
30805         this.isSlid = true;
30806         if(this.collapseBtn){
30807             this.collapseBtn.hide();
30808         }
30809         this.closeBtnState = this.closeBtn.getStyle('display');
30810         this.closeBtn.hide();
30811         if(this.stickBtn){
30812             this.stickBtn.show();
30813         }
30814         this.el.show();
30815         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30816         this.beforeSlide();
30817         this.el.setStyle("z-index", 10001);
30818         this.el.slideIn(this.getSlideAnchor(), {
30819             callback: function(){
30820                 this.afterSlide();
30821                 this.initAutoHide();
30822                 Roo.get(document).on("click", this.slideInIf, this);
30823                 this.fireEvent("slideshow", this);
30824             },
30825             scope: this,
30826             block: true
30827         });
30828     },
30829
30830     afterSlideIn : function(){
30831         this.clearAutoHide();
30832         this.isSlid = false;
30833         this.clearMonitor();
30834         this.el.setStyle("z-index", "");
30835         if(this.collapseBtn){
30836             this.collapseBtn.show();
30837         }
30838         this.closeBtn.setStyle('display', this.closeBtnState);
30839         if(this.stickBtn){
30840             this.stickBtn.hide();
30841         }
30842         this.fireEvent("slidehide", this);
30843     },
30844
30845     slideIn : function(cb){
30846         if(!this.isSlid || this.el.hasActiveFx()){
30847             Roo.callback(cb);
30848             return;
30849         }
30850         this.isSlid = false;
30851         this.beforeSlide();
30852         this.el.slideOut(this.getSlideAnchor(), {
30853             callback: function(){
30854                 this.el.setLeftTop(-10000, -10000);
30855                 this.afterSlide();
30856                 this.afterSlideIn();
30857                 Roo.callback(cb);
30858             },
30859             scope: this,
30860             block: true
30861         });
30862     },
30863     
30864     slideInIf : function(e){
30865         if(!e.within(this.el)){
30866             this.slideIn();
30867         }
30868     },
30869
30870     animateCollapse : function(){
30871         this.beforeSlide();
30872         this.el.setStyle("z-index", 20000);
30873         var anchor = this.getSlideAnchor();
30874         this.el.slideOut(anchor, {
30875             callback : function(){
30876                 this.el.setStyle("z-index", "");
30877                 this.collapsedEl.slideIn(anchor, {duration:.3});
30878                 this.afterSlide();
30879                 this.el.setLocation(-10000,-10000);
30880                 this.el.hide();
30881                 this.fireEvent("collapsed", this);
30882             },
30883             scope: this,
30884             block: true
30885         });
30886     },
30887
30888     animateExpand : function(){
30889         this.beforeSlide();
30890         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30891         this.el.setStyle("z-index", 20000);
30892         this.collapsedEl.hide({
30893             duration:.1
30894         });
30895         this.el.slideIn(this.getSlideAnchor(), {
30896             callback : function(){
30897                 this.el.setStyle("z-index", "");
30898                 this.afterSlide();
30899                 if(this.split){
30900                     this.split.el.show();
30901                 }
30902                 this.fireEvent("invalidated", this);
30903                 this.fireEvent("expanded", this);
30904             },
30905             scope: this,
30906             block: true
30907         });
30908     },
30909
30910     anchors : {
30911         "west" : "left",
30912         "east" : "right",
30913         "north" : "top",
30914         "south" : "bottom"
30915     },
30916
30917     sanchors : {
30918         "west" : "l",
30919         "east" : "r",
30920         "north" : "t",
30921         "south" : "b"
30922     },
30923
30924     canchors : {
30925         "west" : "tl-tr",
30926         "east" : "tr-tl",
30927         "north" : "tl-bl",
30928         "south" : "bl-tl"
30929     },
30930
30931     getAnchor : function(){
30932         return this.anchors[this.position];
30933     },
30934
30935     getCollapseAnchor : function(){
30936         return this.canchors[this.position];
30937     },
30938
30939     getSlideAnchor : function(){
30940         return this.sanchors[this.position];
30941     },
30942
30943     getAlignAdj : function(){
30944         var cm = this.cmargins;
30945         switch(this.position){
30946             case "west":
30947                 return [0, 0];
30948             break;
30949             case "east":
30950                 return [0, 0];
30951             break;
30952             case "north":
30953                 return [0, 0];
30954             break;
30955             case "south":
30956                 return [0, 0];
30957             break;
30958         }
30959     },
30960
30961     getExpandAdj : function(){
30962         var c = this.collapsedEl, cm = this.cmargins;
30963         switch(this.position){
30964             case "west":
30965                 return [-(cm.right+c.getWidth()+cm.left), 0];
30966             break;
30967             case "east":
30968                 return [cm.right+c.getWidth()+cm.left, 0];
30969             break;
30970             case "north":
30971                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30972             break;
30973             case "south":
30974                 return [0, cm.top+cm.bottom+c.getHeight()];
30975             break;
30976         }
30977     }
30978 });/*
30979  * Based on:
30980  * Ext JS Library 1.1.1
30981  * Copyright(c) 2006-2007, Ext JS, LLC.
30982  *
30983  * Originally Released Under LGPL - original licence link has changed is not relivant.
30984  *
30985  * Fork - LGPL
30986  * <script type="text/javascript">
30987  */
30988 /*
30989  * These classes are private internal classes
30990  */
30991 Roo.CenterLayoutRegion = function(mgr, config){
30992     Roo.LayoutRegion.call(this, mgr, config, "center");
30993     this.visible = true;
30994     this.minWidth = config.minWidth || 20;
30995     this.minHeight = config.minHeight || 20;
30996 };
30997
30998 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30999     hide : function(){
31000         // center panel can't be hidden
31001     },
31002     
31003     show : function(){
31004         // center panel can't be hidden
31005     },
31006     
31007     getMinWidth: function(){
31008         return this.minWidth;
31009     },
31010     
31011     getMinHeight: function(){
31012         return this.minHeight;
31013     }
31014 });
31015
31016
31017 Roo.NorthLayoutRegion = function(mgr, config){
31018     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31019     if(this.split){
31020         this.split.placement = Roo.SplitBar.TOP;
31021         this.split.orientation = Roo.SplitBar.VERTICAL;
31022         this.split.el.addClass("x-layout-split-v");
31023     }
31024     var size = config.initialSize || config.height;
31025     if(typeof size != "undefined"){
31026         this.el.setHeight(size);
31027     }
31028 };
31029 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31030     orientation: Roo.SplitBar.VERTICAL,
31031     getBox : function(){
31032         if(this.collapsed){
31033             return this.collapsedEl.getBox();
31034         }
31035         var box = this.el.getBox();
31036         if(this.split){
31037             box.height += this.split.el.getHeight();
31038         }
31039         return box;
31040     },
31041     
31042     updateBox : function(box){
31043         if(this.split && !this.collapsed){
31044             box.height -= this.split.el.getHeight();
31045             this.split.el.setLeft(box.x);
31046             this.split.el.setTop(box.y+box.height);
31047             this.split.el.setWidth(box.width);
31048         }
31049         if(this.collapsed){
31050             this.updateBody(box.width, null);
31051         }
31052         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31053     }
31054 });
31055
31056 Roo.SouthLayoutRegion = function(mgr, config){
31057     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31058     if(this.split){
31059         this.split.placement = Roo.SplitBar.BOTTOM;
31060         this.split.orientation = Roo.SplitBar.VERTICAL;
31061         this.split.el.addClass("x-layout-split-v");
31062     }
31063     var size = config.initialSize || config.height;
31064     if(typeof size != "undefined"){
31065         this.el.setHeight(size);
31066     }
31067 };
31068 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31069     orientation: Roo.SplitBar.VERTICAL,
31070     getBox : function(){
31071         if(this.collapsed){
31072             return this.collapsedEl.getBox();
31073         }
31074         var box = this.el.getBox();
31075         if(this.split){
31076             var sh = this.split.el.getHeight();
31077             box.height += sh;
31078             box.y -= sh;
31079         }
31080         return box;
31081     },
31082     
31083     updateBox : function(box){
31084         if(this.split && !this.collapsed){
31085             var sh = this.split.el.getHeight();
31086             box.height -= sh;
31087             box.y += sh;
31088             this.split.el.setLeft(box.x);
31089             this.split.el.setTop(box.y-sh);
31090             this.split.el.setWidth(box.width);
31091         }
31092         if(this.collapsed){
31093             this.updateBody(box.width, null);
31094         }
31095         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31096     }
31097 });
31098
31099 Roo.EastLayoutRegion = function(mgr, config){
31100     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31101     if(this.split){
31102         this.split.placement = Roo.SplitBar.RIGHT;
31103         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31104         this.split.el.addClass("x-layout-split-h");
31105     }
31106     var size = config.initialSize || config.width;
31107     if(typeof size != "undefined"){
31108         this.el.setWidth(size);
31109     }
31110 };
31111 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31112     orientation: Roo.SplitBar.HORIZONTAL,
31113     getBox : function(){
31114         if(this.collapsed){
31115             return this.collapsedEl.getBox();
31116         }
31117         var box = this.el.getBox();
31118         if(this.split){
31119             var sw = this.split.el.getWidth();
31120             box.width += sw;
31121             box.x -= sw;
31122         }
31123         return box;
31124     },
31125
31126     updateBox : function(box){
31127         if(this.split && !this.collapsed){
31128             var sw = this.split.el.getWidth();
31129             box.width -= sw;
31130             this.split.el.setLeft(box.x);
31131             this.split.el.setTop(box.y);
31132             this.split.el.setHeight(box.height);
31133             box.x += sw;
31134         }
31135         if(this.collapsed){
31136             this.updateBody(null, box.height);
31137         }
31138         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31139     }
31140 });
31141
31142 Roo.WestLayoutRegion = function(mgr, config){
31143     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31144     if(this.split){
31145         this.split.placement = Roo.SplitBar.LEFT;
31146         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31147         this.split.el.addClass("x-layout-split-h");
31148     }
31149     var size = config.initialSize || config.width;
31150     if(typeof size != "undefined"){
31151         this.el.setWidth(size);
31152     }
31153 };
31154 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31155     orientation: Roo.SplitBar.HORIZONTAL,
31156     getBox : function(){
31157         if(this.collapsed){
31158             return this.collapsedEl.getBox();
31159         }
31160         var box = this.el.getBox();
31161         if(this.split){
31162             box.width += this.split.el.getWidth();
31163         }
31164         return box;
31165     },
31166     
31167     updateBox : function(box){
31168         if(this.split && !this.collapsed){
31169             var sw = this.split.el.getWidth();
31170             box.width -= sw;
31171             this.split.el.setLeft(box.x+box.width);
31172             this.split.el.setTop(box.y);
31173             this.split.el.setHeight(box.height);
31174         }
31175         if(this.collapsed){
31176             this.updateBody(null, box.height);
31177         }
31178         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31179     }
31180 });
31181 /*
31182  * Based on:
31183  * Ext JS Library 1.1.1
31184  * Copyright(c) 2006-2007, Ext JS, LLC.
31185  *
31186  * Originally Released Under LGPL - original licence link has changed is not relivant.
31187  *
31188  * Fork - LGPL
31189  * <script type="text/javascript">
31190  */
31191  
31192  
31193 /*
31194  * Private internal class for reading and applying state
31195  */
31196 Roo.LayoutStateManager = function(layout){
31197      // default empty state
31198      this.state = {
31199         north: {},
31200         south: {},
31201         east: {},
31202         west: {}       
31203     };
31204 };
31205
31206 Roo.LayoutStateManager.prototype = {
31207     init : function(layout, provider){
31208         this.provider = provider;
31209         var state = provider.get(layout.id+"-layout-state");
31210         if(state){
31211             var wasUpdating = layout.isUpdating();
31212             if(!wasUpdating){
31213                 layout.beginUpdate();
31214             }
31215             for(var key in state){
31216                 if(typeof state[key] != "function"){
31217                     var rstate = state[key];
31218                     var r = layout.getRegion(key);
31219                     if(r && rstate){
31220                         if(rstate.size){
31221                             r.resizeTo(rstate.size);
31222                         }
31223                         if(rstate.collapsed == true){
31224                             r.collapse(true);
31225                         }else{
31226                             r.expand(null, true);
31227                         }
31228                     }
31229                 }
31230             }
31231             if(!wasUpdating){
31232                 layout.endUpdate();
31233             }
31234             this.state = state; 
31235         }
31236         this.layout = layout;
31237         layout.on("regionresized", this.onRegionResized, this);
31238         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31239         layout.on("regionexpanded", this.onRegionExpanded, this);
31240     },
31241     
31242     storeState : function(){
31243         this.provider.set(this.layout.id+"-layout-state", this.state);
31244     },
31245     
31246     onRegionResized : function(region, newSize){
31247         this.state[region.getPosition()].size = newSize;
31248         this.storeState();
31249     },
31250     
31251     onRegionCollapsed : function(region){
31252         this.state[region.getPosition()].collapsed = true;
31253         this.storeState();
31254     },
31255     
31256     onRegionExpanded : function(region){
31257         this.state[region.getPosition()].collapsed = false;
31258         this.storeState();
31259     }
31260 };/*
31261  * Based on:
31262  * Ext JS Library 1.1.1
31263  * Copyright(c) 2006-2007, Ext JS, LLC.
31264  *
31265  * Originally Released Under LGPL - original licence link has changed is not relivant.
31266  *
31267  * Fork - LGPL
31268  * <script type="text/javascript">
31269  */
31270 /**
31271  * @class Roo.ContentPanel
31272  * @extends Roo.util.Observable
31273  * @children Roo.form.Form Roo.JsonView Roo.View
31274  * A basic ContentPanel element.
31275  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31276  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31277  * @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
31278  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31279  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31280  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31281  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
31282  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31283  * @cfg {String} title          The title for this panel
31284  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31285  * @cfg {String} url            Calls {@link #setUrl} with this value
31286  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31287  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31288  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31289  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31290  * @cfg {String}    style  Extra style to add to the content panel
31291  * @cfg {Roo.menu.Menu} menu  popup menu
31292
31293  * @constructor
31294  * Create a new ContentPanel.
31295  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31296  * @param {String/Object} config A string to set only the title or a config object
31297  * @param {String} content (optional) Set the HTML content for this panel
31298  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31299  */
31300 Roo.ContentPanel = function(el, config, content){
31301     
31302      
31303     /*
31304     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31305         config = el;
31306         el = Roo.id();
31307     }
31308     if (config && config.parentLayout) { 
31309         el = config.parentLayout.el.createChild(); 
31310     }
31311     */
31312     if(el.autoCreate){ // xtype is available if this is called from factory
31313         config = el;
31314         el = Roo.id();
31315     }
31316     this.el = Roo.get(el);
31317     if(!this.el && config && config.autoCreate){
31318         if(typeof config.autoCreate == "object"){
31319             if(!config.autoCreate.id){
31320                 config.autoCreate.id = config.id||el;
31321             }
31322             this.el = Roo.DomHelper.append(document.body,
31323                         config.autoCreate, true);
31324         }else{
31325             this.el = Roo.DomHelper.append(document.body,
31326                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31327         }
31328     }
31329     
31330     
31331     this.closable = false;
31332     this.loaded = false;
31333     this.active = false;
31334     if(typeof config == "string"){
31335         this.title = config;
31336     }else{
31337         Roo.apply(this, config);
31338     }
31339     
31340     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31341         this.wrapEl = this.el.wrap();
31342         this.toolbar.container = this.el.insertSibling(false, 'before');
31343         this.toolbar = new Roo.Toolbar(this.toolbar);
31344     }
31345     
31346     // xtype created footer. - not sure if will work as we normally have to render first..
31347     if (this.footer && !this.footer.el && this.footer.xtype) {
31348         if (!this.wrapEl) {
31349             this.wrapEl = this.el.wrap();
31350         }
31351     
31352         this.footer.container = this.wrapEl.createChild();
31353          
31354         this.footer = Roo.factory(this.footer, Roo);
31355         
31356     }
31357     
31358     if(this.resizeEl){
31359         this.resizeEl = Roo.get(this.resizeEl, true);
31360     }else{
31361         this.resizeEl = this.el;
31362     }
31363     // handle view.xtype
31364     
31365  
31366     
31367     
31368     this.addEvents({
31369         /**
31370          * @event activate
31371          * Fires when this panel is activated. 
31372          * @param {Roo.ContentPanel} this
31373          */
31374         "activate" : true,
31375         /**
31376          * @event deactivate
31377          * Fires when this panel is activated. 
31378          * @param {Roo.ContentPanel} this
31379          */
31380         "deactivate" : true,
31381
31382         /**
31383          * @event resize
31384          * Fires when this panel is resized if fitToFrame is true.
31385          * @param {Roo.ContentPanel} this
31386          * @param {Number} width The width after any component adjustments
31387          * @param {Number} height The height after any component adjustments
31388          */
31389         "resize" : true,
31390         
31391          /**
31392          * @event render
31393          * Fires when this tab is created
31394          * @param {Roo.ContentPanel} this
31395          */
31396         "render" : true
31397          
31398         
31399     });
31400     
31401
31402     
31403     
31404     if(this.autoScroll){
31405         this.resizeEl.setStyle("overflow", "auto");
31406     } else {
31407         // fix randome scrolling
31408         this.el.on('scroll', function() {
31409             Roo.log('fix random scolling');
31410             this.scrollTo('top',0); 
31411         });
31412     }
31413     content = content || this.content;
31414     if(content){
31415         this.setContent(content);
31416     }
31417     if(config && config.url){
31418         this.setUrl(this.url, this.params, this.loadOnce);
31419     }
31420     
31421     
31422     
31423     Roo.ContentPanel.superclass.constructor.call(this);
31424     
31425     if (this.view && typeof(this.view.xtype) != 'undefined') {
31426         this.view.el = this.el.appendChild(document.createElement("div"));
31427         this.view = Roo.factory(this.view); 
31428         this.view.render  &&  this.view.render(false, '');  
31429     }
31430     
31431     
31432     this.fireEvent('render', this);
31433 };
31434
31435 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31436     tabTip:'',
31437     setRegion : function(region){
31438         this.region = region;
31439         if(region){
31440            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31441         }else{
31442            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31443         } 
31444     },
31445     
31446     /**
31447      * Returns the toolbar for this Panel if one was configured. 
31448      * @return {Roo.Toolbar} 
31449      */
31450     getToolbar : function(){
31451         return this.toolbar;
31452     },
31453     
31454     setActiveState : function(active){
31455         this.active = active;
31456         if(!active){
31457             this.fireEvent("deactivate", this);
31458         }else{
31459             this.fireEvent("activate", this);
31460         }
31461     },
31462     /**
31463      * Updates this panel's element
31464      * @param {String} content The new content
31465      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31466     */
31467     setContent : function(content, loadScripts){
31468         this.el.update(content, loadScripts);
31469     },
31470
31471     ignoreResize : function(w, h){
31472         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31473             return true;
31474         }else{
31475             this.lastSize = {width: w, height: h};
31476             return false;
31477         }
31478     },
31479     /**
31480      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31481      * @return {Roo.UpdateManager} The UpdateManager
31482      */
31483     getUpdateManager : function(){
31484         return this.el.getUpdateManager();
31485     },
31486      /**
31487      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31488      * @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:
31489 <pre><code>
31490 panel.load({
31491     url: "your-url.php",
31492     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31493     callback: yourFunction,
31494     scope: yourObject, //(optional scope)
31495     discardUrl: false,
31496     nocache: false,
31497     text: "Loading...",
31498     timeout: 30,
31499     scripts: false
31500 });
31501 </code></pre>
31502      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31503      * 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.
31504      * @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}
31505      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31506      * @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.
31507      * @return {Roo.ContentPanel} this
31508      */
31509     load : function(){
31510         var um = this.el.getUpdateManager();
31511         um.update.apply(um, arguments);
31512         return this;
31513     },
31514
31515
31516     /**
31517      * 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.
31518      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31519      * @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)
31520      * @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)
31521      * @return {Roo.UpdateManager} The UpdateManager
31522      */
31523     setUrl : function(url, params, loadOnce){
31524         if(this.refreshDelegate){
31525             this.removeListener("activate", this.refreshDelegate);
31526         }
31527         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31528         this.on("activate", this.refreshDelegate);
31529         return this.el.getUpdateManager();
31530     },
31531     
31532     _handleRefresh : function(url, params, loadOnce){
31533         if(!loadOnce || !this.loaded){
31534             var updater = this.el.getUpdateManager();
31535             updater.update(url, params, this._setLoaded.createDelegate(this));
31536         }
31537     },
31538     
31539     _setLoaded : function(){
31540         this.loaded = true;
31541     }, 
31542     
31543     /**
31544      * Returns this panel's id
31545      * @return {String} 
31546      */
31547     getId : function(){
31548         return this.el.id;
31549     },
31550     
31551     /** 
31552      * Returns this panel's element - used by regiosn to add.
31553      * @return {Roo.Element} 
31554      */
31555     getEl : function(){
31556         return this.wrapEl || this.el;
31557     },
31558     
31559     adjustForComponents : function(width, height)
31560     {
31561         //Roo.log('adjustForComponents ');
31562         if(this.resizeEl != this.el){
31563             width -= this.el.getFrameWidth('lr');
31564             height -= this.el.getFrameWidth('tb');
31565         }
31566         if(this.toolbar){
31567             var te = this.toolbar.getEl();
31568             height -= te.getHeight();
31569             te.setWidth(width);
31570         }
31571         if(this.footer){
31572             var te = this.footer.getEl();
31573             //Roo.log("footer:" + te.getHeight());
31574             
31575             height -= te.getHeight();
31576             te.setWidth(width);
31577         }
31578         
31579         
31580         if(this.adjustments){
31581             width += this.adjustments[0];
31582             height += this.adjustments[1];
31583         }
31584         return {"width": width, "height": height};
31585     },
31586     
31587     setSize : function(width, height){
31588         if(this.fitToFrame && !this.ignoreResize(width, height)){
31589             if(this.fitContainer && this.resizeEl != this.el){
31590                 this.el.setSize(width, height);
31591             }
31592             var size = this.adjustForComponents(width, height);
31593             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31594             this.fireEvent('resize', this, size.width, size.height);
31595         }
31596     },
31597     
31598     /**
31599      * Returns this panel's title
31600      * @return {String} 
31601      */
31602     getTitle : function(){
31603         return this.title;
31604     },
31605     
31606     /**
31607      * Set this panel's title
31608      * @param {String} title
31609      */
31610     setTitle : function(title){
31611         this.title = title;
31612         if(this.region){
31613             this.region.updatePanelTitle(this, title);
31614         }
31615     },
31616     
31617     /**
31618      * Returns true is this panel was configured to be closable
31619      * @return {Boolean} 
31620      */
31621     isClosable : function(){
31622         return this.closable;
31623     },
31624     
31625     beforeSlide : function(){
31626         this.el.clip();
31627         this.resizeEl.clip();
31628     },
31629     
31630     afterSlide : function(){
31631         this.el.unclip();
31632         this.resizeEl.unclip();
31633     },
31634     
31635     /**
31636      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31637      *   Will fail silently if the {@link #setUrl} method has not been called.
31638      *   This does not activate the panel, just updates its content.
31639      */
31640     refresh : function(){
31641         if(this.refreshDelegate){
31642            this.loaded = false;
31643            this.refreshDelegate();
31644         }
31645     },
31646     
31647     /**
31648      * Destroys this panel
31649      */
31650     destroy : function(){
31651         this.el.removeAllListeners();
31652         var tempEl = document.createElement("span");
31653         tempEl.appendChild(this.el.dom);
31654         tempEl.innerHTML = "";
31655         this.el.remove();
31656         this.el = null;
31657     },
31658     
31659     /**
31660      * form - if the content panel contains a form - this is a reference to it.
31661      * @type {Roo.form.Form}
31662      */
31663     form : false,
31664     /**
31665      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31666      *    This contains a reference to it.
31667      * @type {Roo.View}
31668      */
31669     view : false,
31670     
31671       /**
31672      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31673      * <pre><code>
31674
31675 layout.addxtype({
31676        xtype : 'Form',
31677        items: [ .... ]
31678    }
31679 );
31680
31681 </code></pre>
31682      * @param {Object} cfg Xtype definition of item to add.
31683      */
31684     
31685     addxtype : function(cfg) {
31686         // add form..
31687         if (cfg.xtype.match(/^Form$/)) {
31688             
31689             var el;
31690             //if (this.footer) {
31691             //    el = this.footer.container.insertSibling(false, 'before');
31692             //} else {
31693                 el = this.el.createChild();
31694             //}
31695
31696             this.form = new  Roo.form.Form(cfg);
31697             
31698             
31699             if ( this.form.allItems.length) {
31700                 this.form.render(el.dom);
31701             }
31702             return this.form;
31703         }
31704         // should only have one of theses..
31705         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31706             // views.. should not be just added - used named prop 'view''
31707             
31708             cfg.el = this.el.appendChild(document.createElement("div"));
31709             // factory?
31710             
31711             var ret = new Roo.factory(cfg);
31712              
31713              ret.render && ret.render(false, ''); // render blank..
31714             this.view = ret;
31715             return ret;
31716         }
31717         return false;
31718     }
31719 });
31720
31721 /**
31722  * @class Roo.GridPanel
31723  * @extends Roo.ContentPanel
31724  * @constructor
31725  * Create a new GridPanel.
31726  * @param {Roo.grid.Grid} grid The grid for this panel
31727  * @param {String/Object} config A string to set only the panel's title, or a config object
31728  */
31729 Roo.GridPanel = function(grid, config){
31730     
31731   
31732     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31733         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31734         
31735     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31736     
31737     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31738     
31739     if(this.toolbar){
31740         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31741     }
31742     // xtype created footer. - not sure if will work as we normally have to render first..
31743     if (this.footer && !this.footer.el && this.footer.xtype) {
31744         
31745         this.footer.container = this.grid.getView().getFooterPanel(true);
31746         this.footer.dataSource = this.grid.dataSource;
31747         this.footer = Roo.factory(this.footer, Roo);
31748         
31749     }
31750     
31751     grid.monitorWindowResize = false; // turn off autosizing
31752     grid.autoHeight = false;
31753     grid.autoWidth = false;
31754     this.grid = grid;
31755     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31756 };
31757
31758 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31759     getId : function(){
31760         return this.grid.id;
31761     },
31762     
31763     /**
31764      * Returns the grid for this panel
31765      * @return {Roo.grid.Grid} 
31766      */
31767     getGrid : function(){
31768         return this.grid;    
31769     },
31770     
31771     setSize : function(width, height){
31772         if(!this.ignoreResize(width, height)){
31773             var grid = this.grid;
31774             var size = this.adjustForComponents(width, height);
31775             grid.getGridEl().setSize(size.width, size.height);
31776             grid.autoSize();
31777         }
31778     },
31779     
31780     beforeSlide : function(){
31781         this.grid.getView().scroller.clip();
31782     },
31783     
31784     afterSlide : function(){
31785         this.grid.getView().scroller.unclip();
31786     },
31787     
31788     destroy : function(){
31789         this.grid.destroy();
31790         delete this.grid;
31791         Roo.GridPanel.superclass.destroy.call(this); 
31792     }
31793 });
31794
31795
31796 /**
31797  * @class Roo.NestedLayoutPanel
31798  * @extends Roo.ContentPanel
31799  * @constructor
31800  * Create a new NestedLayoutPanel.
31801  * 
31802  * 
31803  * @param {Roo.BorderLayout} layout [required] The layout for this panel
31804  * @param {String/Object} config A string to set only the title or a config object
31805  */
31806 Roo.NestedLayoutPanel = function(layout, config)
31807 {
31808     // construct with only one argument..
31809     /* FIXME - implement nicer consturctors
31810     if (layout.layout) {
31811         config = layout;
31812         layout = config.layout;
31813         delete config.layout;
31814     }
31815     if (layout.xtype && !layout.getEl) {
31816         // then layout needs constructing..
31817         layout = Roo.factory(layout, Roo);
31818     }
31819     */
31820     
31821     
31822     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31823     
31824     layout.monitorWindowResize = false; // turn off autosizing
31825     this.layout = layout;
31826     this.layout.getEl().addClass("x-layout-nested-layout");
31827     
31828     
31829     
31830     
31831 };
31832
31833 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31834
31835     setSize : function(width, height){
31836         if(!this.ignoreResize(width, height)){
31837             var size = this.adjustForComponents(width, height);
31838             var el = this.layout.getEl();
31839             el.setSize(size.width, size.height);
31840             var touch = el.dom.offsetWidth;
31841             this.layout.layout();
31842             // ie requires a double layout on the first pass
31843             if(Roo.isIE && !this.initialized){
31844                 this.initialized = true;
31845                 this.layout.layout();
31846             }
31847         }
31848     },
31849     
31850     // activate all subpanels if not currently active..
31851     
31852     setActiveState : function(active){
31853         this.active = active;
31854         if(!active){
31855             this.fireEvent("deactivate", this);
31856             return;
31857         }
31858         
31859         this.fireEvent("activate", this);
31860         // not sure if this should happen before or after..
31861         if (!this.layout) {
31862             return; // should not happen..
31863         }
31864         var reg = false;
31865         for (var r in this.layout.regions) {
31866             reg = this.layout.getRegion(r);
31867             if (reg.getActivePanel()) {
31868                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31869                 reg.setActivePanel(reg.getActivePanel());
31870                 continue;
31871             }
31872             if (!reg.panels.length) {
31873                 continue;
31874             }
31875             reg.showPanel(reg.getPanel(0));
31876         }
31877         
31878         
31879         
31880         
31881     },
31882     
31883     /**
31884      * Returns the nested BorderLayout for this panel
31885      * @return {Roo.BorderLayout} 
31886      */
31887     getLayout : function(){
31888         return this.layout;
31889     },
31890     
31891      /**
31892      * Adds a xtype elements to the layout of the nested panel
31893      * <pre><code>
31894
31895 panel.addxtype({
31896        xtype : 'ContentPanel',
31897        region: 'west',
31898        items: [ .... ]
31899    }
31900 );
31901
31902 panel.addxtype({
31903         xtype : 'NestedLayoutPanel',
31904         region: 'west',
31905         layout: {
31906            center: { },
31907            west: { }   
31908         },
31909         items : [ ... list of content panels or nested layout panels.. ]
31910    }
31911 );
31912 </code></pre>
31913      * @param {Object} cfg Xtype definition of item to add.
31914      */
31915     addxtype : function(cfg) {
31916         return this.layout.addxtype(cfg);
31917     
31918     }
31919 });
31920
31921 Roo.ScrollPanel = function(el, config, content){
31922     config = config || {};
31923     config.fitToFrame = true;
31924     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31925     
31926     this.el.dom.style.overflow = "hidden";
31927     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31928     this.el.removeClass("x-layout-inactive-content");
31929     this.el.on("mousewheel", this.onWheel, this);
31930
31931     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31932     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31933     up.unselectable(); down.unselectable();
31934     up.on("click", this.scrollUp, this);
31935     down.on("click", this.scrollDown, this);
31936     up.addClassOnOver("x-scroller-btn-over");
31937     down.addClassOnOver("x-scroller-btn-over");
31938     up.addClassOnClick("x-scroller-btn-click");
31939     down.addClassOnClick("x-scroller-btn-click");
31940     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31941
31942     this.resizeEl = this.el;
31943     this.el = wrap; this.up = up; this.down = down;
31944 };
31945
31946 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31947     increment : 100,
31948     wheelIncrement : 5,
31949     scrollUp : function(){
31950         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31951     },
31952
31953     scrollDown : function(){
31954         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31955     },
31956
31957     afterScroll : function(){
31958         var el = this.resizeEl;
31959         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31960         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31961         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31962     },
31963
31964     setSize : function(){
31965         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31966         this.afterScroll();
31967     },
31968
31969     onWheel : function(e){
31970         var d = e.getWheelDelta();
31971         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31972         this.afterScroll();
31973         e.stopEvent();
31974     },
31975
31976     setContent : function(content, loadScripts){
31977         this.resizeEl.update(content, loadScripts);
31978     }
31979
31980 });
31981
31982
31983
31984 /**
31985  * @class Roo.TreePanel
31986  * @extends Roo.ContentPanel
31987  * Treepanel component
31988  * 
31989  * @constructor
31990  * Create a new TreePanel. - defaults to fit/scoll contents.
31991  * @param {String/Object} config A string to set only the panel's title, or a config object
31992  */
31993 Roo.TreePanel = function(config){
31994     var el = config.el;
31995     var tree = config.tree;
31996     delete config.tree; 
31997     delete config.el; // hopefull!
31998     
31999     // wrapper for IE7 strict & safari scroll issue
32000     
32001     var treeEl = el.createChild();
32002     config.resizeEl = treeEl;
32003     
32004     
32005     
32006     Roo.TreePanel.superclass.constructor.call(this, el, config);
32007  
32008  
32009     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32010     //console.log(tree);
32011     this.on('activate', function()
32012     {
32013         if (this.tree.rendered) {
32014             return;
32015         }
32016         //console.log('render tree');
32017         this.tree.render();
32018     });
32019     // this should not be needed.. - it's actually the 'el' that resizes?
32020     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32021     
32022     //this.on('resize',  function (cp, w, h) {
32023     //        this.tree.innerCt.setWidth(w);
32024     //        this.tree.innerCt.setHeight(h);
32025     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32026     //});
32027
32028         
32029     
32030 };
32031
32032 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32033     fitToFrame : true,
32034     autoScroll : true,
32035     /*
32036      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
32037      */
32038     tree : false
32039
32040 });
32041
32042
32043
32044
32045
32046
32047
32048
32049
32050
32051
32052 /*
32053  * Based on:
32054  * Ext JS Library 1.1.1
32055  * Copyright(c) 2006-2007, Ext JS, LLC.
32056  *
32057  * Originally Released Under LGPL - original licence link has changed is not relivant.
32058  *
32059  * Fork - LGPL
32060  * <script type="text/javascript">
32061  */
32062  
32063
32064 /**
32065  * @class Roo.ReaderLayout
32066  * @extends Roo.BorderLayout
32067  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32068  * center region containing two nested regions (a top one for a list view and one for item preview below),
32069  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32070  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32071  * expedites the setup of the overall layout and regions for this common application style.
32072  * Example:
32073  <pre><code>
32074 var reader = new Roo.ReaderLayout();
32075 var CP = Roo.ContentPanel;  // shortcut for adding
32076
32077 reader.beginUpdate();
32078 reader.add("north", new CP("north", "North"));
32079 reader.add("west", new CP("west", {title: "West"}));
32080 reader.add("east", new CP("east", {title: "East"}));
32081
32082 reader.regions.listView.add(new CP("listView", "List"));
32083 reader.regions.preview.add(new CP("preview", "Preview"));
32084 reader.endUpdate();
32085 </code></pre>
32086 * @constructor
32087 * Create a new ReaderLayout
32088 * @param {Object} config Configuration options
32089 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32090 * document.body if omitted)
32091 */
32092 Roo.ReaderLayout = function(config, renderTo){
32093     var c = config || {size:{}};
32094     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32095         north: c.north !== false ? Roo.apply({
32096             split:false,
32097             initialSize: 32,
32098             titlebar: false
32099         }, c.north) : false,
32100         west: c.west !== false ? Roo.apply({
32101             split:true,
32102             initialSize: 200,
32103             minSize: 175,
32104             maxSize: 400,
32105             titlebar: true,
32106             collapsible: true,
32107             animate: true,
32108             margins:{left:5,right:0,bottom:5,top:5},
32109             cmargins:{left:5,right:5,bottom:5,top:5}
32110         }, c.west) : false,
32111         east: c.east !== false ? Roo.apply({
32112             split:true,
32113             initialSize: 200,
32114             minSize: 175,
32115             maxSize: 400,
32116             titlebar: true,
32117             collapsible: true,
32118             animate: true,
32119             margins:{left:0,right:5,bottom:5,top:5},
32120             cmargins:{left:5,right:5,bottom:5,top:5}
32121         }, c.east) : false,
32122         center: Roo.apply({
32123             tabPosition: 'top',
32124             autoScroll:false,
32125             closeOnTab: true,
32126             titlebar:false,
32127             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32128         }, c.center)
32129     });
32130
32131     this.el.addClass('x-reader');
32132
32133     this.beginUpdate();
32134
32135     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32136         south: c.preview !== false ? Roo.apply({
32137             split:true,
32138             initialSize: 200,
32139             minSize: 100,
32140             autoScroll:true,
32141             collapsible:true,
32142             titlebar: true,
32143             cmargins:{top:5,left:0, right:0, bottom:0}
32144         }, c.preview) : false,
32145         center: Roo.apply({
32146             autoScroll:false,
32147             titlebar:false,
32148             minHeight:200
32149         }, c.listView)
32150     });
32151     this.add('center', new Roo.NestedLayoutPanel(inner,
32152             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32153
32154     this.endUpdate();
32155
32156     this.regions.preview = inner.getRegion('south');
32157     this.regions.listView = inner.getRegion('center');
32158 };
32159
32160 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32161  * Based on:
32162  * Ext JS Library 1.1.1
32163  * Copyright(c) 2006-2007, Ext JS, LLC.
32164  *
32165  * Originally Released Under LGPL - original licence link has changed is not relivant.
32166  *
32167  * Fork - LGPL
32168  * <script type="text/javascript">
32169  */
32170  
32171 /**
32172  * @class Roo.grid.Grid
32173  * @extends Roo.util.Observable
32174  * This class represents the primary interface of a component based grid control.
32175  * <br><br>Usage:<pre><code>
32176  var grid = new Roo.grid.Grid("my-container-id", {
32177      ds: myDataStore,
32178      cm: myColModel,
32179      selModel: mySelectionModel,
32180      autoSizeColumns: true,
32181      monitorWindowResize: false,
32182      trackMouseOver: true
32183  });
32184  // set any options
32185  grid.render();
32186  * </code></pre>
32187  * <b>Common Problems:</b><br/>
32188  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32189  * element will correct this<br/>
32190  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32191  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32192  * are unpredictable.<br/>
32193  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32194  * grid to calculate dimensions/offsets.<br/>
32195   * @constructor
32196  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32197  * The container MUST have some type of size defined for the grid to fill. The container will be
32198  * automatically set to position relative if it isn't already.
32199  * @param {Object} config A config object that sets properties on this grid.
32200  */
32201 Roo.grid.Grid = function(container, config){
32202         // initialize the container
32203         this.container = Roo.get(container);
32204         this.container.update("");
32205         this.container.setStyle("overflow", "hidden");
32206     this.container.addClass('x-grid-container');
32207
32208     this.id = this.container.id;
32209
32210     Roo.apply(this, config);
32211     // check and correct shorthanded configs
32212     if(this.ds){
32213         this.dataSource = this.ds;
32214         delete this.ds;
32215     }
32216     if(this.cm){
32217         this.colModel = this.cm;
32218         delete this.cm;
32219     }
32220     if(this.sm){
32221         this.selModel = this.sm;
32222         delete this.sm;
32223     }
32224
32225     if (this.selModel) {
32226         this.selModel = Roo.factory(this.selModel, Roo.grid);
32227         this.sm = this.selModel;
32228         this.sm.xmodule = this.xmodule || false;
32229     }
32230     if (typeof(this.colModel.config) == 'undefined') {
32231         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32232         this.cm = this.colModel;
32233         this.cm.xmodule = this.xmodule || false;
32234     }
32235     if (this.dataSource) {
32236         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32237         this.ds = this.dataSource;
32238         this.ds.xmodule = this.xmodule || false;
32239          
32240     }
32241     
32242     
32243     
32244     if(this.width){
32245         this.container.setWidth(this.width);
32246     }
32247
32248     if(this.height){
32249         this.container.setHeight(this.height);
32250     }
32251     /** @private */
32252         this.addEvents({
32253         // raw events
32254         /**
32255          * @event click
32256          * The raw click event for the entire grid.
32257          * @param {Roo.EventObject} e
32258          */
32259         "click" : true,
32260         /**
32261          * @event dblclick
32262          * The raw dblclick event for the entire grid.
32263          * @param {Roo.EventObject} e
32264          */
32265         "dblclick" : true,
32266         /**
32267          * @event contextmenu
32268          * The raw contextmenu event for the entire grid.
32269          * @param {Roo.EventObject} e
32270          */
32271         "contextmenu" : true,
32272         /**
32273          * @event mousedown
32274          * The raw mousedown event for the entire grid.
32275          * @param {Roo.EventObject} e
32276          */
32277         "mousedown" : true,
32278         /**
32279          * @event mouseup
32280          * The raw mouseup event for the entire grid.
32281          * @param {Roo.EventObject} e
32282          */
32283         "mouseup" : true,
32284         /**
32285          * @event mouseover
32286          * The raw mouseover event for the entire grid.
32287          * @param {Roo.EventObject} e
32288          */
32289         "mouseover" : true,
32290         /**
32291          * @event mouseout
32292          * The raw mouseout event for the entire grid.
32293          * @param {Roo.EventObject} e
32294          */
32295         "mouseout" : true,
32296         /**
32297          * @event keypress
32298          * The raw keypress event for the entire grid.
32299          * @param {Roo.EventObject} e
32300          */
32301         "keypress" : true,
32302         /**
32303          * @event keydown
32304          * The raw keydown event for the entire grid.
32305          * @param {Roo.EventObject} e
32306          */
32307         "keydown" : true,
32308
32309         // custom events
32310
32311         /**
32312          * @event cellclick
32313          * Fires when a cell is clicked
32314          * @param {Grid} this
32315          * @param {Number} rowIndex
32316          * @param {Number} columnIndex
32317          * @param {Roo.EventObject} e
32318          */
32319         "cellclick" : true,
32320         /**
32321          * @event celldblclick
32322          * Fires when a cell is double clicked
32323          * @param {Grid} this
32324          * @param {Number} rowIndex
32325          * @param {Number} columnIndex
32326          * @param {Roo.EventObject} e
32327          */
32328         "celldblclick" : true,
32329         /**
32330          * @event rowclick
32331          * Fires when a row is clicked
32332          * @param {Grid} this
32333          * @param {Number} rowIndex
32334          * @param {Roo.EventObject} e
32335          */
32336         "rowclick" : true,
32337         /**
32338          * @event rowdblclick
32339          * Fires when a row is double clicked
32340          * @param {Grid} this
32341          * @param {Number} rowIndex
32342          * @param {Roo.EventObject} e
32343          */
32344         "rowdblclick" : true,
32345         /**
32346          * @event headerclick
32347          * Fires when a header is clicked
32348          * @param {Grid} this
32349          * @param {Number} columnIndex
32350          * @param {Roo.EventObject} e
32351          */
32352         "headerclick" : true,
32353         /**
32354          * @event headerdblclick
32355          * Fires when a header cell is double clicked
32356          * @param {Grid} this
32357          * @param {Number} columnIndex
32358          * @param {Roo.EventObject} e
32359          */
32360         "headerdblclick" : true,
32361         /**
32362          * @event rowcontextmenu
32363          * Fires when a row is right clicked
32364          * @param {Grid} this
32365          * @param {Number} rowIndex
32366          * @param {Roo.EventObject} e
32367          */
32368         "rowcontextmenu" : true,
32369         /**
32370          * @event cellcontextmenu
32371          * Fires when a cell is right clicked
32372          * @param {Grid} this
32373          * @param {Number} rowIndex
32374          * @param {Number} cellIndex
32375          * @param {Roo.EventObject} e
32376          */
32377          "cellcontextmenu" : true,
32378         /**
32379          * @event headercontextmenu
32380          * Fires when a header is right clicked
32381          * @param {Grid} this
32382          * @param {Number} columnIndex
32383          * @param {Roo.EventObject} e
32384          */
32385         "headercontextmenu" : true,
32386         /**
32387          * @event bodyscroll
32388          * Fires when the body element is scrolled
32389          * @param {Number} scrollLeft
32390          * @param {Number} scrollTop
32391          */
32392         "bodyscroll" : true,
32393         /**
32394          * @event columnresize
32395          * Fires when the user resizes a column
32396          * @param {Number} columnIndex
32397          * @param {Number} newSize
32398          */
32399         "columnresize" : true,
32400         /**
32401          * @event columnmove
32402          * Fires when the user moves a column
32403          * @param {Number} oldIndex
32404          * @param {Number} newIndex
32405          */
32406         "columnmove" : true,
32407         /**
32408          * @event startdrag
32409          * Fires when row(s) start being dragged
32410          * @param {Grid} this
32411          * @param {Roo.GridDD} dd The drag drop object
32412          * @param {event} e The raw browser event
32413          */
32414         "startdrag" : true,
32415         /**
32416          * @event enddrag
32417          * Fires when a drag operation is complete
32418          * @param {Grid} this
32419          * @param {Roo.GridDD} dd The drag drop object
32420          * @param {event} e The raw browser event
32421          */
32422         "enddrag" : true,
32423         /**
32424          * @event dragdrop
32425          * Fires when dragged row(s) are dropped on a valid DD target
32426          * @param {Grid} this
32427          * @param {Roo.GridDD} dd The drag drop object
32428          * @param {String} targetId The target drag drop object
32429          * @param {event} e The raw browser event
32430          */
32431         "dragdrop" : true,
32432         /**
32433          * @event dragover
32434          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32435          * @param {Grid} this
32436          * @param {Roo.GridDD} dd The drag drop object
32437          * @param {String} targetId The target drag drop object
32438          * @param {event} e The raw browser event
32439          */
32440         "dragover" : true,
32441         /**
32442          * @event dragenter
32443          *  Fires when the dragged row(s) first cross another DD target while being dragged
32444          * @param {Grid} this
32445          * @param {Roo.GridDD} dd The drag drop object
32446          * @param {String} targetId The target drag drop object
32447          * @param {event} e The raw browser event
32448          */
32449         "dragenter" : true,
32450         /**
32451          * @event dragout
32452          * Fires when the dragged row(s) leave another DD target while being dragged
32453          * @param {Grid} this
32454          * @param {Roo.GridDD} dd The drag drop object
32455          * @param {String} targetId The target drag drop object
32456          * @param {event} e The raw browser event
32457          */
32458         "dragout" : true,
32459         /**
32460          * @event rowclass
32461          * Fires when a row is rendered, so you can change add a style to it.
32462          * @param {GridView} gridview   The grid view
32463          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32464          */
32465         'rowclass' : true,
32466
32467         /**
32468          * @event render
32469          * Fires when the grid is rendered
32470          * @param {Grid} grid
32471          */
32472         'render' : true
32473     });
32474
32475     Roo.grid.Grid.superclass.constructor.call(this);
32476 };
32477 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32478     
32479     /**
32480          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
32481          */
32482         /**
32483          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
32484          */
32485         /**
32486          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
32487          */
32488         /**
32489          * @cfg {Roo.grid.Store} ds The data store for the grid
32490          */
32491         /**
32492          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
32493          */
32494         /**
32495      * @cfg {String} ddGroup - drag drop group.
32496      */
32497       /**
32498      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32499      */
32500
32501     /**
32502      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32503      */
32504     minColumnWidth : 25,
32505
32506     /**
32507      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32508      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32509      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32510      */
32511     autoSizeColumns : false,
32512
32513     /**
32514      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32515      */
32516     autoSizeHeaders : true,
32517
32518     /**
32519      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32520      */
32521     monitorWindowResize : true,
32522
32523     /**
32524      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32525      * rows measured to get a columns size. Default is 0 (all rows).
32526      */
32527     maxRowsToMeasure : 0,
32528
32529     /**
32530      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32531      */
32532     trackMouseOver : true,
32533
32534     /**
32535     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32536     */
32537       /**
32538     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32539     */
32540     
32541     /**
32542     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32543     */
32544     enableDragDrop : false,
32545     
32546     /**
32547     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32548     */
32549     enableColumnMove : true,
32550     
32551     /**
32552     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32553     */
32554     enableColumnHide : true,
32555     
32556     /**
32557     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32558     */
32559     enableRowHeightSync : false,
32560     
32561     /**
32562     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32563     */
32564     stripeRows : true,
32565     
32566     /**
32567     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32568     */
32569     autoHeight : false,
32570
32571     /**
32572      * @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.
32573      */
32574     autoExpandColumn : false,
32575
32576     /**
32577     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32578     * Default is 50.
32579     */
32580     autoExpandMin : 50,
32581
32582     /**
32583     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32584     */
32585     autoExpandMax : 1000,
32586
32587     /**
32588     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32589     */
32590     view : null,
32591
32592     /**
32593     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32594     */
32595     loadMask : false,
32596     /**
32597     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32598     */
32599     dropTarget: false,
32600     
32601    
32602     
32603     // private
32604     rendered : false,
32605
32606     /**
32607     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32608     * of a fixed width. Default is false.
32609     */
32610     /**
32611     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32612     */
32613     
32614     
32615     /**
32616     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32617     * %0 is replaced with the number of selected rows.
32618     */
32619     ddText : "{0} selected row{1}",
32620     
32621     
32622     /**
32623      * Called once after all setup has been completed and the grid is ready to be rendered.
32624      * @return {Roo.grid.Grid} this
32625      */
32626     render : function()
32627     {
32628         var c = this.container;
32629         // try to detect autoHeight/width mode
32630         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32631             this.autoHeight = true;
32632         }
32633         var view = this.getView();
32634         view.init(this);
32635
32636         c.on("click", this.onClick, this);
32637         c.on("dblclick", this.onDblClick, this);
32638         c.on("contextmenu", this.onContextMenu, this);
32639         c.on("keydown", this.onKeyDown, this);
32640         if (Roo.isTouch) {
32641             c.on("touchstart", this.onTouchStart, this);
32642         }
32643
32644         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32645
32646         this.getSelectionModel().init(this);
32647
32648         view.render();
32649
32650         if(this.loadMask){
32651             this.loadMask = new Roo.LoadMask(this.container,
32652                     Roo.apply({store:this.dataSource}, this.loadMask));
32653         }
32654         
32655         
32656         if (this.toolbar && this.toolbar.xtype) {
32657             this.toolbar.container = this.getView().getHeaderPanel(true);
32658             this.toolbar = new Roo.Toolbar(this.toolbar);
32659         }
32660         if (this.footer && this.footer.xtype) {
32661             this.footer.dataSource = this.getDataSource();
32662             this.footer.container = this.getView().getFooterPanel(true);
32663             this.footer = Roo.factory(this.footer, Roo);
32664         }
32665         if (this.dropTarget && this.dropTarget.xtype) {
32666             delete this.dropTarget.xtype;
32667             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32668         }
32669         
32670         
32671         this.rendered = true;
32672         this.fireEvent('render', this);
32673         return this;
32674     },
32675
32676     /**
32677      * Reconfigures the grid to use a different Store and Column Model.
32678      * The View will be bound to the new objects and refreshed.
32679      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32680      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32681      */
32682     reconfigure : function(dataSource, colModel){
32683         if(this.loadMask){
32684             this.loadMask.destroy();
32685             this.loadMask = new Roo.LoadMask(this.container,
32686                     Roo.apply({store:dataSource}, this.loadMask));
32687         }
32688         this.view.bind(dataSource, colModel);
32689         this.dataSource = dataSource;
32690         this.colModel = colModel;
32691         this.view.refresh(true);
32692     },
32693     /**
32694      * addColumns
32695      * Add's a column, default at the end..
32696      
32697      * @param {int} position to add (default end)
32698      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32699      */
32700     addColumns : function(pos, ar)
32701     {
32702         
32703         for (var i =0;i< ar.length;i++) {
32704             var cfg = ar[i];
32705             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32706             this.cm.lookup[cfg.id] = cfg;
32707         }
32708         
32709         
32710         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32711             pos = this.cm.config.length; //this.cm.config.push(cfg);
32712         } 
32713         pos = Math.max(0,pos);
32714         ar.unshift(0);
32715         ar.unshift(pos);
32716         this.cm.config.splice.apply(this.cm.config, ar);
32717         
32718         
32719         
32720         this.view.generateRules(this.cm);
32721         this.view.refresh(true);
32722         
32723     },
32724     
32725     
32726     
32727     
32728     // private
32729     onKeyDown : function(e){
32730         this.fireEvent("keydown", e);
32731     },
32732
32733     /**
32734      * Destroy this grid.
32735      * @param {Boolean} removeEl True to remove the element
32736      */
32737     destroy : function(removeEl, keepListeners){
32738         if(this.loadMask){
32739             this.loadMask.destroy();
32740         }
32741         var c = this.container;
32742         c.removeAllListeners();
32743         this.view.destroy();
32744         this.colModel.purgeListeners();
32745         if(!keepListeners){
32746             this.purgeListeners();
32747         }
32748         c.update("");
32749         if(removeEl === true){
32750             c.remove();
32751         }
32752     },
32753
32754     // private
32755     processEvent : function(name, e){
32756         // does this fire select???
32757         //Roo.log('grid:processEvent '  + name);
32758         
32759         if (name != 'touchstart' ) {
32760             this.fireEvent(name, e);    
32761         }
32762         
32763         var t = e.getTarget();
32764         var v = this.view;
32765         var header = v.findHeaderIndex(t);
32766         if(header !== false){
32767             var ename = name == 'touchstart' ? 'click' : name;
32768              
32769             this.fireEvent("header" + ename, this, header, e);
32770         }else{
32771             var row = v.findRowIndex(t);
32772             var cell = v.findCellIndex(t);
32773             if (name == 'touchstart') {
32774                 // first touch is always a click.
32775                 // hopefull this happens after selection is updated.?
32776                 name = false;
32777                 
32778                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32779                     var cs = this.selModel.getSelectedCell();
32780                     if (row == cs[0] && cell == cs[1]){
32781                         name = 'dblclick';
32782                     }
32783                 }
32784                 if (typeof(this.selModel.getSelections) != 'undefined') {
32785                     var cs = this.selModel.getSelections();
32786                     var ds = this.dataSource;
32787                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32788                         name = 'dblclick';
32789                     }
32790                 }
32791                 if (!name) {
32792                     return;
32793                 }
32794             }
32795             
32796             
32797             if(row !== false){
32798                 this.fireEvent("row" + name, this, row, e);
32799                 if(cell !== false){
32800                     this.fireEvent("cell" + name, this, row, cell, e);
32801                 }
32802             }
32803         }
32804     },
32805
32806     // private
32807     onClick : function(e){
32808         this.processEvent("click", e);
32809     },
32810    // private
32811     onTouchStart : function(e){
32812         this.processEvent("touchstart", e);
32813     },
32814
32815     // private
32816     onContextMenu : function(e, t){
32817         this.processEvent("contextmenu", e);
32818     },
32819
32820     // private
32821     onDblClick : function(e){
32822         this.processEvent("dblclick", e);
32823     },
32824
32825     // private
32826     walkCells : function(row, col, step, fn, scope){
32827         var cm = this.colModel, clen = cm.getColumnCount();
32828         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32829         if(step < 0){
32830             if(col < 0){
32831                 row--;
32832                 first = false;
32833             }
32834             while(row >= 0){
32835                 if(!first){
32836                     col = clen-1;
32837                 }
32838                 first = false;
32839                 while(col >= 0){
32840                     if(fn.call(scope || this, row, col, cm) === true){
32841                         return [row, col];
32842                     }
32843                     col--;
32844                 }
32845                 row--;
32846             }
32847         } else {
32848             if(col >= clen){
32849                 row++;
32850                 first = false;
32851             }
32852             while(row < rlen){
32853                 if(!first){
32854                     col = 0;
32855                 }
32856                 first = false;
32857                 while(col < clen){
32858                     if(fn.call(scope || this, row, col, cm) === true){
32859                         return [row, col];
32860                     }
32861                     col++;
32862                 }
32863                 row++;
32864             }
32865         }
32866         return null;
32867     },
32868
32869     // private
32870     getSelections : function(){
32871         return this.selModel.getSelections();
32872     },
32873
32874     /**
32875      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32876      * but if manual update is required this method will initiate it.
32877      */
32878     autoSize : function(){
32879         if(this.rendered){
32880             this.view.layout();
32881             if(this.view.adjustForScroll){
32882                 this.view.adjustForScroll();
32883             }
32884         }
32885     },
32886
32887     /**
32888      * Returns the grid's underlying element.
32889      * @return {Element} The element
32890      */
32891     getGridEl : function(){
32892         return this.container;
32893     },
32894
32895     // private for compatibility, overridden by editor grid
32896     stopEditing : function(){},
32897
32898     /**
32899      * Returns the grid's SelectionModel.
32900      * @return {SelectionModel}
32901      */
32902     getSelectionModel : function(){
32903         if(!this.selModel){
32904             this.selModel = new Roo.grid.RowSelectionModel();
32905         }
32906         return this.selModel;
32907     },
32908
32909     /**
32910      * Returns the grid's DataSource.
32911      * @return {DataSource}
32912      */
32913     getDataSource : function(){
32914         return this.dataSource;
32915     },
32916
32917     /**
32918      * Returns the grid's ColumnModel.
32919      * @return {ColumnModel}
32920      */
32921     getColumnModel : function(){
32922         return this.colModel;
32923     },
32924
32925     /**
32926      * Returns the grid's GridView object.
32927      * @return {GridView}
32928      */
32929     getView : function(){
32930         if(!this.view){
32931             this.view = new Roo.grid.GridView(this.viewConfig);
32932             this.relayEvents(this.view, [
32933                 "beforerowremoved", "beforerowsinserted",
32934                 "beforerefresh", "rowremoved",
32935                 "rowsinserted", "rowupdated" ,"refresh"
32936             ]);
32937         }
32938         return this.view;
32939     },
32940     /**
32941      * Called to get grid's drag proxy text, by default returns this.ddText.
32942      * Override this to put something different in the dragged text.
32943      * @return {String}
32944      */
32945     getDragDropText : function(){
32946         var count = this.selModel.getCount();
32947         return String.format(this.ddText, count, count == 1 ? '' : 's');
32948     }
32949 });
32950 /*
32951  * Based on:
32952  * Ext JS Library 1.1.1
32953  * Copyright(c) 2006-2007, Ext JS, LLC.
32954  *
32955  * Originally Released Under LGPL - original licence link has changed is not relivant.
32956  *
32957  * Fork - LGPL
32958  * <script type="text/javascript">
32959  */
32960  /**
32961  * @class Roo.grid.AbstractGridView
32962  * @extends Roo.util.Observable
32963  * @abstract
32964  * Abstract base class for grid Views
32965  * @constructor
32966  */
32967 Roo.grid.AbstractGridView = function(){
32968         this.grid = null;
32969         
32970         this.events = {
32971             "beforerowremoved" : true,
32972             "beforerowsinserted" : true,
32973             "beforerefresh" : true,
32974             "rowremoved" : true,
32975             "rowsinserted" : true,
32976             "rowupdated" : true,
32977             "refresh" : true
32978         };
32979     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32980 };
32981
32982 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32983     rowClass : "x-grid-row",
32984     cellClass : "x-grid-cell",
32985     tdClass : "x-grid-td",
32986     hdClass : "x-grid-hd",
32987     splitClass : "x-grid-hd-split",
32988     
32989     init: function(grid){
32990         this.grid = grid;
32991                 var cid = this.grid.getGridEl().id;
32992         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32993         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32994         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32995         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32996         },
32997         
32998     getColumnRenderers : function(){
32999         var renderers = [];
33000         var cm = this.grid.colModel;
33001         var colCount = cm.getColumnCount();
33002         for(var i = 0; i < colCount; i++){
33003             renderers[i] = cm.getRenderer(i);
33004         }
33005         return renderers;
33006     },
33007     
33008     getColumnIds : function(){
33009         var ids = [];
33010         var cm = this.grid.colModel;
33011         var colCount = cm.getColumnCount();
33012         for(var i = 0; i < colCount; i++){
33013             ids[i] = cm.getColumnId(i);
33014         }
33015         return ids;
33016     },
33017     
33018     getDataIndexes : function(){
33019         if(!this.indexMap){
33020             this.indexMap = this.buildIndexMap();
33021         }
33022         return this.indexMap.colToData;
33023     },
33024     
33025     getColumnIndexByDataIndex : function(dataIndex){
33026         if(!this.indexMap){
33027             this.indexMap = this.buildIndexMap();
33028         }
33029         return this.indexMap.dataToCol[dataIndex];
33030     },
33031     
33032     /**
33033      * Set a css style for a column dynamically. 
33034      * @param {Number} colIndex The index of the column
33035      * @param {String} name The css property name
33036      * @param {String} value The css value
33037      */
33038     setCSSStyle : function(colIndex, name, value){
33039         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33040         Roo.util.CSS.updateRule(selector, name, value);
33041     },
33042     
33043     generateRules : function(cm){
33044         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33045         Roo.util.CSS.removeStyleSheet(rulesId);
33046         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33047             var cid = cm.getColumnId(i);
33048             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33049                          this.tdSelector, cid, " {\n}\n",
33050                          this.hdSelector, cid, " {\n}\n",
33051                          this.splitSelector, cid, " {\n}\n");
33052         }
33053         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33054     }
33055 });/*
33056  * Based on:
33057  * Ext JS Library 1.1.1
33058  * Copyright(c) 2006-2007, Ext JS, LLC.
33059  *
33060  * Originally Released Under LGPL - original licence link has changed is not relivant.
33061  *
33062  * Fork - LGPL
33063  * <script type="text/javascript">
33064  */
33065
33066 // private
33067 // This is a support class used internally by the Grid components
33068 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33069     this.grid = grid;
33070     this.view = grid.getView();
33071     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33072     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33073     if(hd2){
33074         this.setHandleElId(Roo.id(hd));
33075         this.setOuterHandleElId(Roo.id(hd2));
33076     }
33077     this.scroll = false;
33078 };
33079 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33080     maxDragWidth: 120,
33081     getDragData : function(e){
33082         var t = Roo.lib.Event.getTarget(e);
33083         var h = this.view.findHeaderCell(t);
33084         if(h){
33085             return {ddel: h.firstChild, header:h};
33086         }
33087         return false;
33088     },
33089
33090     onInitDrag : function(e){
33091         this.view.headersDisabled = true;
33092         var clone = this.dragData.ddel.cloneNode(true);
33093         clone.id = Roo.id();
33094         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33095         this.proxy.update(clone);
33096         return true;
33097     },
33098
33099     afterValidDrop : function(){
33100         var v = this.view;
33101         setTimeout(function(){
33102             v.headersDisabled = false;
33103         }, 50);
33104     },
33105
33106     afterInvalidDrop : function(){
33107         var v = this.view;
33108         setTimeout(function(){
33109             v.headersDisabled = false;
33110         }, 50);
33111     }
33112 });
33113 /*
33114  * Based on:
33115  * Ext JS Library 1.1.1
33116  * Copyright(c) 2006-2007, Ext JS, LLC.
33117  *
33118  * Originally Released Under LGPL - original licence link has changed is not relivant.
33119  *
33120  * Fork - LGPL
33121  * <script type="text/javascript">
33122  */
33123 // private
33124 // This is a support class used internally by the Grid components
33125 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33126     this.grid = grid;
33127     this.view = grid.getView();
33128     // split the proxies so they don't interfere with mouse events
33129     this.proxyTop = Roo.DomHelper.append(document.body, {
33130         cls:"col-move-top", html:"&#160;"
33131     }, true);
33132     this.proxyBottom = Roo.DomHelper.append(document.body, {
33133         cls:"col-move-bottom", html:"&#160;"
33134     }, true);
33135     this.proxyTop.hide = this.proxyBottom.hide = function(){
33136         this.setLeftTop(-100,-100);
33137         this.setStyle("visibility", "hidden");
33138     };
33139     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33140     // temporarily disabled
33141     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33142     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33143 };
33144 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33145     proxyOffsets : [-4, -9],
33146     fly: Roo.Element.fly,
33147
33148     getTargetFromEvent : function(e){
33149         var t = Roo.lib.Event.getTarget(e);
33150         var cindex = this.view.findCellIndex(t);
33151         if(cindex !== false){
33152             return this.view.getHeaderCell(cindex);
33153         }
33154         return null;
33155     },
33156
33157     nextVisible : function(h){
33158         var v = this.view, cm = this.grid.colModel;
33159         h = h.nextSibling;
33160         while(h){
33161             if(!cm.isHidden(v.getCellIndex(h))){
33162                 return h;
33163             }
33164             h = h.nextSibling;
33165         }
33166         return null;
33167     },
33168
33169     prevVisible : function(h){
33170         var v = this.view, cm = this.grid.colModel;
33171         h = h.prevSibling;
33172         while(h){
33173             if(!cm.isHidden(v.getCellIndex(h))){
33174                 return h;
33175             }
33176             h = h.prevSibling;
33177         }
33178         return null;
33179     },
33180
33181     positionIndicator : function(h, n, e){
33182         var x = Roo.lib.Event.getPageX(e);
33183         var r = Roo.lib.Dom.getRegion(n.firstChild);
33184         var px, pt, py = r.top + this.proxyOffsets[1];
33185         if((r.right - x) <= (r.right-r.left)/2){
33186             px = r.right+this.view.borderWidth;
33187             pt = "after";
33188         }else{
33189             px = r.left;
33190             pt = "before";
33191         }
33192         var oldIndex = this.view.getCellIndex(h);
33193         var newIndex = this.view.getCellIndex(n);
33194
33195         if(this.grid.colModel.isFixed(newIndex)){
33196             return false;
33197         }
33198
33199         var locked = this.grid.colModel.isLocked(newIndex);
33200
33201         if(pt == "after"){
33202             newIndex++;
33203         }
33204         if(oldIndex < newIndex){
33205             newIndex--;
33206         }
33207         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33208             return false;
33209         }
33210         px +=  this.proxyOffsets[0];
33211         this.proxyTop.setLeftTop(px, py);
33212         this.proxyTop.show();
33213         if(!this.bottomOffset){
33214             this.bottomOffset = this.view.mainHd.getHeight();
33215         }
33216         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33217         this.proxyBottom.show();
33218         return pt;
33219     },
33220
33221     onNodeEnter : function(n, dd, e, data){
33222         if(data.header != n){
33223             this.positionIndicator(data.header, n, e);
33224         }
33225     },
33226
33227     onNodeOver : function(n, dd, e, data){
33228         var result = false;
33229         if(data.header != n){
33230             result = this.positionIndicator(data.header, n, e);
33231         }
33232         if(!result){
33233             this.proxyTop.hide();
33234             this.proxyBottom.hide();
33235         }
33236         return result ? this.dropAllowed : this.dropNotAllowed;
33237     },
33238
33239     onNodeOut : function(n, dd, e, data){
33240         this.proxyTop.hide();
33241         this.proxyBottom.hide();
33242     },
33243
33244     onNodeDrop : function(n, dd, e, data){
33245         var h = data.header;
33246         if(h != n){
33247             var cm = this.grid.colModel;
33248             var x = Roo.lib.Event.getPageX(e);
33249             var r = Roo.lib.Dom.getRegion(n.firstChild);
33250             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33251             var oldIndex = this.view.getCellIndex(h);
33252             var newIndex = this.view.getCellIndex(n);
33253             var locked = cm.isLocked(newIndex);
33254             if(pt == "after"){
33255                 newIndex++;
33256             }
33257             if(oldIndex < newIndex){
33258                 newIndex--;
33259             }
33260             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33261                 return false;
33262             }
33263             cm.setLocked(oldIndex, locked, true);
33264             cm.moveColumn(oldIndex, newIndex);
33265             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33266             return true;
33267         }
33268         return false;
33269     }
33270 });
33271 /*
33272  * Based on:
33273  * Ext JS Library 1.1.1
33274  * Copyright(c) 2006-2007, Ext JS, LLC.
33275  *
33276  * Originally Released Under LGPL - original licence link has changed is not relivant.
33277  *
33278  * Fork - LGPL
33279  * <script type="text/javascript">
33280  */
33281   
33282 /**
33283  * @class Roo.grid.GridView
33284  * @extends Roo.util.Observable
33285  *
33286  * @constructor
33287  * @param {Object} config
33288  */
33289 Roo.grid.GridView = function(config){
33290     Roo.grid.GridView.superclass.constructor.call(this);
33291     this.el = null;
33292
33293     Roo.apply(this, config);
33294 };
33295
33296 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33297
33298     unselectable :  'unselectable="on"',
33299     unselectableCls :  'x-unselectable',
33300     
33301     
33302     rowClass : "x-grid-row",
33303
33304     cellClass : "x-grid-col",
33305
33306     tdClass : "x-grid-td",
33307
33308     hdClass : "x-grid-hd",
33309
33310     splitClass : "x-grid-split",
33311
33312     sortClasses : ["sort-asc", "sort-desc"],
33313
33314     enableMoveAnim : false,
33315
33316     hlColor: "C3DAF9",
33317
33318     dh : Roo.DomHelper,
33319
33320     fly : Roo.Element.fly,
33321
33322     css : Roo.util.CSS,
33323
33324     borderWidth: 1,
33325
33326     splitOffset: 3,
33327
33328     scrollIncrement : 22,
33329
33330     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33331
33332     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33333
33334     bind : function(ds, cm){
33335         if(this.ds){
33336             this.ds.un("load", this.onLoad, this);
33337             this.ds.un("datachanged", this.onDataChange, this);
33338             this.ds.un("add", this.onAdd, this);
33339             this.ds.un("remove", this.onRemove, this);
33340             this.ds.un("update", this.onUpdate, this);
33341             this.ds.un("clear", this.onClear, this);
33342         }
33343         if(ds){
33344             ds.on("load", this.onLoad, this);
33345             ds.on("datachanged", this.onDataChange, this);
33346             ds.on("add", this.onAdd, this);
33347             ds.on("remove", this.onRemove, this);
33348             ds.on("update", this.onUpdate, this);
33349             ds.on("clear", this.onClear, this);
33350         }
33351         this.ds = ds;
33352
33353         if(this.cm){
33354             this.cm.un("widthchange", this.onColWidthChange, this);
33355             this.cm.un("headerchange", this.onHeaderChange, this);
33356             this.cm.un("hiddenchange", this.onHiddenChange, this);
33357             this.cm.un("columnmoved", this.onColumnMove, this);
33358             this.cm.un("columnlockchange", this.onColumnLock, this);
33359         }
33360         if(cm){
33361             this.generateRules(cm);
33362             cm.on("widthchange", this.onColWidthChange, this);
33363             cm.on("headerchange", this.onHeaderChange, this);
33364             cm.on("hiddenchange", this.onHiddenChange, this);
33365             cm.on("columnmoved", this.onColumnMove, this);
33366             cm.on("columnlockchange", this.onColumnLock, this);
33367         }
33368         this.cm = cm;
33369     },
33370
33371     init: function(grid){
33372         Roo.grid.GridView.superclass.init.call(this, grid);
33373
33374         this.bind(grid.dataSource, grid.colModel);
33375
33376         grid.on("headerclick", this.handleHeaderClick, this);
33377
33378         if(grid.trackMouseOver){
33379             grid.on("mouseover", this.onRowOver, this);
33380             grid.on("mouseout", this.onRowOut, this);
33381         }
33382         grid.cancelTextSelection = function(){};
33383         this.gridId = grid.id;
33384
33385         var tpls = this.templates || {};
33386
33387         if(!tpls.master){
33388             tpls.master = new Roo.Template(
33389                '<div class="x-grid" hidefocus="true">',
33390                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33391                   '<div class="x-grid-topbar"></div>',
33392                   '<div class="x-grid-scroller"><div></div></div>',
33393                   '<div class="x-grid-locked">',
33394                       '<div class="x-grid-header">{lockedHeader}</div>',
33395                       '<div class="x-grid-body">{lockedBody}</div>',
33396                   "</div>",
33397                   '<div class="x-grid-viewport">',
33398                       '<div class="x-grid-header">{header}</div>',
33399                       '<div class="x-grid-body">{body}</div>',
33400                   "</div>",
33401                   '<div class="x-grid-bottombar"></div>',
33402                  
33403                   '<div class="x-grid-resize-proxy">&#160;</div>',
33404                "</div>"
33405             );
33406             tpls.master.disableformats = true;
33407         }
33408
33409         if(!tpls.header){
33410             tpls.header = new Roo.Template(
33411                '<table border="0" cellspacing="0" cellpadding="0">',
33412                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33413                "</table>{splits}"
33414             );
33415             tpls.header.disableformats = true;
33416         }
33417         tpls.header.compile();
33418
33419         if(!tpls.hcell){
33420             tpls.hcell = new Roo.Template(
33421                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33422                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33423                 "</div></td>"
33424              );
33425              tpls.hcell.disableFormats = true;
33426         }
33427         tpls.hcell.compile();
33428
33429         if(!tpls.hsplit){
33430             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33431                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33432             tpls.hsplit.disableFormats = true;
33433         }
33434         tpls.hsplit.compile();
33435
33436         if(!tpls.body){
33437             tpls.body = new Roo.Template(
33438                '<table border="0" cellspacing="0" cellpadding="0">',
33439                "<tbody>{rows}</tbody>",
33440                "</table>"
33441             );
33442             tpls.body.disableFormats = true;
33443         }
33444         tpls.body.compile();
33445
33446         if(!tpls.row){
33447             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33448             tpls.row.disableFormats = true;
33449         }
33450         tpls.row.compile();
33451
33452         if(!tpls.cell){
33453             tpls.cell = new Roo.Template(
33454                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33455                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33456                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33457                 "</td>"
33458             );
33459             tpls.cell.disableFormats = true;
33460         }
33461         tpls.cell.compile();
33462
33463         this.templates = tpls;
33464     },
33465
33466     // remap these for backwards compat
33467     onColWidthChange : function(){
33468         this.updateColumns.apply(this, arguments);
33469     },
33470     onHeaderChange : function(){
33471         this.updateHeaders.apply(this, arguments);
33472     }, 
33473     onHiddenChange : function(){
33474         this.handleHiddenChange.apply(this, arguments);
33475     },
33476     onColumnMove : function(){
33477         this.handleColumnMove.apply(this, arguments);
33478     },
33479     onColumnLock : function(){
33480         this.handleLockChange.apply(this, arguments);
33481     },
33482
33483     onDataChange : function(){
33484         this.refresh();
33485         this.updateHeaderSortState();
33486     },
33487
33488     onClear : function(){
33489         this.refresh();
33490     },
33491
33492     onUpdate : function(ds, record){
33493         this.refreshRow(record);
33494     },
33495
33496     refreshRow : function(record){
33497         var ds = this.ds, index;
33498         if(typeof record == 'number'){
33499             index = record;
33500             record = ds.getAt(index);
33501         }else{
33502             index = ds.indexOf(record);
33503         }
33504         this.insertRows(ds, index, index, true);
33505         this.onRemove(ds, record, index+1, true);
33506         this.syncRowHeights(index, index);
33507         this.layout();
33508         this.fireEvent("rowupdated", this, index, record);
33509     },
33510
33511     onAdd : function(ds, records, index){
33512         this.insertRows(ds, index, index + (records.length-1));
33513     },
33514
33515     onRemove : function(ds, record, index, isUpdate){
33516         if(isUpdate !== true){
33517             this.fireEvent("beforerowremoved", this, index, record);
33518         }
33519         var bt = this.getBodyTable(), lt = this.getLockedTable();
33520         if(bt.rows[index]){
33521             bt.firstChild.removeChild(bt.rows[index]);
33522         }
33523         if(lt.rows[index]){
33524             lt.firstChild.removeChild(lt.rows[index]);
33525         }
33526         if(isUpdate !== true){
33527             this.stripeRows(index);
33528             this.syncRowHeights(index, index);
33529             this.layout();
33530             this.fireEvent("rowremoved", this, index, record);
33531         }
33532     },
33533
33534     onLoad : function(){
33535         this.scrollToTop();
33536     },
33537
33538     /**
33539      * Scrolls the grid to the top
33540      */
33541     scrollToTop : function(){
33542         if(this.scroller){
33543             this.scroller.dom.scrollTop = 0;
33544             this.syncScroll();
33545         }
33546     },
33547
33548     /**
33549      * Gets a panel in the header of the grid that can be used for toolbars etc.
33550      * After modifying the contents of this panel a call to grid.autoSize() may be
33551      * required to register any changes in size.
33552      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33553      * @return Roo.Element
33554      */
33555     getHeaderPanel : function(doShow){
33556         if(doShow){
33557             this.headerPanel.show();
33558         }
33559         return this.headerPanel;
33560     },
33561
33562     /**
33563      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33564      * After modifying the contents of this panel a call to grid.autoSize() may be
33565      * required to register any changes in size.
33566      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33567      * @return Roo.Element
33568      */
33569     getFooterPanel : function(doShow){
33570         if(doShow){
33571             this.footerPanel.show();
33572         }
33573         return this.footerPanel;
33574     },
33575
33576     initElements : function(){
33577         var E = Roo.Element;
33578         var el = this.grid.getGridEl().dom.firstChild;
33579         var cs = el.childNodes;
33580
33581         this.el = new E(el);
33582         
33583          this.focusEl = new E(el.firstChild);
33584         this.focusEl.swallowEvent("click", true);
33585         
33586         this.headerPanel = new E(cs[1]);
33587         this.headerPanel.enableDisplayMode("block");
33588
33589         this.scroller = new E(cs[2]);
33590         this.scrollSizer = new E(this.scroller.dom.firstChild);
33591
33592         this.lockedWrap = new E(cs[3]);
33593         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33594         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33595
33596         this.mainWrap = new E(cs[4]);
33597         this.mainHd = new E(this.mainWrap.dom.firstChild);
33598         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33599
33600         this.footerPanel = new E(cs[5]);
33601         this.footerPanel.enableDisplayMode("block");
33602
33603         this.resizeProxy = new E(cs[6]);
33604
33605         this.headerSelector = String.format(
33606            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33607            this.lockedHd.id, this.mainHd.id
33608         );
33609
33610         this.splitterSelector = String.format(
33611            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33612            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33613         );
33614     },
33615     idToCssName : function(s)
33616     {
33617         return s.replace(/[^a-z0-9]+/ig, '-');
33618     },
33619
33620     getHeaderCell : function(index){
33621         return Roo.DomQuery.select(this.headerSelector)[index];
33622     },
33623
33624     getHeaderCellMeasure : function(index){
33625         return this.getHeaderCell(index).firstChild;
33626     },
33627
33628     getHeaderCellText : function(index){
33629         return this.getHeaderCell(index).firstChild.firstChild;
33630     },
33631
33632     getLockedTable : function(){
33633         return this.lockedBody.dom.firstChild;
33634     },
33635
33636     getBodyTable : function(){
33637         return this.mainBody.dom.firstChild;
33638     },
33639
33640     getLockedRow : function(index){
33641         return this.getLockedTable().rows[index];
33642     },
33643
33644     getRow : function(index){
33645         return this.getBodyTable().rows[index];
33646     },
33647
33648     getRowComposite : function(index){
33649         if(!this.rowEl){
33650             this.rowEl = new Roo.CompositeElementLite();
33651         }
33652         var els = [], lrow, mrow;
33653         if(lrow = this.getLockedRow(index)){
33654             els.push(lrow);
33655         }
33656         if(mrow = this.getRow(index)){
33657             els.push(mrow);
33658         }
33659         this.rowEl.elements = els;
33660         return this.rowEl;
33661     },
33662     /**
33663      * Gets the 'td' of the cell
33664      * 
33665      * @param {Integer} rowIndex row to select
33666      * @param {Integer} colIndex column to select
33667      * 
33668      * @return {Object} 
33669      */
33670     getCell : function(rowIndex, colIndex){
33671         var locked = this.cm.getLockedCount();
33672         var source;
33673         if(colIndex < locked){
33674             source = this.lockedBody.dom.firstChild;
33675         }else{
33676             source = this.mainBody.dom.firstChild;
33677             colIndex -= locked;
33678         }
33679         return source.rows[rowIndex].childNodes[colIndex];
33680     },
33681
33682     getCellText : function(rowIndex, colIndex){
33683         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33684     },
33685
33686     getCellBox : function(cell){
33687         var b = this.fly(cell).getBox();
33688         if(Roo.isOpera){ // opera fails to report the Y
33689             b.y = cell.offsetTop + this.mainBody.getY();
33690         }
33691         return b;
33692     },
33693
33694     getCellIndex : function(cell){
33695         var id = String(cell.className).match(this.cellRE);
33696         if(id){
33697             return parseInt(id[1], 10);
33698         }
33699         return 0;
33700     },
33701
33702     findHeaderIndex : function(n){
33703         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33704         return r ? this.getCellIndex(r) : false;
33705     },
33706
33707     findHeaderCell : function(n){
33708         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33709         return r ? r : false;
33710     },
33711
33712     findRowIndex : function(n){
33713         if(!n){
33714             return false;
33715         }
33716         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33717         return r ? r.rowIndex : false;
33718     },
33719
33720     findCellIndex : function(node){
33721         var stop = this.el.dom;
33722         while(node && node != stop){
33723             if(this.findRE.test(node.className)){
33724                 return this.getCellIndex(node);
33725             }
33726             node = node.parentNode;
33727         }
33728         return false;
33729     },
33730
33731     getColumnId : function(index){
33732         return this.cm.getColumnId(index);
33733     },
33734
33735     getSplitters : function()
33736     {
33737         if(this.splitterSelector){
33738            return Roo.DomQuery.select(this.splitterSelector);
33739         }else{
33740             return null;
33741       }
33742     },
33743
33744     getSplitter : function(index){
33745         return this.getSplitters()[index];
33746     },
33747
33748     onRowOver : function(e, t){
33749         var row;
33750         if((row = this.findRowIndex(t)) !== false){
33751             this.getRowComposite(row).addClass("x-grid-row-over");
33752         }
33753     },
33754
33755     onRowOut : function(e, t){
33756         var row;
33757         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33758             this.getRowComposite(row).removeClass("x-grid-row-over");
33759         }
33760     },
33761
33762     renderHeaders : function(){
33763         var cm = this.cm;
33764         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33765         var cb = [], lb = [], sb = [], lsb = [], p = {};
33766         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33767             p.cellId = "x-grid-hd-0-" + i;
33768             p.splitId = "x-grid-csplit-0-" + i;
33769             p.id = cm.getColumnId(i);
33770             p.value = cm.getColumnHeader(i) || "";
33771             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33772             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33773             if(!cm.isLocked(i)){
33774                 cb[cb.length] = ct.apply(p);
33775                 sb[sb.length] = st.apply(p);
33776             }else{
33777                 lb[lb.length] = ct.apply(p);
33778                 lsb[lsb.length] = st.apply(p);
33779             }
33780         }
33781         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33782                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33783     },
33784
33785     updateHeaders : function(){
33786         var html = this.renderHeaders();
33787         this.lockedHd.update(html[0]);
33788         this.mainHd.update(html[1]);
33789     },
33790
33791     /**
33792      * Focuses the specified row.
33793      * @param {Number} row The row index
33794      */
33795     focusRow : function(row)
33796     {
33797         //Roo.log('GridView.focusRow');
33798         var x = this.scroller.dom.scrollLeft;
33799         this.focusCell(row, 0, false);
33800         this.scroller.dom.scrollLeft = x;
33801     },
33802
33803     /**
33804      * Focuses the specified cell.
33805      * @param {Number} row The row index
33806      * @param {Number} col The column index
33807      * @param {Boolean} hscroll false to disable horizontal scrolling
33808      */
33809     focusCell : function(row, col, hscroll)
33810     {
33811         //Roo.log('GridView.focusCell');
33812         var el = this.ensureVisible(row, col, hscroll);
33813         this.focusEl.alignTo(el, "tl-tl");
33814         if(Roo.isGecko){
33815             this.focusEl.focus();
33816         }else{
33817             this.focusEl.focus.defer(1, this.focusEl);
33818         }
33819     },
33820
33821     /**
33822      * Scrolls the specified cell into view
33823      * @param {Number} row The row index
33824      * @param {Number} col The column index
33825      * @param {Boolean} hscroll false to disable horizontal scrolling
33826      */
33827     ensureVisible : function(row, col, hscroll)
33828     {
33829         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33830         //return null; //disable for testing.
33831         if(typeof row != "number"){
33832             row = row.rowIndex;
33833         }
33834         if(row < 0 && row >= this.ds.getCount()){
33835             return  null;
33836         }
33837         col = (col !== undefined ? col : 0);
33838         var cm = this.grid.colModel;
33839         while(cm.isHidden(col)){
33840             col++;
33841         }
33842
33843         var el = this.getCell(row, col);
33844         if(!el){
33845             return null;
33846         }
33847         var c = this.scroller.dom;
33848
33849         var ctop = parseInt(el.offsetTop, 10);
33850         var cleft = parseInt(el.offsetLeft, 10);
33851         var cbot = ctop + el.offsetHeight;
33852         var cright = cleft + el.offsetWidth;
33853         
33854         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33855         var stop = parseInt(c.scrollTop, 10);
33856         var sleft = parseInt(c.scrollLeft, 10);
33857         var sbot = stop + ch;
33858         var sright = sleft + c.clientWidth;
33859         /*
33860         Roo.log('GridView.ensureVisible:' +
33861                 ' ctop:' + ctop +
33862                 ' c.clientHeight:' + c.clientHeight +
33863                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33864                 ' stop:' + stop +
33865                 ' cbot:' + cbot +
33866                 ' sbot:' + sbot +
33867                 ' ch:' + ch  
33868                 );
33869         */
33870         if(ctop < stop){
33871             c.scrollTop = ctop;
33872             //Roo.log("set scrolltop to ctop DISABLE?");
33873         }else if(cbot > sbot){
33874             //Roo.log("set scrolltop to cbot-ch");
33875             c.scrollTop = cbot-ch;
33876         }
33877         
33878         if(hscroll !== false){
33879             if(cleft < sleft){
33880                 c.scrollLeft = cleft;
33881             }else if(cright > sright){
33882                 c.scrollLeft = cright-c.clientWidth;
33883             }
33884         }
33885          
33886         return el;
33887     },
33888
33889     updateColumns : function(){
33890         this.grid.stopEditing();
33891         var cm = this.grid.colModel, colIds = this.getColumnIds();
33892         //var totalWidth = cm.getTotalWidth();
33893         var pos = 0;
33894         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33895             //if(cm.isHidden(i)) continue;
33896             var w = cm.getColumnWidth(i);
33897             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33898             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33899         }
33900         this.updateSplitters();
33901     },
33902
33903     generateRules : function(cm){
33904         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33905         Roo.util.CSS.removeStyleSheet(rulesId);
33906         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33907             var cid = cm.getColumnId(i);
33908             var align = '';
33909             if(cm.config[i].align){
33910                 align = 'text-align:'+cm.config[i].align+';';
33911             }
33912             var hidden = '';
33913             if(cm.isHidden(i)){
33914                 hidden = 'display:none;';
33915             }
33916             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33917             ruleBuf.push(
33918                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33919                     this.hdSelector, cid, " {\n", align, width, "}\n",
33920                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33921                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33922         }
33923         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33924     },
33925
33926     updateSplitters : function(){
33927         var cm = this.cm, s = this.getSplitters();
33928         if(s){ // splitters not created yet
33929             var pos = 0, locked = true;
33930             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33931                 if(cm.isHidden(i)) {
33932                     continue;
33933                 }
33934                 var w = cm.getColumnWidth(i); // make sure it's a number
33935                 if(!cm.isLocked(i) && locked){
33936                     pos = 0;
33937                     locked = false;
33938                 }
33939                 pos += w;
33940                 s[i].style.left = (pos-this.splitOffset) + "px";
33941             }
33942         }
33943     },
33944
33945     handleHiddenChange : function(colModel, colIndex, hidden){
33946         if(hidden){
33947             this.hideColumn(colIndex);
33948         }else{
33949             this.unhideColumn(colIndex);
33950         }
33951     },
33952
33953     hideColumn : function(colIndex){
33954         var cid = this.getColumnId(colIndex);
33955         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33956         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33957         if(Roo.isSafari){
33958             this.updateHeaders();
33959         }
33960         this.updateSplitters();
33961         this.layout();
33962     },
33963
33964     unhideColumn : function(colIndex){
33965         var cid = this.getColumnId(colIndex);
33966         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33967         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33968
33969         if(Roo.isSafari){
33970             this.updateHeaders();
33971         }
33972         this.updateSplitters();
33973         this.layout();
33974     },
33975
33976     insertRows : function(dm, firstRow, lastRow, isUpdate){
33977         if(firstRow == 0 && lastRow == dm.getCount()-1){
33978             this.refresh();
33979         }else{
33980             if(!isUpdate){
33981                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33982             }
33983             var s = this.getScrollState();
33984             var markup = this.renderRows(firstRow, lastRow);
33985             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33986             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33987             this.restoreScroll(s);
33988             if(!isUpdate){
33989                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33990                 this.syncRowHeights(firstRow, lastRow);
33991                 this.stripeRows(firstRow);
33992                 this.layout();
33993             }
33994         }
33995     },
33996
33997     bufferRows : function(markup, target, index){
33998         var before = null, trows = target.rows, tbody = target.tBodies[0];
33999         if(index < trows.length){
34000             before = trows[index];
34001         }
34002         var b = document.createElement("div");
34003         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34004         var rows = b.firstChild.rows;
34005         for(var i = 0, len = rows.length; i < len; i++){
34006             if(before){
34007                 tbody.insertBefore(rows[0], before);
34008             }else{
34009                 tbody.appendChild(rows[0]);
34010             }
34011         }
34012         b.innerHTML = "";
34013         b = null;
34014     },
34015
34016     deleteRows : function(dm, firstRow, lastRow){
34017         if(dm.getRowCount()<1){
34018             this.fireEvent("beforerefresh", this);
34019             this.mainBody.update("");
34020             this.lockedBody.update("");
34021             this.fireEvent("refresh", this);
34022         }else{
34023             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34024             var bt = this.getBodyTable();
34025             var tbody = bt.firstChild;
34026             var rows = bt.rows;
34027             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34028                 tbody.removeChild(rows[firstRow]);
34029             }
34030             this.stripeRows(firstRow);
34031             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34032         }
34033     },
34034
34035     updateRows : function(dataSource, firstRow, lastRow){
34036         var s = this.getScrollState();
34037         this.refresh();
34038         this.restoreScroll(s);
34039     },
34040
34041     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34042         if(!noRefresh){
34043            this.refresh();
34044         }
34045         this.updateHeaderSortState();
34046     },
34047
34048     getScrollState : function(){
34049         
34050         var sb = this.scroller.dom;
34051         return {left: sb.scrollLeft, top: sb.scrollTop};
34052     },
34053
34054     stripeRows : function(startRow){
34055         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34056             return;
34057         }
34058         startRow = startRow || 0;
34059         var rows = this.getBodyTable().rows;
34060         var lrows = this.getLockedTable().rows;
34061         var cls = ' x-grid-row-alt ';
34062         for(var i = startRow, len = rows.length; i < len; i++){
34063             var row = rows[i], lrow = lrows[i];
34064             var isAlt = ((i+1) % 2 == 0);
34065             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34066             if(isAlt == hasAlt){
34067                 continue;
34068             }
34069             if(isAlt){
34070                 row.className += " x-grid-row-alt";
34071             }else{
34072                 row.className = row.className.replace("x-grid-row-alt", "");
34073             }
34074             if(lrow){
34075                 lrow.className = row.className;
34076             }
34077         }
34078     },
34079
34080     restoreScroll : function(state){
34081         //Roo.log('GridView.restoreScroll');
34082         var sb = this.scroller.dom;
34083         sb.scrollLeft = state.left;
34084         sb.scrollTop = state.top;
34085         this.syncScroll();
34086     },
34087
34088     syncScroll : function(){
34089         //Roo.log('GridView.syncScroll');
34090         var sb = this.scroller.dom;
34091         var sh = this.mainHd.dom;
34092         var bs = this.mainBody.dom;
34093         var lv = this.lockedBody.dom;
34094         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34095         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34096     },
34097
34098     handleScroll : function(e){
34099         this.syncScroll();
34100         var sb = this.scroller.dom;
34101         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34102         e.stopEvent();
34103     },
34104
34105     handleWheel : function(e){
34106         var d = e.getWheelDelta();
34107         this.scroller.dom.scrollTop -= d*22;
34108         // set this here to prevent jumpy scrolling on large tables
34109         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34110         e.stopEvent();
34111     },
34112
34113     renderRows : function(startRow, endRow){
34114         // pull in all the crap needed to render rows
34115         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34116         var colCount = cm.getColumnCount();
34117
34118         if(ds.getCount() < 1){
34119             return ["", ""];
34120         }
34121
34122         // build a map for all the columns
34123         var cs = [];
34124         for(var i = 0; i < colCount; i++){
34125             var name = cm.getDataIndex(i);
34126             cs[i] = {
34127                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34128                 renderer : cm.getRenderer(i),
34129                 id : cm.getColumnId(i),
34130                 locked : cm.isLocked(i),
34131                 has_editor : cm.isCellEditable(i)
34132             };
34133         }
34134
34135         startRow = startRow || 0;
34136         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34137
34138         // records to render
34139         var rs = ds.getRange(startRow, endRow);
34140
34141         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34142     },
34143
34144     // As much as I hate to duplicate code, this was branched because FireFox really hates
34145     // [].join("") on strings. The performance difference was substantial enough to
34146     // branch this function
34147     doRender : Roo.isGecko ?
34148             function(cs, rs, ds, startRow, colCount, stripe){
34149                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34150                 // buffers
34151                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34152                 
34153                 var hasListener = this.grid.hasListener('rowclass');
34154                 var rowcfg = {};
34155                 for(var j = 0, len = rs.length; j < len; j++){
34156                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34157                     for(var i = 0; i < colCount; i++){
34158                         c = cs[i];
34159                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34160                         p.id = c.id;
34161                         p.css = p.attr = "";
34162                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34163                         if(p.value == undefined || p.value === "") {
34164                             p.value = "&#160;";
34165                         }
34166                         if(c.has_editor){
34167                             p.css += ' x-grid-editable-cell';
34168                         }
34169                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34170                             p.css +=  ' x-grid-dirty-cell';
34171                         }
34172                         var markup = ct.apply(p);
34173                         if(!c.locked){
34174                             cb+= markup;
34175                         }else{
34176                             lcb+= markup;
34177                         }
34178                     }
34179                     var alt = [];
34180                     if(stripe && ((rowIndex+1) % 2 == 0)){
34181                         alt.push("x-grid-row-alt")
34182                     }
34183                     if(r.dirty){
34184                         alt.push(  " x-grid-dirty-row");
34185                     }
34186                     rp.cells = lcb;
34187                     if(this.getRowClass){
34188                         alt.push(this.getRowClass(r, rowIndex));
34189                     }
34190                     if (hasListener) {
34191                         rowcfg = {
34192                              
34193                             record: r,
34194                             rowIndex : rowIndex,
34195                             rowClass : ''
34196                         };
34197                         this.grid.fireEvent('rowclass', this, rowcfg);
34198                         alt.push(rowcfg.rowClass);
34199                     }
34200                     rp.alt = alt.join(" ");
34201                     lbuf+= rt.apply(rp);
34202                     rp.cells = cb;
34203                     buf+=  rt.apply(rp);
34204                 }
34205                 return [lbuf, buf];
34206             } :
34207             function(cs, rs, ds, startRow, colCount, stripe){
34208                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34209                 // buffers
34210                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34211                 var hasListener = this.grid.hasListener('rowclass');
34212  
34213                 var rowcfg = {};
34214                 for(var j = 0, len = rs.length; j < len; j++){
34215                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34216                     for(var i = 0; i < colCount; i++){
34217                         c = cs[i];
34218                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34219                         p.id = c.id;
34220                         p.css = p.attr = "";
34221                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34222                         if(p.value == undefined || p.value === "") {
34223                             p.value = "&#160;";
34224                         }
34225                         //Roo.log(c);
34226                          if(c.has_editor){
34227                             p.css += ' x-grid-editable-cell';
34228                         }
34229                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34230                             p.css += ' x-grid-dirty-cell' 
34231                         }
34232                         
34233                         var markup = ct.apply(p);
34234                         if(!c.locked){
34235                             cb[cb.length] = markup;
34236                         }else{
34237                             lcb[lcb.length] = markup;
34238                         }
34239                     }
34240                     var alt = [];
34241                     if(stripe && ((rowIndex+1) % 2 == 0)){
34242                         alt.push( "x-grid-row-alt");
34243                     }
34244                     if(r.dirty){
34245                         alt.push(" x-grid-dirty-row");
34246                     }
34247                     rp.cells = lcb;
34248                     if(this.getRowClass){
34249                         alt.push( this.getRowClass(r, rowIndex));
34250                     }
34251                     if (hasListener) {
34252                         rowcfg = {
34253                              
34254                             record: r,
34255                             rowIndex : rowIndex,
34256                             rowClass : ''
34257                         };
34258                         this.grid.fireEvent('rowclass', this, rowcfg);
34259                         alt.push(rowcfg.rowClass);
34260                     }
34261                     
34262                     rp.alt = alt.join(" ");
34263                     rp.cells = lcb.join("");
34264                     lbuf[lbuf.length] = rt.apply(rp);
34265                     rp.cells = cb.join("");
34266                     buf[buf.length] =  rt.apply(rp);
34267                 }
34268                 return [lbuf.join(""), buf.join("")];
34269             },
34270
34271     renderBody : function(){
34272         var markup = this.renderRows();
34273         var bt = this.templates.body;
34274         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34275     },
34276
34277     /**
34278      * Refreshes the grid
34279      * @param {Boolean} headersToo
34280      */
34281     refresh : function(headersToo){
34282         this.fireEvent("beforerefresh", this);
34283         this.grid.stopEditing();
34284         var result = this.renderBody();
34285         this.lockedBody.update(result[0]);
34286         this.mainBody.update(result[1]);
34287         if(headersToo === true){
34288             this.updateHeaders();
34289             this.updateColumns();
34290             this.updateSplitters();
34291             this.updateHeaderSortState();
34292         }
34293         this.syncRowHeights();
34294         this.layout();
34295         this.fireEvent("refresh", this);
34296     },
34297
34298     handleColumnMove : function(cm, oldIndex, newIndex){
34299         this.indexMap = null;
34300         var s = this.getScrollState();
34301         this.refresh(true);
34302         this.restoreScroll(s);
34303         this.afterMove(newIndex);
34304     },
34305
34306     afterMove : function(colIndex){
34307         if(this.enableMoveAnim && Roo.enableFx){
34308             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34309         }
34310         // if multisort - fix sortOrder, and reload..
34311         if (this.grid.dataSource.multiSort) {
34312             // the we can call sort again..
34313             var dm = this.grid.dataSource;
34314             var cm = this.grid.colModel;
34315             var so = [];
34316             for(var i = 0; i < cm.config.length; i++ ) {
34317                 
34318                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34319                     continue; // dont' bother, it's not in sort list or being set.
34320                 }
34321                 
34322                 so.push(cm.config[i].dataIndex);
34323             };
34324             dm.sortOrder = so;
34325             dm.load(dm.lastOptions);
34326             
34327             
34328         }
34329         
34330     },
34331
34332     updateCell : function(dm, rowIndex, dataIndex){
34333         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34334         if(typeof colIndex == "undefined"){ // not present in grid
34335             return;
34336         }
34337         var cm = this.grid.colModel;
34338         var cell = this.getCell(rowIndex, colIndex);
34339         var cellText = this.getCellText(rowIndex, colIndex);
34340
34341         var p = {
34342             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34343             id : cm.getColumnId(colIndex),
34344             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34345         };
34346         var renderer = cm.getRenderer(colIndex);
34347         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34348         if(typeof val == "undefined" || val === "") {
34349             val = "&#160;";
34350         }
34351         cellText.innerHTML = val;
34352         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34353         this.syncRowHeights(rowIndex, rowIndex);
34354     },
34355
34356     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34357         var maxWidth = 0;
34358         if(this.grid.autoSizeHeaders){
34359             var h = this.getHeaderCellMeasure(colIndex);
34360             maxWidth = Math.max(maxWidth, h.scrollWidth);
34361         }
34362         var tb, index;
34363         if(this.cm.isLocked(colIndex)){
34364             tb = this.getLockedTable();
34365             index = colIndex;
34366         }else{
34367             tb = this.getBodyTable();
34368             index = colIndex - this.cm.getLockedCount();
34369         }
34370         if(tb && tb.rows){
34371             var rows = tb.rows;
34372             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34373             for(var i = 0; i < stopIndex; i++){
34374                 var cell = rows[i].childNodes[index].firstChild;
34375                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34376             }
34377         }
34378         return maxWidth + /*margin for error in IE*/ 5;
34379     },
34380     /**
34381      * Autofit a column to its content.
34382      * @param {Number} colIndex
34383      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34384      */
34385      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34386          if(this.cm.isHidden(colIndex)){
34387              return; // can't calc a hidden column
34388          }
34389         if(forceMinSize){
34390             var cid = this.cm.getColumnId(colIndex);
34391             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34392            if(this.grid.autoSizeHeaders){
34393                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34394            }
34395         }
34396         var newWidth = this.calcColumnWidth(colIndex);
34397         this.cm.setColumnWidth(colIndex,
34398             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34399         if(!suppressEvent){
34400             this.grid.fireEvent("columnresize", colIndex, newWidth);
34401         }
34402     },
34403
34404     /**
34405      * Autofits all columns to their content and then expands to fit any extra space in the grid
34406      */
34407      autoSizeColumns : function(){
34408         var cm = this.grid.colModel;
34409         var colCount = cm.getColumnCount();
34410         for(var i = 0; i < colCount; i++){
34411             this.autoSizeColumn(i, true, true);
34412         }
34413         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34414             this.fitColumns();
34415         }else{
34416             this.updateColumns();
34417             this.layout();
34418         }
34419     },
34420
34421     /**
34422      * Autofits all columns to the grid's width proportionate with their current size
34423      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34424      */
34425     fitColumns : function(reserveScrollSpace){
34426         var cm = this.grid.colModel;
34427         var colCount = cm.getColumnCount();
34428         var cols = [];
34429         var width = 0;
34430         var i, w;
34431         for (i = 0; i < colCount; i++){
34432             if(!cm.isHidden(i) && !cm.isFixed(i)){
34433                 w = cm.getColumnWidth(i);
34434                 cols.push(i);
34435                 cols.push(w);
34436                 width += w;
34437             }
34438         }
34439         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34440         if(reserveScrollSpace){
34441             avail -= 17;
34442         }
34443         var frac = (avail - cm.getTotalWidth())/width;
34444         while (cols.length){
34445             w = cols.pop();
34446             i = cols.pop();
34447             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34448         }
34449         this.updateColumns();
34450         this.layout();
34451     },
34452
34453     onRowSelect : function(rowIndex){
34454         var row = this.getRowComposite(rowIndex);
34455         row.addClass("x-grid-row-selected");
34456     },
34457
34458     onRowDeselect : function(rowIndex){
34459         var row = this.getRowComposite(rowIndex);
34460         row.removeClass("x-grid-row-selected");
34461     },
34462
34463     onCellSelect : function(row, col){
34464         var cell = this.getCell(row, col);
34465         if(cell){
34466             Roo.fly(cell).addClass("x-grid-cell-selected");
34467         }
34468     },
34469
34470     onCellDeselect : function(row, col){
34471         var cell = this.getCell(row, col);
34472         if(cell){
34473             Roo.fly(cell).removeClass("x-grid-cell-selected");
34474         }
34475     },
34476
34477     updateHeaderSortState : function(){
34478         
34479         // sort state can be single { field: xxx, direction : yyy}
34480         // or   { xxx=>ASC , yyy : DESC ..... }
34481         
34482         var mstate = {};
34483         if (!this.ds.multiSort) { 
34484             var state = this.ds.getSortState();
34485             if(!state){
34486                 return;
34487             }
34488             mstate[state.field] = state.direction;
34489             // FIXME... - this is not used here.. but might be elsewhere..
34490             this.sortState = state;
34491             
34492         } else {
34493             mstate = this.ds.sortToggle;
34494         }
34495         //remove existing sort classes..
34496         
34497         var sc = this.sortClasses;
34498         var hds = this.el.select(this.headerSelector).removeClass(sc);
34499         
34500         for(var f in mstate) {
34501         
34502             var sortColumn = this.cm.findColumnIndex(f);
34503             
34504             if(sortColumn != -1){
34505                 var sortDir = mstate[f];        
34506                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34507             }
34508         }
34509         
34510          
34511         
34512     },
34513
34514
34515     handleHeaderClick : function(g, index,e){
34516         
34517         Roo.log("header click");
34518         
34519         if (Roo.isTouch) {
34520             // touch events on header are handled by context
34521             this.handleHdCtx(g,index,e);
34522             return;
34523         }
34524         
34525         
34526         if(this.headersDisabled){
34527             return;
34528         }
34529         var dm = g.dataSource, cm = g.colModel;
34530         if(!cm.isSortable(index)){
34531             return;
34532         }
34533         g.stopEditing();
34534         
34535         if (dm.multiSort) {
34536             // update the sortOrder
34537             var so = [];
34538             for(var i = 0; i < cm.config.length; i++ ) {
34539                 
34540                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34541                     continue; // dont' bother, it's not in sort list or being set.
34542                 }
34543                 
34544                 so.push(cm.config[i].dataIndex);
34545             };
34546             dm.sortOrder = so;
34547         }
34548         
34549         
34550         dm.sort(cm.getDataIndex(index));
34551     },
34552
34553
34554     destroy : function(){
34555         if(this.colMenu){
34556             this.colMenu.removeAll();
34557             Roo.menu.MenuMgr.unregister(this.colMenu);
34558             this.colMenu.getEl().remove();
34559             delete this.colMenu;
34560         }
34561         if(this.hmenu){
34562             this.hmenu.removeAll();
34563             Roo.menu.MenuMgr.unregister(this.hmenu);
34564             this.hmenu.getEl().remove();
34565             delete this.hmenu;
34566         }
34567         if(this.grid.enableColumnMove){
34568             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34569             if(dds){
34570                 for(var dd in dds){
34571                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34572                         var elid = dds[dd].dragElId;
34573                         dds[dd].unreg();
34574                         Roo.get(elid).remove();
34575                     } else if(dds[dd].config.isTarget){
34576                         dds[dd].proxyTop.remove();
34577                         dds[dd].proxyBottom.remove();
34578                         dds[dd].unreg();
34579                     }
34580                     if(Roo.dd.DDM.locationCache[dd]){
34581                         delete Roo.dd.DDM.locationCache[dd];
34582                     }
34583                 }
34584                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34585             }
34586         }
34587         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34588         this.bind(null, null);
34589         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34590     },
34591
34592     handleLockChange : function(){
34593         this.refresh(true);
34594     },
34595
34596     onDenyColumnLock : function(){
34597
34598     },
34599
34600     onDenyColumnHide : function(){
34601
34602     },
34603
34604     handleHdMenuClick : function(item){
34605         var index = this.hdCtxIndex;
34606         var cm = this.cm, ds = this.ds;
34607         switch(item.id){
34608             case "asc":
34609                 ds.sort(cm.getDataIndex(index), "ASC");
34610                 break;
34611             case "desc":
34612                 ds.sort(cm.getDataIndex(index), "DESC");
34613                 break;
34614             case "lock":
34615                 var lc = cm.getLockedCount();
34616                 if(cm.getColumnCount(true) <= lc+1){
34617                     this.onDenyColumnLock();
34618                     return;
34619                 }
34620                 if(lc != index){
34621                     cm.setLocked(index, true, true);
34622                     cm.moveColumn(index, lc);
34623                     this.grid.fireEvent("columnmove", index, lc);
34624                 }else{
34625                     cm.setLocked(index, true);
34626                 }
34627             break;
34628             case "unlock":
34629                 var lc = cm.getLockedCount();
34630                 if((lc-1) != index){
34631                     cm.setLocked(index, false, true);
34632                     cm.moveColumn(index, lc-1);
34633                     this.grid.fireEvent("columnmove", index, lc-1);
34634                 }else{
34635                     cm.setLocked(index, false);
34636                 }
34637             break;
34638             case 'wider': // used to expand cols on touch..
34639             case 'narrow':
34640                 var cw = cm.getColumnWidth(index);
34641                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34642                 cw = Math.max(0, cw);
34643                 cw = Math.min(cw,4000);
34644                 cm.setColumnWidth(index, cw);
34645                 break;
34646                 
34647             default:
34648                 index = cm.getIndexById(item.id.substr(4));
34649                 if(index != -1){
34650                     if(item.checked && cm.getColumnCount(true) <= 1){
34651                         this.onDenyColumnHide();
34652                         return false;
34653                     }
34654                     cm.setHidden(index, item.checked);
34655                 }
34656         }
34657         return true;
34658     },
34659
34660     beforeColMenuShow : function(){
34661         var cm = this.cm,  colCount = cm.getColumnCount();
34662         this.colMenu.removeAll();
34663         for(var i = 0; i < colCount; i++){
34664             this.colMenu.add(new Roo.menu.CheckItem({
34665                 id: "col-"+cm.getColumnId(i),
34666                 text: cm.getColumnHeader(i),
34667                 checked: !cm.isHidden(i),
34668                 hideOnClick:false
34669             }));
34670         }
34671     },
34672
34673     handleHdCtx : function(g, index, e){
34674         e.stopEvent();
34675         var hd = this.getHeaderCell(index);
34676         this.hdCtxIndex = index;
34677         var ms = this.hmenu.items, cm = this.cm;
34678         ms.get("asc").setDisabled(!cm.isSortable(index));
34679         ms.get("desc").setDisabled(!cm.isSortable(index));
34680         if(this.grid.enableColLock !== false){
34681             ms.get("lock").setDisabled(cm.isLocked(index));
34682             ms.get("unlock").setDisabled(!cm.isLocked(index));
34683         }
34684         this.hmenu.show(hd, "tl-bl");
34685     },
34686
34687     handleHdOver : function(e){
34688         var hd = this.findHeaderCell(e.getTarget());
34689         if(hd && !this.headersDisabled){
34690             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34691                this.fly(hd).addClass("x-grid-hd-over");
34692             }
34693         }
34694     },
34695
34696     handleHdOut : function(e){
34697         var hd = this.findHeaderCell(e.getTarget());
34698         if(hd){
34699             this.fly(hd).removeClass("x-grid-hd-over");
34700         }
34701     },
34702
34703     handleSplitDblClick : function(e, t){
34704         var i = this.getCellIndex(t);
34705         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34706             this.autoSizeColumn(i, true);
34707             this.layout();
34708         }
34709     },
34710
34711     render : function(){
34712
34713         var cm = this.cm;
34714         var colCount = cm.getColumnCount();
34715
34716         if(this.grid.monitorWindowResize === true){
34717             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34718         }
34719         var header = this.renderHeaders();
34720         var body = this.templates.body.apply({rows:""});
34721         var html = this.templates.master.apply({
34722             lockedBody: body,
34723             body: body,
34724             lockedHeader: header[0],
34725             header: header[1]
34726         });
34727
34728         //this.updateColumns();
34729
34730         this.grid.getGridEl().dom.innerHTML = html;
34731
34732         this.initElements();
34733         
34734         // a kludge to fix the random scolling effect in webkit
34735         this.el.on("scroll", function() {
34736             this.el.dom.scrollTop=0; // hopefully not recursive..
34737         },this);
34738
34739         this.scroller.on("scroll", this.handleScroll, this);
34740         this.lockedBody.on("mousewheel", this.handleWheel, this);
34741         this.mainBody.on("mousewheel", this.handleWheel, this);
34742
34743         this.mainHd.on("mouseover", this.handleHdOver, this);
34744         this.mainHd.on("mouseout", this.handleHdOut, this);
34745         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34746                 {delegate: "."+this.splitClass});
34747
34748         this.lockedHd.on("mouseover", this.handleHdOver, this);
34749         this.lockedHd.on("mouseout", this.handleHdOut, this);
34750         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34751                 {delegate: "."+this.splitClass});
34752
34753         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34754             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34755         }
34756
34757         this.updateSplitters();
34758
34759         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34760             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34761             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34762         }
34763
34764         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34765             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34766             this.hmenu.add(
34767                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34768                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34769             );
34770             if(this.grid.enableColLock !== false){
34771                 this.hmenu.add('-',
34772                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34773                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34774                 );
34775             }
34776             if (Roo.isTouch) {
34777                  this.hmenu.add('-',
34778                     {id:"wider", text: this.columnsWiderText},
34779                     {id:"narrow", text: this.columnsNarrowText }
34780                 );
34781                 
34782                  
34783             }
34784             
34785             if(this.grid.enableColumnHide !== false){
34786
34787                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34788                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34789                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34790
34791                 this.hmenu.add('-',
34792                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34793                 );
34794             }
34795             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34796
34797             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34798         }
34799
34800         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34801             this.dd = new Roo.grid.GridDragZone(this.grid, {
34802                 ddGroup : this.grid.ddGroup || 'GridDD'
34803             });
34804             
34805         }
34806
34807         /*
34808         for(var i = 0; i < colCount; i++){
34809             if(cm.isHidden(i)){
34810                 this.hideColumn(i);
34811             }
34812             if(cm.config[i].align){
34813                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34814                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34815             }
34816         }*/
34817         
34818         this.updateHeaderSortState();
34819
34820         this.beforeInitialResize();
34821         this.layout(true);
34822
34823         // two part rendering gives faster view to the user
34824         this.renderPhase2.defer(1, this);
34825     },
34826
34827     renderPhase2 : function(){
34828         // render the rows now
34829         this.refresh();
34830         if(this.grid.autoSizeColumns){
34831             this.autoSizeColumns();
34832         }
34833     },
34834
34835     beforeInitialResize : function(){
34836
34837     },
34838
34839     onColumnSplitterMoved : function(i, w){
34840         this.userResized = true;
34841         var cm = this.grid.colModel;
34842         cm.setColumnWidth(i, w, true);
34843         var cid = cm.getColumnId(i);
34844         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34845         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34846         this.updateSplitters();
34847         this.layout();
34848         this.grid.fireEvent("columnresize", i, w);
34849     },
34850
34851     syncRowHeights : function(startIndex, endIndex){
34852         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34853             startIndex = startIndex || 0;
34854             var mrows = this.getBodyTable().rows;
34855             var lrows = this.getLockedTable().rows;
34856             var len = mrows.length-1;
34857             endIndex = Math.min(endIndex || len, len);
34858             for(var i = startIndex; i <= endIndex; i++){
34859                 var m = mrows[i], l = lrows[i];
34860                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34861                 m.style.height = l.style.height = h + "px";
34862             }
34863         }
34864     },
34865
34866     layout : function(initialRender, is2ndPass)
34867     {
34868         var g = this.grid;
34869         var auto = g.autoHeight;
34870         var scrollOffset = 16;
34871         var c = g.getGridEl(), cm = this.cm,
34872                 expandCol = g.autoExpandColumn,
34873                 gv = this;
34874         //c.beginMeasure();
34875
34876         if(!c.dom.offsetWidth){ // display:none?
34877             if(initialRender){
34878                 this.lockedWrap.show();
34879                 this.mainWrap.show();
34880             }
34881             return;
34882         }
34883
34884         var hasLock = this.cm.isLocked(0);
34885
34886         var tbh = this.headerPanel.getHeight();
34887         var bbh = this.footerPanel.getHeight();
34888
34889         if(auto){
34890             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34891             var newHeight = ch + c.getBorderWidth("tb");
34892             if(g.maxHeight){
34893                 newHeight = Math.min(g.maxHeight, newHeight);
34894             }
34895             c.setHeight(newHeight);
34896         }
34897
34898         if(g.autoWidth){
34899             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34900         }
34901
34902         var s = this.scroller;
34903
34904         var csize = c.getSize(true);
34905
34906         this.el.setSize(csize.width, csize.height);
34907
34908         this.headerPanel.setWidth(csize.width);
34909         this.footerPanel.setWidth(csize.width);
34910
34911         var hdHeight = this.mainHd.getHeight();
34912         var vw = csize.width;
34913         var vh = csize.height - (tbh + bbh);
34914
34915         s.setSize(vw, vh);
34916
34917         var bt = this.getBodyTable();
34918         
34919         if(cm.getLockedCount() == cm.config.length){
34920             bt = this.getLockedTable();
34921         }
34922         
34923         var ltWidth = hasLock ?
34924                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34925
34926         var scrollHeight = bt.offsetHeight;
34927         var scrollWidth = ltWidth + bt.offsetWidth;
34928         var vscroll = false, hscroll = false;
34929
34930         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34931
34932         var lw = this.lockedWrap, mw = this.mainWrap;
34933         var lb = this.lockedBody, mb = this.mainBody;
34934
34935         setTimeout(function(){
34936             var t = s.dom.offsetTop;
34937             var w = s.dom.clientWidth,
34938                 h = s.dom.clientHeight;
34939
34940             lw.setTop(t);
34941             lw.setSize(ltWidth, h);
34942
34943             mw.setLeftTop(ltWidth, t);
34944             mw.setSize(w-ltWidth, h);
34945
34946             lb.setHeight(h-hdHeight);
34947             mb.setHeight(h-hdHeight);
34948
34949             if(is2ndPass !== true && !gv.userResized && expandCol){
34950                 // high speed resize without full column calculation
34951                 
34952                 var ci = cm.getIndexById(expandCol);
34953                 if (ci < 0) {
34954                     ci = cm.findColumnIndex(expandCol);
34955                 }
34956                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34957                 var expandId = cm.getColumnId(ci);
34958                 var  tw = cm.getTotalWidth(false);
34959                 var currentWidth = cm.getColumnWidth(ci);
34960                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34961                 if(currentWidth != cw){
34962                     cm.setColumnWidth(ci, cw, true);
34963                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34964                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34965                     gv.updateSplitters();
34966                     gv.layout(false, true);
34967                 }
34968             }
34969
34970             if(initialRender){
34971                 lw.show();
34972                 mw.show();
34973             }
34974             //c.endMeasure();
34975         }, 10);
34976     },
34977
34978     onWindowResize : function(){
34979         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34980             return;
34981         }
34982         this.layout();
34983     },
34984
34985     appendFooter : function(parentEl){
34986         return null;
34987     },
34988
34989     sortAscText : "Sort Ascending",
34990     sortDescText : "Sort Descending",
34991     lockText : "Lock Column",
34992     unlockText : "Unlock Column",
34993     columnsText : "Columns",
34994  
34995     columnsWiderText : "Wider",
34996     columnsNarrowText : "Thinner"
34997 });
34998
34999
35000 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35001     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35002     this.proxy.el.addClass('x-grid3-col-dd');
35003 };
35004
35005 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35006     handleMouseDown : function(e){
35007
35008     },
35009
35010     callHandleMouseDown : function(e){
35011         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35012     }
35013 });
35014 /*
35015  * Based on:
35016  * Ext JS Library 1.1.1
35017  * Copyright(c) 2006-2007, Ext JS, LLC.
35018  *
35019  * Originally Released Under LGPL - original licence link has changed is not relivant.
35020  *
35021  * Fork - LGPL
35022  * <script type="text/javascript">
35023  */
35024  /**
35025  * @extends Roo.dd.DDProxy
35026  * @class Roo.grid.SplitDragZone
35027  * Support for Column Header resizing
35028  * @constructor
35029  * @param {Object} config
35030  */
35031 // private
35032 // This is a support class used internally by the Grid components
35033 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35034     this.grid = grid;
35035     this.view = grid.getView();
35036     this.proxy = this.view.resizeProxy;
35037     Roo.grid.SplitDragZone.superclass.constructor.call(
35038         this,
35039         hd, // ID
35040         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35041         {  // CONFIG
35042             dragElId : Roo.id(this.proxy.dom),
35043             resizeFrame:false
35044         }
35045     );
35046     
35047     this.setHandleElId(Roo.id(hd));
35048     if (hd2 !== false) {
35049         this.setOuterHandleElId(Roo.id(hd2));
35050     }
35051     
35052     this.scroll = false;
35053 };
35054 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35055     fly: Roo.Element.fly,
35056
35057     b4StartDrag : function(x, y){
35058         this.view.headersDisabled = true;
35059         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35060                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35061         );
35062         this.proxy.setHeight(h);
35063         
35064         // for old system colWidth really stored the actual width?
35065         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35066         // which in reality did not work.. - it worked only for fixed sizes
35067         // for resizable we need to use actual sizes.
35068         var w = this.cm.getColumnWidth(this.cellIndex);
35069         if (!this.view.mainWrap) {
35070             // bootstrap.
35071             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35072         }
35073         
35074         
35075         
35076         // this was w-this.grid.minColumnWidth;
35077         // doesnt really make sense? - w = thie curren width or the rendered one?
35078         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35079         this.resetConstraints();
35080         this.setXConstraint(minw, 1000);
35081         this.setYConstraint(0, 0);
35082         this.minX = x - minw;
35083         this.maxX = x + 1000;
35084         this.startPos = x;
35085         if (!this.view.mainWrap) { // this is Bootstrap code..
35086             this.getDragEl().style.display='block';
35087         }
35088         
35089         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35090     },
35091
35092
35093     handleMouseDown : function(e){
35094         ev = Roo.EventObject.setEvent(e);
35095         var t = this.fly(ev.getTarget());
35096         if(t.hasClass("x-grid-split")){
35097             this.cellIndex = this.view.getCellIndex(t.dom);
35098             this.split = t.dom;
35099             this.cm = this.grid.colModel;
35100             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35101                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35102             }
35103         }
35104     },
35105
35106     endDrag : function(e){
35107         this.view.headersDisabled = false;
35108         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35109         var diff = endX - this.startPos;
35110         // 
35111         var w = this.cm.getColumnWidth(this.cellIndex);
35112         if (!this.view.mainWrap) {
35113             w = 0;
35114         }
35115         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35116     },
35117
35118     autoOffset : function(){
35119         this.setDelta(0,0);
35120     }
35121 });/*
35122  * Based on:
35123  * Ext JS Library 1.1.1
35124  * Copyright(c) 2006-2007, Ext JS, LLC.
35125  *
35126  * Originally Released Under LGPL - original licence link has changed is not relivant.
35127  *
35128  * Fork - LGPL
35129  * <script type="text/javascript">
35130  */
35131  
35132 // private
35133 // This is a support class used internally by the Grid components
35134 Roo.grid.GridDragZone = function(grid, config){
35135     this.view = grid.getView();
35136     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35137     if(this.view.lockedBody){
35138         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35139         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35140     }
35141     this.scroll = false;
35142     this.grid = grid;
35143     this.ddel = document.createElement('div');
35144     this.ddel.className = 'x-grid-dd-wrap';
35145 };
35146
35147 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35148     ddGroup : "GridDD",
35149
35150     getDragData : function(e){
35151         var t = Roo.lib.Event.getTarget(e);
35152         var rowIndex = this.view.findRowIndex(t);
35153         var sm = this.grid.selModel;
35154             
35155         //Roo.log(rowIndex);
35156         
35157         if (sm.getSelectedCell) {
35158             // cell selection..
35159             if (!sm.getSelectedCell()) {
35160                 return false;
35161             }
35162             if (rowIndex != sm.getSelectedCell()[0]) {
35163                 return false;
35164             }
35165         
35166         }
35167         if (sm.getSelections && sm.getSelections().length < 1) {
35168             return false;
35169         }
35170         
35171         
35172         // before it used to all dragging of unseleted... - now we dont do that.
35173         if(rowIndex !== false){
35174             
35175             // if editorgrid.. 
35176             
35177             
35178             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35179                
35180             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35181               //  
35182             //}
35183             if (e.hasModifier()){
35184                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35185             }
35186             
35187             Roo.log("getDragData");
35188             
35189             return {
35190                 grid: this.grid,
35191                 ddel: this.ddel,
35192                 rowIndex: rowIndex,
35193                 selections: sm.getSelections ? sm.getSelections() : (
35194                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35195             };
35196         }
35197         return false;
35198     },
35199     
35200     
35201     onInitDrag : function(e){
35202         var data = this.dragData;
35203         this.ddel.innerHTML = this.grid.getDragDropText();
35204         this.proxy.update(this.ddel);
35205         // fire start drag?
35206     },
35207
35208     afterRepair : function(){
35209         this.dragging = false;
35210     },
35211
35212     getRepairXY : function(e, data){
35213         return false;
35214     },
35215
35216     onEndDrag : function(data, e){
35217         // fire end drag?
35218     },
35219
35220     onValidDrop : function(dd, e, id){
35221         // fire drag drop?
35222         this.hideProxy();
35223     },
35224
35225     beforeInvalidDrop : function(e, id){
35226
35227     }
35228 });/*
35229  * Based on:
35230  * Ext JS Library 1.1.1
35231  * Copyright(c) 2006-2007, Ext JS, LLC.
35232  *
35233  * Originally Released Under LGPL - original licence link has changed is not relivant.
35234  *
35235  * Fork - LGPL
35236  * <script type="text/javascript">
35237  */
35238  
35239
35240 /**
35241  * @class Roo.grid.ColumnModel
35242  * @extends Roo.util.Observable
35243  * This is the default implementation of a ColumnModel used by the Grid. It defines
35244  * the columns in the grid.
35245  * <br>Usage:<br>
35246  <pre><code>
35247  var colModel = new Roo.grid.ColumnModel([
35248         {header: "Ticker", width: 60, sortable: true, locked: true},
35249         {header: "Company Name", width: 150, sortable: true},
35250         {header: "Market Cap.", width: 100, sortable: true},
35251         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35252         {header: "Employees", width: 100, sortable: true, resizable: false}
35253  ]);
35254  </code></pre>
35255  * <p>
35256  
35257  * The config options listed for this class are options which may appear in each
35258  * individual column definition.
35259  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35260  * @constructor
35261  * @param {Object} config An Array of column config objects. See this class's
35262  * config objects for details.
35263 */
35264 Roo.grid.ColumnModel = function(config){
35265         /**
35266      * The config passed into the constructor
35267      */
35268     this.config = []; //config;
35269     this.lookup = {};
35270
35271     // if no id, create one
35272     // if the column does not have a dataIndex mapping,
35273     // map it to the order it is in the config
35274     for(var i = 0, len = config.length; i < len; i++){
35275         this.addColumn(config[i]);
35276         
35277     }
35278
35279     /**
35280      * The width of columns which have no width specified (defaults to 100)
35281      * @type Number
35282      */
35283     this.defaultWidth = 100;
35284
35285     /**
35286      * Default sortable of columns which have no sortable specified (defaults to false)
35287      * @type Boolean
35288      */
35289     this.defaultSortable = false;
35290
35291     this.addEvents({
35292         /**
35293              * @event widthchange
35294              * Fires when the width of a column changes.
35295              * @param {ColumnModel} this
35296              * @param {Number} columnIndex The column index
35297              * @param {Number} newWidth The new width
35298              */
35299             "widthchange": true,
35300         /**
35301              * @event headerchange
35302              * Fires when the text of a header changes.
35303              * @param {ColumnModel} this
35304              * @param {Number} columnIndex The column index
35305              * @param {Number} newText The new header text
35306              */
35307             "headerchange": true,
35308         /**
35309              * @event hiddenchange
35310              * Fires when a column is hidden or "unhidden".
35311              * @param {ColumnModel} this
35312              * @param {Number} columnIndex The column index
35313              * @param {Boolean} hidden true if hidden, false otherwise
35314              */
35315             "hiddenchange": true,
35316             /**
35317          * @event columnmoved
35318          * Fires when a column is moved.
35319          * @param {ColumnModel} this
35320          * @param {Number} oldIndex
35321          * @param {Number} newIndex
35322          */
35323         "columnmoved" : true,
35324         /**
35325          * @event columlockchange
35326          * Fires when a column's locked state is changed
35327          * @param {ColumnModel} this
35328          * @param {Number} colIndex
35329          * @param {Boolean} locked true if locked
35330          */
35331         "columnlockchange" : true
35332     });
35333     Roo.grid.ColumnModel.superclass.constructor.call(this);
35334 };
35335 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35336     /**
35337      * @cfg {String} header The header text to display in the Grid view.
35338      */
35339         /**
35340      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35341      */
35342         /**
35343      * @cfg {String} smHeader Header at Bootsrap Small width
35344      */
35345         /**
35346      * @cfg {String} mdHeader Header at Bootsrap Medium width
35347      */
35348         /**
35349      * @cfg {String} lgHeader Header at Bootsrap Large width
35350      */
35351         /**
35352      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35353      */
35354     /**
35355      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35356      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35357      * specified, the column's index is used as an index into the Record's data Array.
35358      */
35359     /**
35360      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35361      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35362      */
35363     /**
35364      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35365      * Defaults to the value of the {@link #defaultSortable} property.
35366      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35367      */
35368     /**
35369      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35370      */
35371     /**
35372      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35373      */
35374     /**
35375      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35376      */
35377     /**
35378      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35379      */
35380     /**
35381      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35382      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35383      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35384      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35385      */
35386        /**
35387      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35388      */
35389     /**
35390      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35391      */
35392     /**
35393      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35394      */
35395     /**
35396      * @cfg {String} cursor (Optional)
35397      */
35398     /**
35399      * @cfg {String} tooltip (Optional)
35400      */
35401     /**
35402      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35403      */
35404     /**
35405      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35406      */
35407     /**
35408      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35409      */
35410     /**
35411      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35412      */
35413         /**
35414      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35415      */
35416     /**
35417      * Returns the id of the column at the specified index.
35418      * @param {Number} index The column index
35419      * @return {String} the id
35420      */
35421     getColumnId : function(index){
35422         return this.config[index].id;
35423     },
35424
35425     /**
35426      * Returns the column for a specified id.
35427      * @param {String} id The column id
35428      * @return {Object} the column
35429      */
35430     getColumnById : function(id){
35431         return this.lookup[id];
35432     },
35433
35434     
35435     /**
35436      * Returns the column Object for a specified dataIndex.
35437      * @param {String} dataIndex The column dataIndex
35438      * @return {Object|Boolean} the column or false if not found
35439      */
35440     getColumnByDataIndex: function(dataIndex){
35441         var index = this.findColumnIndex(dataIndex);
35442         return index > -1 ? this.config[index] : false;
35443     },
35444     
35445     /**
35446      * Returns the index for a specified column id.
35447      * @param {String} id The column id
35448      * @return {Number} the index, or -1 if not found
35449      */
35450     getIndexById : function(id){
35451         for(var i = 0, len = this.config.length; i < len; i++){
35452             if(this.config[i].id == id){
35453                 return i;
35454             }
35455         }
35456         return -1;
35457     },
35458     
35459     /**
35460      * Returns the index for a specified column dataIndex.
35461      * @param {String} dataIndex The column dataIndex
35462      * @return {Number} the index, or -1 if not found
35463      */
35464     
35465     findColumnIndex : function(dataIndex){
35466         for(var i = 0, len = this.config.length; i < len; i++){
35467             if(this.config[i].dataIndex == dataIndex){
35468                 return i;
35469             }
35470         }
35471         return -1;
35472     },
35473     
35474     
35475     moveColumn : function(oldIndex, newIndex){
35476         var c = this.config[oldIndex];
35477         this.config.splice(oldIndex, 1);
35478         this.config.splice(newIndex, 0, c);
35479         this.dataMap = null;
35480         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35481     },
35482
35483     isLocked : function(colIndex){
35484         return this.config[colIndex].locked === true;
35485     },
35486
35487     setLocked : function(colIndex, value, suppressEvent){
35488         if(this.isLocked(colIndex) == value){
35489             return;
35490         }
35491         this.config[colIndex].locked = value;
35492         if(!suppressEvent){
35493             this.fireEvent("columnlockchange", this, colIndex, value);
35494         }
35495     },
35496
35497     getTotalLockedWidth : function(){
35498         var totalWidth = 0;
35499         for(var i = 0; i < this.config.length; i++){
35500             if(this.isLocked(i) && !this.isHidden(i)){
35501                 this.totalWidth += this.getColumnWidth(i);
35502             }
35503         }
35504         return totalWidth;
35505     },
35506
35507     getLockedCount : function(){
35508         for(var i = 0, len = this.config.length; i < len; i++){
35509             if(!this.isLocked(i)){
35510                 return i;
35511             }
35512         }
35513         
35514         return this.config.length;
35515     },
35516
35517     /**
35518      * Returns the number of columns.
35519      * @return {Number}
35520      */
35521     getColumnCount : function(visibleOnly){
35522         if(visibleOnly === true){
35523             var c = 0;
35524             for(var i = 0, len = this.config.length; i < len; i++){
35525                 if(!this.isHidden(i)){
35526                     c++;
35527                 }
35528             }
35529             return c;
35530         }
35531         return this.config.length;
35532     },
35533
35534     /**
35535      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35536      * @param {Function} fn
35537      * @param {Object} scope (optional)
35538      * @return {Array} result
35539      */
35540     getColumnsBy : function(fn, scope){
35541         var r = [];
35542         for(var i = 0, len = this.config.length; i < len; i++){
35543             var c = this.config[i];
35544             if(fn.call(scope||this, c, i) === true){
35545                 r[r.length] = c;
35546             }
35547         }
35548         return r;
35549     },
35550
35551     /**
35552      * Returns true if the specified column is sortable.
35553      * @param {Number} col The column index
35554      * @return {Boolean}
35555      */
35556     isSortable : function(col){
35557         if(typeof this.config[col].sortable == "undefined"){
35558             return this.defaultSortable;
35559         }
35560         return this.config[col].sortable;
35561     },
35562
35563     /**
35564      * Returns the rendering (formatting) function defined for the column.
35565      * @param {Number} col The column index.
35566      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35567      */
35568     getRenderer : function(col){
35569         if(!this.config[col].renderer){
35570             return Roo.grid.ColumnModel.defaultRenderer;
35571         }
35572         return this.config[col].renderer;
35573     },
35574
35575     /**
35576      * Sets the rendering (formatting) function for a column.
35577      * @param {Number} col The column index
35578      * @param {Function} fn The function to use to process the cell's raw data
35579      * to return HTML markup for the grid view. The render function is called with
35580      * the following parameters:<ul>
35581      * <li>Data value.</li>
35582      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35583      * <li>css A CSS style string to apply to the table cell.</li>
35584      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35585      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35586      * <li>Row index</li>
35587      * <li>Column index</li>
35588      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35589      */
35590     setRenderer : function(col, fn){
35591         this.config[col].renderer = fn;
35592     },
35593
35594     /**
35595      * Returns the width for the specified column.
35596      * @param {Number} col The column index
35597      * @param (optional) {String} gridSize bootstrap width size.
35598      * @return {Number}
35599      */
35600     getColumnWidth : function(col, gridSize)
35601         {
35602                 var cfg = this.config[col];
35603                 
35604                 if (typeof(gridSize) == 'undefined') {
35605                         return cfg.width * 1 || this.defaultWidth;
35606                 }
35607                 if (gridSize === false) { // if we set it..
35608                         return cfg.width || false;
35609                 }
35610                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35611                 
35612                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35613                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35614                                 continue;
35615                         }
35616                         return cfg[ sizes[i] ];
35617                 }
35618                 return 1;
35619                 
35620     },
35621
35622     /**
35623      * Sets the width for a column.
35624      * @param {Number} col The column index
35625      * @param {Number} width The new width
35626      */
35627     setColumnWidth : function(col, width, suppressEvent){
35628         this.config[col].width = width;
35629         this.totalWidth = null;
35630         if(!suppressEvent){
35631              this.fireEvent("widthchange", this, col, width);
35632         }
35633     },
35634
35635     /**
35636      * Returns the total width of all columns.
35637      * @param {Boolean} includeHidden True to include hidden column widths
35638      * @return {Number}
35639      */
35640     getTotalWidth : function(includeHidden){
35641         if(!this.totalWidth){
35642             this.totalWidth = 0;
35643             for(var i = 0, len = this.config.length; i < len; i++){
35644                 if(includeHidden || !this.isHidden(i)){
35645                     this.totalWidth += this.getColumnWidth(i);
35646                 }
35647             }
35648         }
35649         return this.totalWidth;
35650     },
35651
35652     /**
35653      * Returns the header for the specified column.
35654      * @param {Number} col The column index
35655      * @return {String}
35656      */
35657     getColumnHeader : function(col){
35658         return this.config[col].header;
35659     },
35660
35661     /**
35662      * Sets the header for a column.
35663      * @param {Number} col The column index
35664      * @param {String} header The new header
35665      */
35666     setColumnHeader : function(col, header){
35667         this.config[col].header = header;
35668         this.fireEvent("headerchange", this, col, header);
35669     },
35670
35671     /**
35672      * Returns the tooltip for the specified column.
35673      * @param {Number} col The column index
35674      * @return {String}
35675      */
35676     getColumnTooltip : function(col){
35677             return this.config[col].tooltip;
35678     },
35679     /**
35680      * Sets the tooltip for a column.
35681      * @param {Number} col The column index
35682      * @param {String} tooltip The new tooltip
35683      */
35684     setColumnTooltip : function(col, tooltip){
35685             this.config[col].tooltip = tooltip;
35686     },
35687
35688     /**
35689      * Returns the dataIndex for the specified column.
35690      * @param {Number} col The column index
35691      * @return {Number}
35692      */
35693     getDataIndex : function(col){
35694         return this.config[col].dataIndex;
35695     },
35696
35697     /**
35698      * Sets the dataIndex for a column.
35699      * @param {Number} col The column index
35700      * @param {Number} dataIndex The new dataIndex
35701      */
35702     setDataIndex : function(col, dataIndex){
35703         this.config[col].dataIndex = dataIndex;
35704     },
35705
35706     
35707     
35708     /**
35709      * Returns true if the cell is editable.
35710      * @param {Number} colIndex The column index
35711      * @param {Number} rowIndex The row index - this is nto actually used..?
35712      * @return {Boolean}
35713      */
35714     isCellEditable : function(colIndex, rowIndex){
35715         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35716     },
35717
35718     /**
35719      * Returns the editor defined for the cell/column.
35720      * return false or null to disable editing.
35721      * @param {Number} colIndex The column index
35722      * @param {Number} rowIndex The row index
35723      * @return {Object}
35724      */
35725     getCellEditor : function(colIndex, rowIndex){
35726         return this.config[colIndex].editor;
35727     },
35728
35729     /**
35730      * Sets if a column is editable.
35731      * @param {Number} col The column index
35732      * @param {Boolean} editable True if the column is editable
35733      */
35734     setEditable : function(col, editable){
35735         this.config[col].editable = editable;
35736     },
35737
35738
35739     /**
35740      * Returns true if the column is hidden.
35741      * @param {Number} colIndex The column index
35742      * @return {Boolean}
35743      */
35744     isHidden : function(colIndex){
35745         return this.config[colIndex].hidden;
35746     },
35747
35748
35749     /**
35750      * Returns true if the column width cannot be changed
35751      */
35752     isFixed : function(colIndex){
35753         return this.config[colIndex].fixed;
35754     },
35755
35756     /**
35757      * Returns true if the column can be resized
35758      * @return {Boolean}
35759      */
35760     isResizable : function(colIndex){
35761         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35762     },
35763     /**
35764      * Sets if a column is hidden.
35765      * @param {Number} colIndex The column index
35766      * @param {Boolean} hidden True if the column is hidden
35767      */
35768     setHidden : function(colIndex, hidden){
35769         this.config[colIndex].hidden = hidden;
35770         this.totalWidth = null;
35771         this.fireEvent("hiddenchange", this, colIndex, hidden);
35772     },
35773
35774     /**
35775      * Sets the editor for a column.
35776      * @param {Number} col The column index
35777      * @param {Object} editor The editor object
35778      */
35779     setEditor : function(col, editor){
35780         this.config[col].editor = editor;
35781     },
35782     /**
35783      * Add a column (experimental...) - defaults to adding to the end..
35784      * @param {Object} config 
35785     */
35786     addColumn : function(c)
35787     {
35788     
35789         var i = this.config.length;
35790         this.config[i] = c;
35791         
35792         if(typeof c.dataIndex == "undefined"){
35793             c.dataIndex = i;
35794         }
35795         if(typeof c.renderer == "string"){
35796             c.renderer = Roo.util.Format[c.renderer];
35797         }
35798         if(typeof c.id == "undefined"){
35799             c.id = Roo.id();
35800         }
35801         if(c.editor && c.editor.xtype){
35802             c.editor  = Roo.factory(c.editor, Roo.grid);
35803         }
35804         if(c.editor && c.editor.isFormField){
35805             c.editor = new Roo.grid.GridEditor(c.editor);
35806         }
35807         this.lookup[c.id] = c;
35808     }
35809     
35810 });
35811
35812 Roo.grid.ColumnModel.defaultRenderer = function(value)
35813 {
35814     if(typeof value == "object") {
35815         return value;
35816     }
35817         if(typeof value == "string" && value.length < 1){
35818             return "&#160;";
35819         }
35820     
35821         return String.format("{0}", value);
35822 };
35823
35824 // Alias for backwards compatibility
35825 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35826 /*
35827  * Based on:
35828  * Ext JS Library 1.1.1
35829  * Copyright(c) 2006-2007, Ext JS, LLC.
35830  *
35831  * Originally Released Under LGPL - original licence link has changed is not relivant.
35832  *
35833  * Fork - LGPL
35834  * <script type="text/javascript">
35835  */
35836
35837 /**
35838  * @class Roo.grid.AbstractSelectionModel
35839  * @extends Roo.util.Observable
35840  * @abstract
35841  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35842  * implemented by descendant classes.  This class should not be directly instantiated.
35843  * @constructor
35844  */
35845 Roo.grid.AbstractSelectionModel = function(){
35846     this.locked = false;
35847     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35848 };
35849
35850 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35851     /** @ignore Called by the grid automatically. Do not call directly. */
35852     init : function(grid){
35853         this.grid = grid;
35854         this.initEvents();
35855     },
35856
35857     /**
35858      * Locks the selections.
35859      */
35860     lock : function(){
35861         this.locked = true;
35862     },
35863
35864     /**
35865      * Unlocks the selections.
35866      */
35867     unlock : function(){
35868         this.locked = false;
35869     },
35870
35871     /**
35872      * Returns true if the selections are locked.
35873      * @return {Boolean}
35874      */
35875     isLocked : function(){
35876         return this.locked;
35877     }
35878 });/*
35879  * Based on:
35880  * Ext JS Library 1.1.1
35881  * Copyright(c) 2006-2007, Ext JS, LLC.
35882  *
35883  * Originally Released Under LGPL - original licence link has changed is not relivant.
35884  *
35885  * Fork - LGPL
35886  * <script type="text/javascript">
35887  */
35888 /**
35889  * @extends Roo.grid.AbstractSelectionModel
35890  * @class Roo.grid.RowSelectionModel
35891  * The default SelectionModel used by {@link Roo.grid.Grid}.
35892  * It supports multiple selections and keyboard selection/navigation. 
35893  * @constructor
35894  * @param {Object} config
35895  */
35896 Roo.grid.RowSelectionModel = function(config){
35897     Roo.apply(this, config);
35898     this.selections = new Roo.util.MixedCollection(false, function(o){
35899         return o.id;
35900     });
35901
35902     this.last = false;
35903     this.lastActive = false;
35904
35905     this.addEvents({
35906         /**
35907         * @event selectionchange
35908         * Fires when the selection changes
35909         * @param {SelectionModel} this
35910         */
35911        "selectionchange" : true,
35912        /**
35913         * @event afterselectionchange
35914         * Fires after the selection changes (eg. by key press or clicking)
35915         * @param {SelectionModel} this
35916         */
35917        "afterselectionchange" : true,
35918        /**
35919         * @event beforerowselect
35920         * Fires when a row is selected being selected, return false to cancel.
35921         * @param {SelectionModel} this
35922         * @param {Number} rowIndex The selected index
35923         * @param {Boolean} keepExisting False if other selections will be cleared
35924         */
35925        "beforerowselect" : true,
35926        /**
35927         * @event rowselect
35928         * Fires when a row is selected.
35929         * @param {SelectionModel} this
35930         * @param {Number} rowIndex The selected index
35931         * @param {Roo.data.Record} r The record
35932         */
35933        "rowselect" : true,
35934        /**
35935         * @event rowdeselect
35936         * Fires when a row is deselected.
35937         * @param {SelectionModel} this
35938         * @param {Number} rowIndex The selected index
35939         */
35940         "rowdeselect" : true
35941     });
35942     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35943     this.locked = false;
35944 };
35945
35946 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35947     /**
35948      * @cfg {Boolean} singleSelect
35949      * True to allow selection of only one row at a time (defaults to false)
35950      */
35951     singleSelect : false,
35952
35953     // private
35954     initEvents : function(){
35955
35956         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35957             this.grid.on("mousedown", this.handleMouseDown, this);
35958         }else{ // allow click to work like normal
35959             this.grid.on("rowclick", this.handleDragableRowClick, this);
35960         }
35961         // bootstrap does not have a view..
35962         var view = this.grid.view ? this.grid.view : this.grid;
35963         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35964             "up" : function(e){
35965                 if(!e.shiftKey){
35966                     this.selectPrevious(e.shiftKey);
35967                 }else if(this.last !== false && this.lastActive !== false){
35968                     var last = this.last;
35969                     this.selectRange(this.last,  this.lastActive-1);
35970                     view.focusRow(this.lastActive);
35971                     if(last !== false){
35972                         this.last = last;
35973                     }
35974                 }else{
35975                     this.selectFirstRow();
35976                 }
35977                 this.fireEvent("afterselectionchange", this);
35978             },
35979             "down" : function(e){
35980                 if(!e.shiftKey){
35981                     this.selectNext(e.shiftKey);
35982                 }else if(this.last !== false && this.lastActive !== false){
35983                     var last = this.last;
35984                     this.selectRange(this.last,  this.lastActive+1);
35985                     view.focusRow(this.lastActive);
35986                     if(last !== false){
35987                         this.last = last;
35988                     }
35989                 }else{
35990                     this.selectFirstRow();
35991                 }
35992                 this.fireEvent("afterselectionchange", this);
35993             },
35994             scope: this
35995         });
35996
35997          
35998         view.on("refresh", this.onRefresh, this);
35999         view.on("rowupdated", this.onRowUpdated, this);
36000         view.on("rowremoved", this.onRemove, this);
36001     },
36002
36003     // private
36004     onRefresh : function(){
36005         var ds = this.grid.ds, i, v = this.grid.view;
36006         var s = this.selections;
36007         s.each(function(r){
36008             if((i = ds.indexOfId(r.id)) != -1){
36009                 v.onRowSelect(i);
36010                 s.add(ds.getAt(i)); // updating the selection relate data
36011             }else{
36012                 s.remove(r);
36013             }
36014         });
36015     },
36016
36017     // private
36018     onRemove : function(v, index, r){
36019         this.selections.remove(r);
36020     },
36021
36022     // private
36023     onRowUpdated : function(v, index, r){
36024         if(this.isSelected(r)){
36025             v.onRowSelect(index);
36026         }
36027     },
36028
36029     /**
36030      * Select records.
36031      * @param {Array} records The records to select
36032      * @param {Boolean} keepExisting (optional) True to keep existing selections
36033      */
36034     selectRecords : function(records, keepExisting){
36035         if(!keepExisting){
36036             this.clearSelections();
36037         }
36038         var ds = this.grid.ds;
36039         for(var i = 0, len = records.length; i < len; i++){
36040             this.selectRow(ds.indexOf(records[i]), true);
36041         }
36042     },
36043
36044     /**
36045      * Gets the number of selected rows.
36046      * @return {Number}
36047      */
36048     getCount : function(){
36049         return this.selections.length;
36050     },
36051
36052     /**
36053      * Selects the first row in the grid.
36054      */
36055     selectFirstRow : function(){
36056         this.selectRow(0);
36057     },
36058
36059     /**
36060      * Select the last row.
36061      * @param {Boolean} keepExisting (optional) True to keep existing selections
36062      */
36063     selectLastRow : function(keepExisting){
36064         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36065     },
36066
36067     /**
36068      * Selects the row immediately following the last selected row.
36069      * @param {Boolean} keepExisting (optional) True to keep existing selections
36070      */
36071     selectNext : function(keepExisting){
36072         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36073             this.selectRow(this.last+1, keepExisting);
36074             var view = this.grid.view ? this.grid.view : this.grid;
36075             view.focusRow(this.last);
36076         }
36077     },
36078
36079     /**
36080      * Selects the row that precedes the last selected row.
36081      * @param {Boolean} keepExisting (optional) True to keep existing selections
36082      */
36083     selectPrevious : function(keepExisting){
36084         if(this.last){
36085             this.selectRow(this.last-1, keepExisting);
36086             var view = this.grid.view ? this.grid.view : this.grid;
36087             view.focusRow(this.last);
36088         }
36089     },
36090
36091     /**
36092      * Returns the selected records
36093      * @return {Array} Array of selected records
36094      */
36095     getSelections : function(){
36096         return [].concat(this.selections.items);
36097     },
36098
36099     /**
36100      * Returns the first selected record.
36101      * @return {Record}
36102      */
36103     getSelected : function(){
36104         return this.selections.itemAt(0);
36105     },
36106
36107
36108     /**
36109      * Clears all selections.
36110      */
36111     clearSelections : function(fast){
36112         if(this.locked) {
36113             return;
36114         }
36115         if(fast !== true){
36116             var ds = this.grid.ds;
36117             var s = this.selections;
36118             s.each(function(r){
36119                 this.deselectRow(ds.indexOfId(r.id));
36120             }, this);
36121             s.clear();
36122         }else{
36123             this.selections.clear();
36124         }
36125         this.last = false;
36126     },
36127
36128
36129     /**
36130      * Selects all rows.
36131      */
36132     selectAll : function(){
36133         if(this.locked) {
36134             return;
36135         }
36136         this.selections.clear();
36137         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36138             this.selectRow(i, true);
36139         }
36140     },
36141
36142     /**
36143      * Returns True if there is a selection.
36144      * @return {Boolean}
36145      */
36146     hasSelection : function(){
36147         return this.selections.length > 0;
36148     },
36149
36150     /**
36151      * Returns True if the specified row is selected.
36152      * @param {Number/Record} record The record or index of the record to check
36153      * @return {Boolean}
36154      */
36155     isSelected : function(index){
36156         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36157         return (r && this.selections.key(r.id) ? true : false);
36158     },
36159
36160     /**
36161      * Returns True if the specified record id is selected.
36162      * @param {String} id The id of record to check
36163      * @return {Boolean}
36164      */
36165     isIdSelected : function(id){
36166         return (this.selections.key(id) ? true : false);
36167     },
36168
36169     // private
36170     handleMouseDown : function(e, t)
36171     {
36172         var view = this.grid.view ? this.grid.view : this.grid;
36173         var rowIndex;
36174         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36175             return;
36176         };
36177         if(e.shiftKey && this.last !== false){
36178             var last = this.last;
36179             this.selectRange(last, rowIndex, e.ctrlKey);
36180             this.last = last; // reset the last
36181             view.focusRow(rowIndex);
36182         }else{
36183             var isSelected = this.isSelected(rowIndex);
36184             if(e.button !== 0 && isSelected){
36185                 view.focusRow(rowIndex);
36186             }else if(e.ctrlKey && isSelected){
36187                 this.deselectRow(rowIndex);
36188             }else if(!isSelected){
36189                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36190                 view.focusRow(rowIndex);
36191             }
36192         }
36193         this.fireEvent("afterselectionchange", this);
36194     },
36195     // private
36196     handleDragableRowClick :  function(grid, rowIndex, e) 
36197     {
36198         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36199             this.selectRow(rowIndex, false);
36200             var view = this.grid.view ? this.grid.view : this.grid;
36201             view.focusRow(rowIndex);
36202              this.fireEvent("afterselectionchange", this);
36203         }
36204     },
36205     
36206     /**
36207      * Selects multiple rows.
36208      * @param {Array} rows Array of the indexes of the row to select
36209      * @param {Boolean} keepExisting (optional) True to keep existing selections
36210      */
36211     selectRows : function(rows, keepExisting){
36212         if(!keepExisting){
36213             this.clearSelections();
36214         }
36215         for(var i = 0, len = rows.length; i < len; i++){
36216             this.selectRow(rows[i], true);
36217         }
36218     },
36219
36220     /**
36221      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36222      * @param {Number} startRow The index of the first row in the range
36223      * @param {Number} endRow The index of the last row in the range
36224      * @param {Boolean} keepExisting (optional) True to retain existing selections
36225      */
36226     selectRange : function(startRow, endRow, keepExisting){
36227         if(this.locked) {
36228             return;
36229         }
36230         if(!keepExisting){
36231             this.clearSelections();
36232         }
36233         if(startRow <= endRow){
36234             for(var i = startRow; i <= endRow; i++){
36235                 this.selectRow(i, true);
36236             }
36237         }else{
36238             for(var i = startRow; i >= endRow; i--){
36239                 this.selectRow(i, true);
36240             }
36241         }
36242     },
36243
36244     /**
36245      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36246      * @param {Number} startRow The index of the first row in the range
36247      * @param {Number} endRow The index of the last row in the range
36248      */
36249     deselectRange : function(startRow, endRow, preventViewNotify){
36250         if(this.locked) {
36251             return;
36252         }
36253         for(var i = startRow; i <= endRow; i++){
36254             this.deselectRow(i, preventViewNotify);
36255         }
36256     },
36257
36258     /**
36259      * Selects a row.
36260      * @param {Number} row The index of the row to select
36261      * @param {Boolean} keepExisting (optional) True to keep existing selections
36262      */
36263     selectRow : function(index, keepExisting, preventViewNotify){
36264         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36265             return;
36266         }
36267         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36268             if(!keepExisting || this.singleSelect){
36269                 this.clearSelections();
36270             }
36271             var r = this.grid.ds.getAt(index);
36272             this.selections.add(r);
36273             this.last = this.lastActive = index;
36274             if(!preventViewNotify){
36275                 var view = this.grid.view ? this.grid.view : this.grid;
36276                 view.onRowSelect(index);
36277             }
36278             this.fireEvent("rowselect", this, index, r);
36279             this.fireEvent("selectionchange", this);
36280         }
36281     },
36282
36283     /**
36284      * Deselects a row.
36285      * @param {Number} row The index of the row to deselect
36286      */
36287     deselectRow : function(index, preventViewNotify){
36288         if(this.locked) {
36289             return;
36290         }
36291         if(this.last == index){
36292             this.last = false;
36293         }
36294         if(this.lastActive == index){
36295             this.lastActive = false;
36296         }
36297         var r = this.grid.ds.getAt(index);
36298         this.selections.remove(r);
36299         if(!preventViewNotify){
36300             var view = this.grid.view ? this.grid.view : this.grid;
36301             view.onRowDeselect(index);
36302         }
36303         this.fireEvent("rowdeselect", this, index);
36304         this.fireEvent("selectionchange", this);
36305     },
36306
36307     // private
36308     restoreLast : function(){
36309         if(this._last){
36310             this.last = this._last;
36311         }
36312     },
36313
36314     // private
36315     acceptsNav : function(row, col, cm){
36316         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36317     },
36318
36319     // private
36320     onEditorKey : function(field, e){
36321         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36322         if(k == e.TAB){
36323             e.stopEvent();
36324             ed.completeEdit();
36325             if(e.shiftKey){
36326                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36327             }else{
36328                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36329             }
36330         }else if(k == e.ENTER && !e.ctrlKey){
36331             e.stopEvent();
36332             ed.completeEdit();
36333             if(e.shiftKey){
36334                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36335             }else{
36336                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36337             }
36338         }else if(k == e.ESC){
36339             ed.cancelEdit();
36340         }
36341         if(newCell){
36342             g.startEditing(newCell[0], newCell[1]);
36343         }
36344     }
36345 });/*
36346  * Based on:
36347  * Ext JS Library 1.1.1
36348  * Copyright(c) 2006-2007, Ext JS, LLC.
36349  *
36350  * Originally Released Under LGPL - original licence link has changed is not relivant.
36351  *
36352  * Fork - LGPL
36353  * <script type="text/javascript">
36354  */
36355 /**
36356  * @class Roo.grid.CellSelectionModel
36357  * @extends Roo.grid.AbstractSelectionModel
36358  * This class provides the basic implementation for cell selection in a grid.
36359  * @constructor
36360  * @param {Object} config The object containing the configuration of this model.
36361  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36362  */
36363 Roo.grid.CellSelectionModel = function(config){
36364     Roo.apply(this, config);
36365
36366     this.selection = null;
36367
36368     this.addEvents({
36369         /**
36370              * @event beforerowselect
36371              * Fires before a cell is selected.
36372              * @param {SelectionModel} this
36373              * @param {Number} rowIndex The selected row index
36374              * @param {Number} colIndex The selected cell index
36375              */
36376             "beforecellselect" : true,
36377         /**
36378              * @event cellselect
36379              * Fires when 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             "cellselect" : true,
36385         /**
36386              * @event selectionchange
36387              * Fires when the active selection changes.
36388              * @param {SelectionModel} this
36389              * @param {Object} selection null for no selection or an object (o) with two properties
36390                 <ul>
36391                 <li>o.record: the record object for the row the selection is in</li>
36392                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36393                 </ul>
36394              */
36395             "selectionchange" : true,
36396         /**
36397              * @event tabend
36398              * Fires when the tab (or enter) was pressed on the last editable cell
36399              * You can use this to trigger add new row.
36400              * @param {SelectionModel} this
36401              */
36402             "tabend" : true,
36403          /**
36404              * @event beforeeditnext
36405              * Fires before the next editable sell is made active
36406              * You can use this to skip to another cell or fire the tabend
36407              *    if you set cell to false
36408              * @param {Object} eventdata object : { cell : [ row, col ] } 
36409              */
36410             "beforeeditnext" : true
36411     });
36412     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36413 };
36414
36415 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36416     
36417     enter_is_tab: false,
36418
36419     /** @ignore */
36420     initEvents : function(){
36421         this.grid.on("mousedown", this.handleMouseDown, this);
36422         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36423         var view = this.grid.view;
36424         view.on("refresh", this.onViewChange, this);
36425         view.on("rowupdated", this.onRowUpdated, this);
36426         view.on("beforerowremoved", this.clearSelections, this);
36427         view.on("beforerowsinserted", this.clearSelections, this);
36428         if(this.grid.isEditor){
36429             this.grid.on("beforeedit", this.beforeEdit,  this);
36430         }
36431     },
36432
36433         //private
36434     beforeEdit : function(e){
36435         this.select(e.row, e.column, false, true, e.record);
36436     },
36437
36438         //private
36439     onRowUpdated : function(v, index, r){
36440         if(this.selection && this.selection.record == r){
36441             v.onCellSelect(index, this.selection.cell[1]);
36442         }
36443     },
36444
36445         //private
36446     onViewChange : function(){
36447         this.clearSelections(true);
36448     },
36449
36450         /**
36451          * Returns the currently selected cell,.
36452          * @return {Array} The selected cell (row, column) or null if none selected.
36453          */
36454     getSelectedCell : function(){
36455         return this.selection ? this.selection.cell : null;
36456     },
36457
36458     /**
36459      * Clears all selections.
36460      * @param {Boolean} true to prevent the gridview from being notified about the change.
36461      */
36462     clearSelections : function(preventNotify){
36463         var s = this.selection;
36464         if(s){
36465             if(preventNotify !== true){
36466                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36467             }
36468             this.selection = null;
36469             this.fireEvent("selectionchange", this, null);
36470         }
36471     },
36472
36473     /**
36474      * Returns true if there is a selection.
36475      * @return {Boolean}
36476      */
36477     hasSelection : function(){
36478         return this.selection ? true : false;
36479     },
36480
36481     /** @ignore */
36482     handleMouseDown : function(e, t){
36483         var v = this.grid.getView();
36484         if(this.isLocked()){
36485             return;
36486         };
36487         var row = v.findRowIndex(t);
36488         var cell = v.findCellIndex(t);
36489         if(row !== false && cell !== false){
36490             this.select(row, cell);
36491         }
36492     },
36493
36494     /**
36495      * Selects a cell.
36496      * @param {Number} rowIndex
36497      * @param {Number} collIndex
36498      */
36499     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36500         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36501             this.clearSelections();
36502             r = r || this.grid.dataSource.getAt(rowIndex);
36503             this.selection = {
36504                 record : r,
36505                 cell : [rowIndex, colIndex]
36506             };
36507             if(!preventViewNotify){
36508                 var v = this.grid.getView();
36509                 v.onCellSelect(rowIndex, colIndex);
36510                 if(preventFocus !== true){
36511                     v.focusCell(rowIndex, colIndex);
36512                 }
36513             }
36514             this.fireEvent("cellselect", this, rowIndex, colIndex);
36515             this.fireEvent("selectionchange", this, this.selection);
36516         }
36517     },
36518
36519         //private
36520     isSelectable : function(rowIndex, colIndex, cm){
36521         return !cm.isHidden(colIndex);
36522     },
36523
36524     /** @ignore */
36525     handleKeyDown : function(e){
36526         //Roo.log('Cell Sel Model handleKeyDown');
36527         if(!e.isNavKeyPress()){
36528             return;
36529         }
36530         var g = this.grid, s = this.selection;
36531         if(!s){
36532             e.stopEvent();
36533             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36534             if(cell){
36535                 this.select(cell[0], cell[1]);
36536             }
36537             return;
36538         }
36539         var sm = this;
36540         var walk = function(row, col, step){
36541             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36542         };
36543         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36544         var newCell;
36545
36546       
36547
36548         switch(k){
36549             case e.TAB:
36550                 // handled by onEditorKey
36551                 if (g.isEditor && g.editing) {
36552                     return;
36553                 }
36554                 if(e.shiftKey) {
36555                     newCell = walk(r, c-1, -1);
36556                 } else {
36557                     newCell = walk(r, c+1, 1);
36558                 }
36559                 break;
36560             
36561             case e.DOWN:
36562                newCell = walk(r+1, c, 1);
36563                 break;
36564             
36565             case e.UP:
36566                 newCell = walk(r-1, c, -1);
36567                 break;
36568             
36569             case e.RIGHT:
36570                 newCell = walk(r, c+1, 1);
36571                 break;
36572             
36573             case e.LEFT:
36574                 newCell = walk(r, c-1, -1);
36575                 break;
36576             
36577             case e.ENTER:
36578                 
36579                 if(g.isEditor && !g.editing){
36580                    g.startEditing(r, c);
36581                    e.stopEvent();
36582                    return;
36583                 }
36584                 
36585                 
36586              break;
36587         };
36588         if(newCell){
36589             this.select(newCell[0], newCell[1]);
36590             e.stopEvent();
36591             
36592         }
36593     },
36594
36595     acceptsNav : function(row, col, cm){
36596         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36597     },
36598     /**
36599      * Selects a cell.
36600      * @param {Number} field (not used) - as it's normally used as a listener
36601      * @param {Number} e - event - fake it by using
36602      *
36603      * var e = Roo.EventObjectImpl.prototype;
36604      * e.keyCode = e.TAB
36605      *
36606      * 
36607      */
36608     onEditorKey : function(field, e){
36609         
36610         var k = e.getKey(),
36611             newCell,
36612             g = this.grid,
36613             ed = g.activeEditor,
36614             forward = false;
36615         ///Roo.log('onEditorKey' + k);
36616         
36617         
36618         if (this.enter_is_tab && k == e.ENTER) {
36619             k = e.TAB;
36620         }
36621         
36622         if(k == e.TAB){
36623             if(e.shiftKey){
36624                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36625             }else{
36626                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36627                 forward = true;
36628             }
36629             
36630             e.stopEvent();
36631             
36632         } else if(k == e.ENTER &&  !e.ctrlKey){
36633             ed.completeEdit();
36634             e.stopEvent();
36635             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36636         
36637                 } else if(k == e.ESC){
36638             ed.cancelEdit();
36639         }
36640                 
36641         if (newCell) {
36642             var ecall = { cell : newCell, forward : forward };
36643             this.fireEvent('beforeeditnext', ecall );
36644             newCell = ecall.cell;
36645                         forward = ecall.forward;
36646         }
36647                 
36648         if(newCell){
36649             //Roo.log('next cell after edit');
36650             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36651         } else if (forward) {
36652             // tabbed past last
36653             this.fireEvent.defer(100, this, ['tabend',this]);
36654         }
36655     }
36656 });/*
36657  * Based on:
36658  * Ext JS Library 1.1.1
36659  * Copyright(c) 2006-2007, Ext JS, LLC.
36660  *
36661  * Originally Released Under LGPL - original licence link has changed is not relivant.
36662  *
36663  * Fork - LGPL
36664  * <script type="text/javascript">
36665  */
36666  
36667 /**
36668  * @class Roo.grid.EditorGrid
36669  * @extends Roo.grid.Grid
36670  * Class for creating and editable grid.
36671  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36672  * The container MUST have some type of size defined for the grid to fill. The container will be 
36673  * automatically set to position relative if it isn't already.
36674  * @param {Object} dataSource The data model to bind to
36675  * @param {Object} colModel The column model with info about this grid's columns
36676  */
36677 Roo.grid.EditorGrid = function(container, config){
36678     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36679     this.getGridEl().addClass("xedit-grid");
36680
36681     if(!this.selModel){
36682         this.selModel = new Roo.grid.CellSelectionModel();
36683     }
36684
36685     this.activeEditor = null;
36686
36687         this.addEvents({
36688             /**
36689              * @event beforeedit
36690              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36691              * <ul style="padding:5px;padding-left:16px;">
36692              * <li>grid - This grid</li>
36693              * <li>record - The record being edited</li>
36694              * <li>field - The field name being edited</li>
36695              * <li>value - The value for the field being edited.</li>
36696              * <li>row - The grid row index</li>
36697              * <li>column - The grid column index</li>
36698              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36699              * </ul>
36700              * @param {Object} e An edit event (see above for description)
36701              */
36702             "beforeedit" : true,
36703             /**
36704              * @event afteredit
36705              * Fires after a cell is edited. <br />
36706              * <ul style="padding:5px;padding-left:16px;">
36707              * <li>grid - This grid</li>
36708              * <li>record - The record being edited</li>
36709              * <li>field - The field name being edited</li>
36710              * <li>value - The value being set</li>
36711              * <li>originalValue - The original value for the field, before the edit.</li>
36712              * <li>row - The grid row index</li>
36713              * <li>column - The grid column index</li>
36714              * </ul>
36715              * @param {Object} e An edit event (see above for description)
36716              */
36717             "afteredit" : true,
36718             /**
36719              * @event validateedit
36720              * Fires after a cell is edited, but before the value is set in the record. 
36721          * You can use this to modify the value being set in the field, Return false
36722              * to cancel the change. The edit event object has the following properties <br />
36723              * <ul style="padding:5px;padding-left:16px;">
36724          * <li>editor - This editor</li>
36725              * <li>grid - This grid</li>
36726              * <li>record - The record being edited</li>
36727              * <li>field - The field name being edited</li>
36728              * <li>value - The value being set</li>
36729              * <li>originalValue - The original value for the field, before the edit.</li>
36730              * <li>row - The grid row index</li>
36731              * <li>column - The grid column index</li>
36732              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36733              * </ul>
36734              * @param {Object} e An edit event (see above for description)
36735              */
36736             "validateedit" : true
36737         });
36738     this.on("bodyscroll", this.stopEditing,  this);
36739     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36740 };
36741
36742 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36743     /**
36744      * @cfg {Number} clicksToEdit
36745      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36746      */
36747     clicksToEdit: 2,
36748
36749     // private
36750     isEditor : true,
36751     // private
36752     trackMouseOver: false, // causes very odd FF errors
36753
36754     onCellDblClick : function(g, row, col){
36755         this.startEditing(row, col);
36756     },
36757
36758     onEditComplete : function(ed, value, startValue){
36759         this.editing = false;
36760         this.activeEditor = null;
36761         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36762         var r = ed.record;
36763         var field = this.colModel.getDataIndex(ed.col);
36764         var e = {
36765             grid: this,
36766             record: r,
36767             field: field,
36768             originalValue: startValue,
36769             value: value,
36770             row: ed.row,
36771             column: ed.col,
36772             cancel:false,
36773             editor: ed
36774         };
36775         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36776         cell.show();
36777           
36778         if(String(value) !== String(startValue)){
36779             
36780             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36781                 r.set(field, e.value);
36782                 // if we are dealing with a combo box..
36783                 // then we also set the 'name' colum to be the displayField
36784                 if (ed.field.displayField && ed.field.name) {
36785                     r.set(ed.field.name, ed.field.el.dom.value);
36786                 }
36787                 
36788                 delete e.cancel; //?? why!!!
36789                 this.fireEvent("afteredit", e);
36790             }
36791         } else {
36792             this.fireEvent("afteredit", e); // always fire it!
36793         }
36794         this.view.focusCell(ed.row, ed.col);
36795     },
36796
36797     /**
36798      * Starts editing the specified for the specified row/column
36799      * @param {Number} rowIndex
36800      * @param {Number} colIndex
36801      */
36802     startEditing : function(row, col){
36803         this.stopEditing();
36804         if(this.colModel.isCellEditable(col, row)){
36805             this.view.ensureVisible(row, col, true);
36806           
36807             var r = this.dataSource.getAt(row);
36808             var field = this.colModel.getDataIndex(col);
36809             var cell = Roo.get(this.view.getCell(row,col));
36810             var e = {
36811                 grid: this,
36812                 record: r,
36813                 field: field,
36814                 value: r.data[field],
36815                 row: row,
36816                 column: col,
36817                 cancel:false 
36818             };
36819             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36820                 this.editing = true;
36821                 var ed = this.colModel.getCellEditor(col, row);
36822                 
36823                 if (!ed) {
36824                     return;
36825                 }
36826                 if(!ed.rendered){
36827                     ed.render(ed.parentEl || document.body);
36828                 }
36829                 ed.field.reset();
36830                
36831                 cell.hide();
36832                 
36833                 (function(){ // complex but required for focus issues in safari, ie and opera
36834                     ed.row = row;
36835                     ed.col = col;
36836                     ed.record = r;
36837                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36838                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36839                     this.activeEditor = ed;
36840                     var v = r.data[field];
36841                     ed.startEdit(this.view.getCell(row, col), v);
36842                     // combo's with 'displayField and name set
36843                     if (ed.field.displayField && ed.field.name) {
36844                         ed.field.el.dom.value = r.data[ed.field.name];
36845                     }
36846                     
36847                     
36848                 }).defer(50, this);
36849             }
36850         }
36851     },
36852         
36853     /**
36854      * Stops any active editing
36855      */
36856     stopEditing : function(){
36857         if(this.activeEditor){
36858             this.activeEditor.completeEdit();
36859         }
36860         this.activeEditor = null;
36861     },
36862         
36863          /**
36864      * Called to get grid's drag proxy text, by default returns this.ddText.
36865      * @return {String}
36866      */
36867     getDragDropText : function(){
36868         var count = this.selModel.getSelectedCell() ? 1 : 0;
36869         return String.format(this.ddText, count, count == 1 ? '' : 's');
36870     }
36871         
36872 });/*
36873  * Based on:
36874  * Ext JS Library 1.1.1
36875  * Copyright(c) 2006-2007, Ext JS, LLC.
36876  *
36877  * Originally Released Under LGPL - original licence link has changed is not relivant.
36878  *
36879  * Fork - LGPL
36880  * <script type="text/javascript">
36881  */
36882
36883 // private - not really -- you end up using it !
36884 // This is a support class used internally by the Grid components
36885
36886 /**
36887  * @class Roo.grid.GridEditor
36888  * @extends Roo.Editor
36889  * Class for creating and editable grid elements.
36890  * @param {Object} config any settings (must include field)
36891  */
36892 Roo.grid.GridEditor = function(field, config){
36893     if (!config && field.field) {
36894         config = field;
36895         field = Roo.factory(config.field, Roo.form);
36896     }
36897     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36898     field.monitorTab = false;
36899 };
36900
36901 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36902     
36903     /**
36904      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36905      */
36906     
36907     alignment: "tl-tl",
36908     autoSize: "width",
36909     hideEl : false,
36910     cls: "x-small-editor x-grid-editor",
36911     shim:false,
36912     shadow:"frame"
36913 });/*
36914  * Based on:
36915  * Ext JS Library 1.1.1
36916  * Copyright(c) 2006-2007, Ext JS, LLC.
36917  *
36918  * Originally Released Under LGPL - original licence link has changed is not relivant.
36919  *
36920  * Fork - LGPL
36921  * <script type="text/javascript">
36922  */
36923   
36924
36925   
36926 Roo.grid.PropertyRecord = Roo.data.Record.create([
36927     {name:'name',type:'string'},  'value'
36928 ]);
36929
36930
36931 Roo.grid.PropertyStore = function(grid, source){
36932     this.grid = grid;
36933     this.store = new Roo.data.Store({
36934         recordType : Roo.grid.PropertyRecord
36935     });
36936     this.store.on('update', this.onUpdate,  this);
36937     if(source){
36938         this.setSource(source);
36939     }
36940     Roo.grid.PropertyStore.superclass.constructor.call(this);
36941 };
36942
36943
36944
36945 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36946     setSource : function(o){
36947         this.source = o;
36948         this.store.removeAll();
36949         var data = [];
36950         for(var k in o){
36951             if(this.isEditableValue(o[k])){
36952                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36953             }
36954         }
36955         this.store.loadRecords({records: data}, {}, true);
36956     },
36957
36958     onUpdate : function(ds, record, type){
36959         if(type == Roo.data.Record.EDIT){
36960             var v = record.data['value'];
36961             var oldValue = record.modified['value'];
36962             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36963                 this.source[record.id] = v;
36964                 record.commit();
36965                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36966             }else{
36967                 record.reject();
36968             }
36969         }
36970     },
36971
36972     getProperty : function(row){
36973        return this.store.getAt(row);
36974     },
36975
36976     isEditableValue: function(val){
36977         if(val && val instanceof Date){
36978             return true;
36979         }else if(typeof val == 'object' || typeof val == 'function'){
36980             return false;
36981         }
36982         return true;
36983     },
36984
36985     setValue : function(prop, value){
36986         this.source[prop] = value;
36987         this.store.getById(prop).set('value', value);
36988     },
36989
36990     getSource : function(){
36991         return this.source;
36992     }
36993 });
36994
36995 Roo.grid.PropertyColumnModel = function(grid, store){
36996     this.grid = grid;
36997     var g = Roo.grid;
36998     g.PropertyColumnModel.superclass.constructor.call(this, [
36999         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37000         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37001     ]);
37002     this.store = store;
37003     this.bselect = Roo.DomHelper.append(document.body, {
37004         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37005             {tag: 'option', value: 'true', html: 'true'},
37006             {tag: 'option', value: 'false', html: 'false'}
37007         ]
37008     });
37009     Roo.id(this.bselect);
37010     var f = Roo.form;
37011     this.editors = {
37012         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37013         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37014         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37015         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37016         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37017     };
37018     this.renderCellDelegate = this.renderCell.createDelegate(this);
37019     this.renderPropDelegate = this.renderProp.createDelegate(this);
37020 };
37021
37022 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37023     
37024     
37025     nameText : 'Name',
37026     valueText : 'Value',
37027     
37028     dateFormat : 'm/j/Y',
37029     
37030     
37031     renderDate : function(dateVal){
37032         return dateVal.dateFormat(this.dateFormat);
37033     },
37034
37035     renderBool : function(bVal){
37036         return bVal ? 'true' : 'false';
37037     },
37038
37039     isCellEditable : function(colIndex, rowIndex){
37040         return colIndex == 1;
37041     },
37042
37043     getRenderer : function(col){
37044         return col == 1 ?
37045             this.renderCellDelegate : this.renderPropDelegate;
37046     },
37047
37048     renderProp : function(v){
37049         return this.getPropertyName(v);
37050     },
37051
37052     renderCell : function(val){
37053         var rv = val;
37054         if(val instanceof Date){
37055             rv = this.renderDate(val);
37056         }else if(typeof val == 'boolean'){
37057             rv = this.renderBool(val);
37058         }
37059         return Roo.util.Format.htmlEncode(rv);
37060     },
37061
37062     getPropertyName : function(name){
37063         var pn = this.grid.propertyNames;
37064         return pn && pn[name] ? pn[name] : name;
37065     },
37066
37067     getCellEditor : function(colIndex, rowIndex){
37068         var p = this.store.getProperty(rowIndex);
37069         var n = p.data['name'], val = p.data['value'];
37070         
37071         if(typeof(this.grid.customEditors[n]) == 'string'){
37072             return this.editors[this.grid.customEditors[n]];
37073         }
37074         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37075             return this.grid.customEditors[n];
37076         }
37077         if(val instanceof Date){
37078             return this.editors['date'];
37079         }else if(typeof val == 'number'){
37080             return this.editors['number'];
37081         }else if(typeof val == 'boolean'){
37082             return this.editors['boolean'];
37083         }else{
37084             return this.editors['string'];
37085         }
37086     }
37087 });
37088
37089 /**
37090  * @class Roo.grid.PropertyGrid
37091  * @extends Roo.grid.EditorGrid
37092  * This class represents the  interface of a component based property grid control.
37093  * <br><br>Usage:<pre><code>
37094  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37095       
37096  });
37097  // set any options
37098  grid.render();
37099  * </code></pre>
37100   
37101  * @constructor
37102  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37103  * The container MUST have some type of size defined for the grid to fill. The container will be
37104  * automatically set to position relative if it isn't already.
37105  * @param {Object} config A config object that sets properties on this grid.
37106  */
37107 Roo.grid.PropertyGrid = function(container, config){
37108     config = config || {};
37109     var store = new Roo.grid.PropertyStore(this);
37110     this.store = store;
37111     var cm = new Roo.grid.PropertyColumnModel(this, store);
37112     store.store.sort('name', 'ASC');
37113     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37114         ds: store.store,
37115         cm: cm,
37116         enableColLock:false,
37117         enableColumnMove:false,
37118         stripeRows:false,
37119         trackMouseOver: false,
37120         clicksToEdit:1
37121     }, config));
37122     this.getGridEl().addClass('x-props-grid');
37123     this.lastEditRow = null;
37124     this.on('columnresize', this.onColumnResize, this);
37125     this.addEvents({
37126          /**
37127              * @event beforepropertychange
37128              * Fires before a property changes (return false to stop?)
37129              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37130              * @param {String} id Record Id
37131              * @param {String} newval New Value
37132          * @param {String} oldval Old Value
37133              */
37134         "beforepropertychange": true,
37135         /**
37136              * @event propertychange
37137              * Fires after a property changes
37138              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37139              * @param {String} id Record Id
37140              * @param {String} newval New Value
37141          * @param {String} oldval Old Value
37142              */
37143         "propertychange": true
37144     });
37145     this.customEditors = this.customEditors || {};
37146 };
37147 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37148     
37149      /**
37150      * @cfg {Object} customEditors map of colnames=> custom editors.
37151      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37152      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37153      * false disables editing of the field.
37154          */
37155     
37156       /**
37157      * @cfg {Object} propertyNames map of property Names to their displayed value
37158          */
37159     
37160     render : function(){
37161         Roo.grid.PropertyGrid.superclass.render.call(this);
37162         this.autoSize.defer(100, this);
37163     },
37164
37165     autoSize : function(){
37166         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37167         if(this.view){
37168             this.view.fitColumns();
37169         }
37170     },
37171
37172     onColumnResize : function(){
37173         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37174         this.autoSize();
37175     },
37176     /**
37177      * Sets the data for the Grid
37178      * accepts a Key => Value object of all the elements avaiable.
37179      * @param {Object} data  to appear in grid.
37180      */
37181     setSource : function(source){
37182         this.store.setSource(source);
37183         //this.autoSize();
37184     },
37185     /**
37186      * Gets all the data from the grid.
37187      * @return {Object} data  data stored in grid
37188      */
37189     getSource : function(){
37190         return this.store.getSource();
37191     }
37192 });/*
37193   
37194  * Licence LGPL
37195  
37196  */
37197  
37198 /**
37199  * @class Roo.grid.Calendar
37200  * @extends Roo.util.Grid
37201  * This class extends the Grid to provide a calendar widget
37202  * <br><br>Usage:<pre><code>
37203  var grid = new Roo.grid.Calendar("my-container-id", {
37204      ds: myDataStore,
37205      cm: myColModel,
37206      selModel: mySelectionModel,
37207      autoSizeColumns: true,
37208      monitorWindowResize: false,
37209      trackMouseOver: true
37210      eventstore : real data store..
37211  });
37212  // set any options
37213  grid.render();
37214   
37215   * @constructor
37216  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37217  * The container MUST have some type of size defined for the grid to fill. The container will be
37218  * automatically set to position relative if it isn't already.
37219  * @param {Object} config A config object that sets properties on this grid.
37220  */
37221 Roo.grid.Calendar = function(container, config){
37222         // initialize the container
37223         this.container = Roo.get(container);
37224         this.container.update("");
37225         this.container.setStyle("overflow", "hidden");
37226     this.container.addClass('x-grid-container');
37227
37228     this.id = this.container.id;
37229
37230     Roo.apply(this, config);
37231     // check and correct shorthanded configs
37232     
37233     var rows = [];
37234     var d =1;
37235     for (var r = 0;r < 6;r++) {
37236         
37237         rows[r]=[];
37238         for (var c =0;c < 7;c++) {
37239             rows[r][c]= '';
37240         }
37241     }
37242     if (this.eventStore) {
37243         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37244         this.eventStore.on('load',this.onLoad, this);
37245         this.eventStore.on('beforeload',this.clearEvents, this);
37246          
37247     }
37248     
37249     this.dataSource = new Roo.data.Store({
37250             proxy: new Roo.data.MemoryProxy(rows),
37251             reader: new Roo.data.ArrayReader({}, [
37252                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37253     });
37254
37255     this.dataSource.load();
37256     this.ds = this.dataSource;
37257     this.ds.xmodule = this.xmodule || false;
37258     
37259     
37260     var cellRender = function(v,x,r)
37261     {
37262         return String.format(
37263             '<div class="fc-day  fc-widget-content"><div>' +
37264                 '<div class="fc-event-container"></div>' +
37265                 '<div class="fc-day-number">{0}</div>'+
37266                 
37267                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37268             '</div></div>', v);
37269     
37270     }
37271     
37272     
37273     this.colModel = new Roo.grid.ColumnModel( [
37274         {
37275             xtype: 'ColumnModel',
37276             xns: Roo.grid,
37277             dataIndex : 'weekday0',
37278             header : 'Sunday',
37279             renderer : cellRender
37280         },
37281         {
37282             xtype: 'ColumnModel',
37283             xns: Roo.grid,
37284             dataIndex : 'weekday1',
37285             header : 'Monday',
37286             renderer : cellRender
37287         },
37288         {
37289             xtype: 'ColumnModel',
37290             xns: Roo.grid,
37291             dataIndex : 'weekday2',
37292             header : 'Tuesday',
37293             renderer : cellRender
37294         },
37295         {
37296             xtype: 'ColumnModel',
37297             xns: Roo.grid,
37298             dataIndex : 'weekday3',
37299             header : 'Wednesday',
37300             renderer : cellRender
37301         },
37302         {
37303             xtype: 'ColumnModel',
37304             xns: Roo.grid,
37305             dataIndex : 'weekday4',
37306             header : 'Thursday',
37307             renderer : cellRender
37308         },
37309         {
37310             xtype: 'ColumnModel',
37311             xns: Roo.grid,
37312             dataIndex : 'weekday5',
37313             header : 'Friday',
37314             renderer : cellRender
37315         },
37316         {
37317             xtype: 'ColumnModel',
37318             xns: Roo.grid,
37319             dataIndex : 'weekday6',
37320             header : 'Saturday',
37321             renderer : cellRender
37322         }
37323     ]);
37324     this.cm = this.colModel;
37325     this.cm.xmodule = this.xmodule || false;
37326  
37327         
37328           
37329     //this.selModel = new Roo.grid.CellSelectionModel();
37330     //this.sm = this.selModel;
37331     //this.selModel.init(this);
37332     
37333     
37334     if(this.width){
37335         this.container.setWidth(this.width);
37336     }
37337
37338     if(this.height){
37339         this.container.setHeight(this.height);
37340     }
37341     /** @private */
37342         this.addEvents({
37343         // raw events
37344         /**
37345          * @event click
37346          * The raw click event for the entire grid.
37347          * @param {Roo.EventObject} e
37348          */
37349         "click" : true,
37350         /**
37351          * @event dblclick
37352          * The raw dblclick event for the entire grid.
37353          * @param {Roo.EventObject} e
37354          */
37355         "dblclick" : true,
37356         /**
37357          * @event contextmenu
37358          * The raw contextmenu event for the entire grid.
37359          * @param {Roo.EventObject} e
37360          */
37361         "contextmenu" : true,
37362         /**
37363          * @event mousedown
37364          * The raw mousedown event for the entire grid.
37365          * @param {Roo.EventObject} e
37366          */
37367         "mousedown" : true,
37368         /**
37369          * @event mouseup
37370          * The raw mouseup event for the entire grid.
37371          * @param {Roo.EventObject} e
37372          */
37373         "mouseup" : true,
37374         /**
37375          * @event mouseover
37376          * The raw mouseover event for the entire grid.
37377          * @param {Roo.EventObject} e
37378          */
37379         "mouseover" : true,
37380         /**
37381          * @event mouseout
37382          * The raw mouseout event for the entire grid.
37383          * @param {Roo.EventObject} e
37384          */
37385         "mouseout" : true,
37386         /**
37387          * @event keypress
37388          * The raw keypress event for the entire grid.
37389          * @param {Roo.EventObject} e
37390          */
37391         "keypress" : true,
37392         /**
37393          * @event keydown
37394          * The raw keydown event for the entire grid.
37395          * @param {Roo.EventObject} e
37396          */
37397         "keydown" : true,
37398
37399         // custom events
37400
37401         /**
37402          * @event cellclick
37403          * Fires when a cell is clicked
37404          * @param {Grid} this
37405          * @param {Number} rowIndex
37406          * @param {Number} columnIndex
37407          * @param {Roo.EventObject} e
37408          */
37409         "cellclick" : true,
37410         /**
37411          * @event celldblclick
37412          * Fires when a cell is double clicked
37413          * @param {Grid} this
37414          * @param {Number} rowIndex
37415          * @param {Number} columnIndex
37416          * @param {Roo.EventObject} e
37417          */
37418         "celldblclick" : true,
37419         /**
37420          * @event rowclick
37421          * Fires when a row is clicked
37422          * @param {Grid} this
37423          * @param {Number} rowIndex
37424          * @param {Roo.EventObject} e
37425          */
37426         "rowclick" : true,
37427         /**
37428          * @event rowdblclick
37429          * Fires when a row is double clicked
37430          * @param {Grid} this
37431          * @param {Number} rowIndex
37432          * @param {Roo.EventObject} e
37433          */
37434         "rowdblclick" : true,
37435         /**
37436          * @event headerclick
37437          * Fires when a header is clicked
37438          * @param {Grid} this
37439          * @param {Number} columnIndex
37440          * @param {Roo.EventObject} e
37441          */
37442         "headerclick" : true,
37443         /**
37444          * @event headerdblclick
37445          * Fires when a header cell is double clicked
37446          * @param {Grid} this
37447          * @param {Number} columnIndex
37448          * @param {Roo.EventObject} e
37449          */
37450         "headerdblclick" : true,
37451         /**
37452          * @event rowcontextmenu
37453          * Fires when a row is right clicked
37454          * @param {Grid} this
37455          * @param {Number} rowIndex
37456          * @param {Roo.EventObject} e
37457          */
37458         "rowcontextmenu" : true,
37459         /**
37460          * @event cellcontextmenu
37461          * Fires when a cell is right clicked
37462          * @param {Grid} this
37463          * @param {Number} rowIndex
37464          * @param {Number} cellIndex
37465          * @param {Roo.EventObject} e
37466          */
37467          "cellcontextmenu" : true,
37468         /**
37469          * @event headercontextmenu
37470          * Fires when a header is right clicked
37471          * @param {Grid} this
37472          * @param {Number} columnIndex
37473          * @param {Roo.EventObject} e
37474          */
37475         "headercontextmenu" : true,
37476         /**
37477          * @event bodyscroll
37478          * Fires when the body element is scrolled
37479          * @param {Number} scrollLeft
37480          * @param {Number} scrollTop
37481          */
37482         "bodyscroll" : true,
37483         /**
37484          * @event columnresize
37485          * Fires when the user resizes a column
37486          * @param {Number} columnIndex
37487          * @param {Number} newSize
37488          */
37489         "columnresize" : true,
37490         /**
37491          * @event columnmove
37492          * Fires when the user moves a column
37493          * @param {Number} oldIndex
37494          * @param {Number} newIndex
37495          */
37496         "columnmove" : true,
37497         /**
37498          * @event startdrag
37499          * Fires when row(s) start being dragged
37500          * @param {Grid} this
37501          * @param {Roo.GridDD} dd The drag drop object
37502          * @param {event} e The raw browser event
37503          */
37504         "startdrag" : true,
37505         /**
37506          * @event enddrag
37507          * Fires when a drag operation is complete
37508          * @param {Grid} this
37509          * @param {Roo.GridDD} dd The drag drop object
37510          * @param {event} e The raw browser event
37511          */
37512         "enddrag" : true,
37513         /**
37514          * @event dragdrop
37515          * Fires when dragged row(s) are dropped on a valid DD target
37516          * @param {Grid} this
37517          * @param {Roo.GridDD} dd The drag drop object
37518          * @param {String} targetId The target drag drop object
37519          * @param {event} e The raw browser event
37520          */
37521         "dragdrop" : true,
37522         /**
37523          * @event dragover
37524          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37525          * @param {Grid} this
37526          * @param {Roo.GridDD} dd The drag drop object
37527          * @param {String} targetId The target drag drop object
37528          * @param {event} e The raw browser event
37529          */
37530         "dragover" : true,
37531         /**
37532          * @event dragenter
37533          *  Fires when the dragged row(s) first cross another DD target while being dragged
37534          * @param {Grid} this
37535          * @param {Roo.GridDD} dd The drag drop object
37536          * @param {String} targetId The target drag drop object
37537          * @param {event} e The raw browser event
37538          */
37539         "dragenter" : true,
37540         /**
37541          * @event dragout
37542          * Fires when the dragged row(s) leave another DD target while being dragged
37543          * @param {Grid} this
37544          * @param {Roo.GridDD} dd The drag drop object
37545          * @param {String} targetId The target drag drop object
37546          * @param {event} e The raw browser event
37547          */
37548         "dragout" : true,
37549         /**
37550          * @event rowclass
37551          * Fires when a row is rendered, so you can change add a style to it.
37552          * @param {GridView} gridview   The grid view
37553          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37554          */
37555         'rowclass' : true,
37556
37557         /**
37558          * @event render
37559          * Fires when the grid is rendered
37560          * @param {Grid} grid
37561          */
37562         'render' : true,
37563             /**
37564              * @event select
37565              * Fires when a date is selected
37566              * @param {DatePicker} this
37567              * @param {Date} date The selected date
37568              */
37569         'select': true,
37570         /**
37571              * @event monthchange
37572              * Fires when the displayed month changes 
37573              * @param {DatePicker} this
37574              * @param {Date} date The selected month
37575              */
37576         'monthchange': true,
37577         /**
37578              * @event evententer
37579              * Fires when mouse over an event
37580              * @param {Calendar} this
37581              * @param {event} Event
37582              */
37583         'evententer': true,
37584         /**
37585              * @event eventleave
37586              * Fires when the mouse leaves an
37587              * @param {Calendar} this
37588              * @param {event}
37589              */
37590         'eventleave': true,
37591         /**
37592              * @event eventclick
37593              * Fires when the mouse click an
37594              * @param {Calendar} this
37595              * @param {event}
37596              */
37597         'eventclick': true,
37598         /**
37599              * @event eventrender
37600              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37601              * @param {Calendar} this
37602              * @param {data} data to be modified
37603              */
37604         'eventrender': true
37605         
37606     });
37607
37608     Roo.grid.Grid.superclass.constructor.call(this);
37609     this.on('render', function() {
37610         this.view.el.addClass('x-grid-cal'); 
37611         
37612         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37613
37614     },this);
37615     
37616     if (!Roo.grid.Calendar.style) {
37617         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37618             
37619             
37620             '.x-grid-cal .x-grid-col' :  {
37621                 height: 'auto !important',
37622                 'vertical-align': 'top'
37623             },
37624             '.x-grid-cal  .fc-event-hori' : {
37625                 height: '14px'
37626             }
37627              
37628             
37629         }, Roo.id());
37630     }
37631
37632     
37633     
37634 };
37635 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37636     /**
37637      * @cfg {Store} eventStore The store that loads events.
37638      */
37639     eventStore : 25,
37640
37641      
37642     activeDate : false,
37643     startDay : 0,
37644     autoWidth : true,
37645     monitorWindowResize : false,
37646
37647     
37648     resizeColumns : function() {
37649         var col = (this.view.el.getWidth() / 7) - 3;
37650         // loop through cols, and setWidth
37651         for(var i =0 ; i < 7 ; i++){
37652             this.cm.setColumnWidth(i, col);
37653         }
37654     },
37655      setDate :function(date) {
37656         
37657         Roo.log('setDate?');
37658         
37659         this.resizeColumns();
37660         var vd = this.activeDate;
37661         this.activeDate = date;
37662 //        if(vd && this.el){
37663 //            var t = date.getTime();
37664 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37665 //                Roo.log('using add remove');
37666 //                
37667 //                this.fireEvent('monthchange', this, date);
37668 //                
37669 //                this.cells.removeClass("fc-state-highlight");
37670 //                this.cells.each(function(c){
37671 //                   if(c.dateValue == t){
37672 //                       c.addClass("fc-state-highlight");
37673 //                       setTimeout(function(){
37674 //                            try{c.dom.firstChild.focus();}catch(e){}
37675 //                       }, 50);
37676 //                       return false;
37677 //                   }
37678 //                   return true;
37679 //                });
37680 //                return;
37681 //            }
37682 //        }
37683         
37684         var days = date.getDaysInMonth();
37685         
37686         var firstOfMonth = date.getFirstDateOfMonth();
37687         var startingPos = firstOfMonth.getDay()-this.startDay;
37688         
37689         if(startingPos < this.startDay){
37690             startingPos += 7;
37691         }
37692         
37693         var pm = date.add(Date.MONTH, -1);
37694         var prevStart = pm.getDaysInMonth()-startingPos;
37695 //        
37696         
37697         
37698         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37699         
37700         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37701         //this.cells.addClassOnOver('fc-state-hover');
37702         
37703         var cells = this.cells.elements;
37704         var textEls = this.textNodes;
37705         
37706         //Roo.each(cells, function(cell){
37707         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37708         //});
37709         
37710         days += startingPos;
37711
37712         // convert everything to numbers so it's fast
37713         var day = 86400000;
37714         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37715         //Roo.log(d);
37716         //Roo.log(pm);
37717         //Roo.log(prevStart);
37718         
37719         var today = new Date().clearTime().getTime();
37720         var sel = date.clearTime().getTime();
37721         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37722         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37723         var ddMatch = this.disabledDatesRE;
37724         var ddText = this.disabledDatesText;
37725         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37726         var ddaysText = this.disabledDaysText;
37727         var format = this.format;
37728         
37729         var setCellClass = function(cal, cell){
37730             
37731             //Roo.log('set Cell Class');
37732             cell.title = "";
37733             var t = d.getTime();
37734             
37735             //Roo.log(d);
37736             
37737             
37738             cell.dateValue = t;
37739             if(t == today){
37740                 cell.className += " fc-today";
37741                 cell.className += " fc-state-highlight";
37742                 cell.title = cal.todayText;
37743             }
37744             if(t == sel){
37745                 // disable highlight in other month..
37746                 cell.className += " fc-state-highlight";
37747                 
37748             }
37749             // disabling
37750             if(t < min) {
37751                 //cell.className = " fc-state-disabled";
37752                 cell.title = cal.minText;
37753                 return;
37754             }
37755             if(t > max) {
37756                 //cell.className = " fc-state-disabled";
37757                 cell.title = cal.maxText;
37758                 return;
37759             }
37760             if(ddays){
37761                 if(ddays.indexOf(d.getDay()) != -1){
37762                     // cell.title = ddaysText;
37763                    // cell.className = " fc-state-disabled";
37764                 }
37765             }
37766             if(ddMatch && format){
37767                 var fvalue = d.dateFormat(format);
37768                 if(ddMatch.test(fvalue)){
37769                     cell.title = ddText.replace("%0", fvalue);
37770                    cell.className = " fc-state-disabled";
37771                 }
37772             }
37773             
37774             if (!cell.initialClassName) {
37775                 cell.initialClassName = cell.dom.className;
37776             }
37777             
37778             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37779         };
37780
37781         var i = 0;
37782         
37783         for(; i < startingPos; i++) {
37784             cells[i].dayName =  (++prevStart);
37785             Roo.log(textEls[i]);
37786             d.setDate(d.getDate()+1);
37787             
37788             //cells[i].className = "fc-past fc-other-month";
37789             setCellClass(this, cells[i]);
37790         }
37791         
37792         var intDay = 0;
37793         
37794         for(; i < days; i++){
37795             intDay = i - startingPos + 1;
37796             cells[i].dayName =  (intDay);
37797             d.setDate(d.getDate()+1);
37798             
37799             cells[i].className = ''; // "x-date-active";
37800             setCellClass(this, cells[i]);
37801         }
37802         var extraDays = 0;
37803         
37804         for(; i < 42; i++) {
37805             //textEls[i].innerHTML = (++extraDays);
37806             
37807             d.setDate(d.getDate()+1);
37808             cells[i].dayName = (++extraDays);
37809             cells[i].className = "fc-future fc-other-month";
37810             setCellClass(this, cells[i]);
37811         }
37812         
37813         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37814         
37815         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37816         
37817         // this will cause all the cells to mis
37818         var rows= [];
37819         var i =0;
37820         for (var r = 0;r < 6;r++) {
37821             for (var c =0;c < 7;c++) {
37822                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37823             }    
37824         }
37825         
37826         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37827         for(i=0;i<cells.length;i++) {
37828             
37829             this.cells.elements[i].dayName = cells[i].dayName ;
37830             this.cells.elements[i].className = cells[i].className;
37831             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37832             this.cells.elements[i].title = cells[i].title ;
37833             this.cells.elements[i].dateValue = cells[i].dateValue ;
37834         }
37835         
37836         
37837         
37838         
37839         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37840         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37841         
37842         ////if(totalRows != 6){
37843             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37844            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37845        // }
37846         
37847         this.fireEvent('monthchange', this, date);
37848         
37849         
37850     },
37851  /**
37852      * Returns the grid's SelectionModel.
37853      * @return {SelectionModel}
37854      */
37855     getSelectionModel : function(){
37856         if(!this.selModel){
37857             this.selModel = new Roo.grid.CellSelectionModel();
37858         }
37859         return this.selModel;
37860     },
37861
37862     load: function() {
37863         this.eventStore.load()
37864         
37865         
37866         
37867     },
37868     
37869     findCell : function(dt) {
37870         dt = dt.clearTime().getTime();
37871         var ret = false;
37872         this.cells.each(function(c){
37873             //Roo.log("check " +c.dateValue + '?=' + dt);
37874             if(c.dateValue == dt){
37875                 ret = c;
37876                 return false;
37877             }
37878             return true;
37879         });
37880         
37881         return ret;
37882     },
37883     
37884     findCells : function(rec) {
37885         var s = rec.data.start_dt.clone().clearTime().getTime();
37886        // Roo.log(s);
37887         var e= rec.data.end_dt.clone().clearTime().getTime();
37888        // Roo.log(e);
37889         var ret = [];
37890         this.cells.each(function(c){
37891              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37892             
37893             if(c.dateValue > e){
37894                 return ;
37895             }
37896             if(c.dateValue < s){
37897                 return ;
37898             }
37899             ret.push(c);
37900         });
37901         
37902         return ret;    
37903     },
37904     
37905     findBestRow: function(cells)
37906     {
37907         var ret = 0;
37908         
37909         for (var i =0 ; i < cells.length;i++) {
37910             ret  = Math.max(cells[i].rows || 0,ret);
37911         }
37912         return ret;
37913         
37914     },
37915     
37916     
37917     addItem : function(rec)
37918     {
37919         // look for vertical location slot in
37920         var cells = this.findCells(rec);
37921         
37922         rec.row = this.findBestRow(cells);
37923         
37924         // work out the location.
37925         
37926         var crow = false;
37927         var rows = [];
37928         for(var i =0; i < cells.length; i++) {
37929             if (!crow) {
37930                 crow = {
37931                     start : cells[i],
37932                     end :  cells[i]
37933                 };
37934                 continue;
37935             }
37936             if (crow.start.getY() == cells[i].getY()) {
37937                 // on same row.
37938                 crow.end = cells[i];
37939                 continue;
37940             }
37941             // different row.
37942             rows.push(crow);
37943             crow = {
37944                 start: cells[i],
37945                 end : cells[i]
37946             };
37947             
37948         }
37949         
37950         rows.push(crow);
37951         rec.els = [];
37952         rec.rows = rows;
37953         rec.cells = cells;
37954         for (var i = 0; i < cells.length;i++) {
37955             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37956             
37957         }
37958         
37959         
37960     },
37961     
37962     clearEvents: function() {
37963         
37964         if (!this.eventStore.getCount()) {
37965             return;
37966         }
37967         // reset number of rows in cells.
37968         Roo.each(this.cells.elements, function(c){
37969             c.rows = 0;
37970         });
37971         
37972         this.eventStore.each(function(e) {
37973             this.clearEvent(e);
37974         },this);
37975         
37976     },
37977     
37978     clearEvent : function(ev)
37979     {
37980         if (ev.els) {
37981             Roo.each(ev.els, function(el) {
37982                 el.un('mouseenter' ,this.onEventEnter, this);
37983                 el.un('mouseleave' ,this.onEventLeave, this);
37984                 el.remove();
37985             },this);
37986             ev.els = [];
37987         }
37988     },
37989     
37990     
37991     renderEvent : function(ev,ctr) {
37992         if (!ctr) {
37993              ctr = this.view.el.select('.fc-event-container',true).first();
37994         }
37995         
37996          
37997         this.clearEvent(ev);
37998             //code
37999        
38000         
38001         
38002         ev.els = [];
38003         var cells = ev.cells;
38004         var rows = ev.rows;
38005         this.fireEvent('eventrender', this, ev);
38006         
38007         for(var i =0; i < rows.length; i++) {
38008             
38009             cls = '';
38010             if (i == 0) {
38011                 cls += ' fc-event-start';
38012             }
38013             if ((i+1) == rows.length) {
38014                 cls += ' fc-event-end';
38015             }
38016             
38017             //Roo.log(ev.data);
38018             // how many rows should it span..
38019             var cg = this.eventTmpl.append(ctr,Roo.apply({
38020                 fccls : cls
38021                 
38022             }, ev.data) , true);
38023             
38024             
38025             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38026             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38027             cg.on('click', this.onEventClick, this, ev);
38028             
38029             ev.els.push(cg);
38030             
38031             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38032             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38033             //Roo.log(cg);
38034              
38035             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38036             cg.setWidth(ebox.right - sbox.x -2);
38037         }
38038     },
38039     
38040     renderEvents: function()
38041     {   
38042         // first make sure there is enough space..
38043         
38044         if (!this.eventTmpl) {
38045             this.eventTmpl = new Roo.Template(
38046                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38047                     '<div class="fc-event-inner">' +
38048                         '<span class="fc-event-time">{time}</span>' +
38049                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38050                     '</div>' +
38051                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38052                 '</div>'
38053             );
38054                 
38055         }
38056                
38057         
38058         
38059         this.cells.each(function(c) {
38060             //Roo.log(c.select('.fc-day-content div',true).first());
38061             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38062         });
38063         
38064         var ctr = this.view.el.select('.fc-event-container',true).first();
38065         
38066         var cls;
38067         this.eventStore.each(function(ev){
38068             
38069             this.renderEvent(ev);
38070              
38071              
38072         }, this);
38073         this.view.layout();
38074         
38075     },
38076     
38077     onEventEnter: function (e, el,event,d) {
38078         this.fireEvent('evententer', this, el, event);
38079     },
38080     
38081     onEventLeave: function (e, el,event,d) {
38082         this.fireEvent('eventleave', this, el, event);
38083     },
38084     
38085     onEventClick: function (e, el,event,d) {
38086         this.fireEvent('eventclick', this, el, event);
38087     },
38088     
38089     onMonthChange: function () {
38090         this.store.load();
38091     },
38092     
38093     onLoad: function () {
38094         
38095         //Roo.log('calendar onload');
38096 //         
38097         if(this.eventStore.getCount() > 0){
38098             
38099            
38100             
38101             this.eventStore.each(function(d){
38102                 
38103                 
38104                 // FIXME..
38105                 var add =   d.data;
38106                 if (typeof(add.end_dt) == 'undefined')  {
38107                     Roo.log("Missing End time in calendar data: ");
38108                     Roo.log(d);
38109                     return;
38110                 }
38111                 if (typeof(add.start_dt) == 'undefined')  {
38112                     Roo.log("Missing Start time in calendar data: ");
38113                     Roo.log(d);
38114                     return;
38115                 }
38116                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38117                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38118                 add.id = add.id || d.id;
38119                 add.title = add.title || '??';
38120                 
38121                 this.addItem(d);
38122                 
38123              
38124             },this);
38125         }
38126         
38127         this.renderEvents();
38128     }
38129     
38130
38131 });
38132 /*
38133  grid : {
38134                 xtype: 'Grid',
38135                 xns: Roo.grid,
38136                 listeners : {
38137                     render : function ()
38138                     {
38139                         _this.grid = this;
38140                         
38141                         if (!this.view.el.hasClass('course-timesheet')) {
38142                             this.view.el.addClass('course-timesheet');
38143                         }
38144                         if (this.tsStyle) {
38145                             this.ds.load({});
38146                             return; 
38147                         }
38148                         Roo.log('width');
38149                         Roo.log(_this.grid.view.el.getWidth());
38150                         
38151                         
38152                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38153                             '.course-timesheet .x-grid-row' : {
38154                                 height: '80px'
38155                             },
38156                             '.x-grid-row td' : {
38157                                 'vertical-align' : 0
38158                             },
38159                             '.course-edit-link' : {
38160                                 'color' : 'blue',
38161                                 'text-overflow' : 'ellipsis',
38162                                 'overflow' : 'hidden',
38163                                 'white-space' : 'nowrap',
38164                                 'cursor' : 'pointer'
38165                             },
38166                             '.sub-link' : {
38167                                 'color' : 'green'
38168                             },
38169                             '.de-act-sup-link' : {
38170                                 'color' : 'purple',
38171                                 'text-decoration' : 'line-through'
38172                             },
38173                             '.de-act-link' : {
38174                                 'color' : 'red',
38175                                 'text-decoration' : 'line-through'
38176                             },
38177                             '.course-timesheet .course-highlight' : {
38178                                 'border-top-style': 'dashed !important',
38179                                 'border-bottom-bottom': 'dashed !important'
38180                             },
38181                             '.course-timesheet .course-item' : {
38182                                 'font-family'   : 'tahoma, arial, helvetica',
38183                                 'font-size'     : '11px',
38184                                 'overflow'      : 'hidden',
38185                                 'padding-left'  : '10px',
38186                                 'padding-right' : '10px',
38187                                 'padding-top' : '10px' 
38188                             }
38189                             
38190                         }, Roo.id());
38191                                 this.ds.load({});
38192                     }
38193                 },
38194                 autoWidth : true,
38195                 monitorWindowResize : false,
38196                 cellrenderer : function(v,x,r)
38197                 {
38198                     return v;
38199                 },
38200                 sm : {
38201                     xtype: 'CellSelectionModel',
38202                     xns: Roo.grid
38203                 },
38204                 dataSource : {
38205                     xtype: 'Store',
38206                     xns: Roo.data,
38207                     listeners : {
38208                         beforeload : function (_self, options)
38209                         {
38210                             options.params = options.params || {};
38211                             options.params._month = _this.monthField.getValue();
38212                             options.params.limit = 9999;
38213                             options.params['sort'] = 'when_dt';    
38214                             options.params['dir'] = 'ASC';    
38215                             this.proxy.loadResponse = this.loadResponse;
38216                             Roo.log("load?");
38217                             //this.addColumns();
38218                         },
38219                         load : function (_self, records, options)
38220                         {
38221                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38222                                 // if you click on the translation.. you can edit it...
38223                                 var el = Roo.get(this);
38224                                 var id = el.dom.getAttribute('data-id');
38225                                 var d = el.dom.getAttribute('data-date');
38226                                 var t = el.dom.getAttribute('data-time');
38227                                 //var id = this.child('span').dom.textContent;
38228                                 
38229                                 //Roo.log(this);
38230                                 Pman.Dialog.CourseCalendar.show({
38231                                     id : id,
38232                                     when_d : d,
38233                                     when_t : t,
38234                                     productitem_active : id ? 1 : 0
38235                                 }, function() {
38236                                     _this.grid.ds.load({});
38237                                 });
38238                            
38239                            });
38240                            
38241                            _this.panel.fireEvent('resize', [ '', '' ]);
38242                         }
38243                     },
38244                     loadResponse : function(o, success, response){
38245                             // this is overridden on before load..
38246                             
38247                             Roo.log("our code?");       
38248                             //Roo.log(success);
38249                             //Roo.log(response)
38250                             delete this.activeRequest;
38251                             if(!success){
38252                                 this.fireEvent("loadexception", this, o, response);
38253                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38254                                 return;
38255                             }
38256                             var result;
38257                             try {
38258                                 result = o.reader.read(response);
38259                             }catch(e){
38260                                 Roo.log("load exception?");
38261                                 this.fireEvent("loadexception", this, o, response, e);
38262                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38263                                 return;
38264                             }
38265                             Roo.log("ready...");        
38266                             // loop through result.records;
38267                             // and set this.tdate[date] = [] << array of records..
38268                             _this.tdata  = {};
38269                             Roo.each(result.records, function(r){
38270                                 //Roo.log(r.data);
38271                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38272                                     _this.tdata[r.data.when_dt.format('j')] = [];
38273                                 }
38274                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38275                             });
38276                             
38277                             //Roo.log(_this.tdata);
38278                             
38279                             result.records = [];
38280                             result.totalRecords = 6;
38281                     
38282                             // let's generate some duumy records for the rows.
38283                             //var st = _this.dateField.getValue();
38284                             
38285                             // work out monday..
38286                             //st = st.add(Date.DAY, -1 * st.format('w'));
38287                             
38288                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38289                             
38290                             var firstOfMonth = date.getFirstDayOfMonth();
38291                             var days = date.getDaysInMonth();
38292                             var d = 1;
38293                             var firstAdded = false;
38294                             for (var i = 0; i < result.totalRecords ; i++) {
38295                                 //var d= st.add(Date.DAY, i);
38296                                 var row = {};
38297                                 var added = 0;
38298                                 for(var w = 0 ; w < 7 ; w++){
38299                                     if(!firstAdded && firstOfMonth != w){
38300                                         continue;
38301                                     }
38302                                     if(d > days){
38303                                         continue;
38304                                     }
38305                                     firstAdded = true;
38306                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38307                                     row['weekday'+w] = String.format(
38308                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38309                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38310                                                     d,
38311                                                     date.format('Y-m-')+dd
38312                                                 );
38313                                     added++;
38314                                     if(typeof(_this.tdata[d]) != 'undefined'){
38315                                         Roo.each(_this.tdata[d], function(r){
38316                                             var is_sub = '';
38317                                             var deactive = '';
38318                                             var id = r.id;
38319                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38320                                             if(r.parent_id*1>0){
38321                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38322                                                 id = r.parent_id;
38323                                             }
38324                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38325                                                 deactive = 'de-act-link';
38326                                             }
38327                                             
38328                                             row['weekday'+w] += String.format(
38329                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38330                                                     id, //0
38331                                                     r.product_id_name, //1
38332                                                     r.when_dt.format('h:ia'), //2
38333                                                     is_sub, //3
38334                                                     deactive, //4
38335                                                     desc // 5
38336                                             );
38337                                         });
38338                                     }
38339                                     d++;
38340                                 }
38341                                 
38342                                 // only do this if something added..
38343                                 if(added > 0){ 
38344                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38345                                 }
38346                                 
38347                                 
38348                                 // push it twice. (second one with an hour..
38349                                 
38350                             }
38351                             //Roo.log(result);
38352                             this.fireEvent("load", this, o, o.request.arg);
38353                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38354                         },
38355                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38356                     proxy : {
38357                         xtype: 'HttpProxy',
38358                         xns: Roo.data,
38359                         method : 'GET',
38360                         url : baseURL + '/Roo/Shop_course.php'
38361                     },
38362                     reader : {
38363                         xtype: 'JsonReader',
38364                         xns: Roo.data,
38365                         id : 'id',
38366                         fields : [
38367                             {
38368                                 'name': 'id',
38369                                 'type': 'int'
38370                             },
38371                             {
38372                                 'name': 'when_dt',
38373                                 'type': 'string'
38374                             },
38375                             {
38376                                 'name': 'end_dt',
38377                                 'type': 'string'
38378                             },
38379                             {
38380                                 'name': 'parent_id',
38381                                 'type': 'int'
38382                             },
38383                             {
38384                                 'name': 'product_id',
38385                                 'type': 'int'
38386                             },
38387                             {
38388                                 'name': 'productitem_id',
38389                                 'type': 'int'
38390                             },
38391                             {
38392                                 'name': 'guid',
38393                                 'type': 'int'
38394                             }
38395                         ]
38396                     }
38397                 },
38398                 toolbar : {
38399                     xtype: 'Toolbar',
38400                     xns: Roo,
38401                     items : [
38402                         {
38403                             xtype: 'Button',
38404                             xns: Roo.Toolbar,
38405                             listeners : {
38406                                 click : function (_self, e)
38407                                 {
38408                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38409                                     sd.setMonth(sd.getMonth()-1);
38410                                     _this.monthField.setValue(sd.format('Y-m-d'));
38411                                     _this.grid.ds.load({});
38412                                 }
38413                             },
38414                             text : "Back"
38415                         },
38416                         {
38417                             xtype: 'Separator',
38418                             xns: Roo.Toolbar
38419                         },
38420                         {
38421                             xtype: 'MonthField',
38422                             xns: Roo.form,
38423                             listeners : {
38424                                 render : function (_self)
38425                                 {
38426                                     _this.monthField = _self;
38427                                    // _this.monthField.set  today
38428                                 },
38429                                 select : function (combo, date)
38430                                 {
38431                                     _this.grid.ds.load({});
38432                                 }
38433                             },
38434                             value : (function() { return new Date(); })()
38435                         },
38436                         {
38437                             xtype: 'Separator',
38438                             xns: Roo.Toolbar
38439                         },
38440                         {
38441                             xtype: 'TextItem',
38442                             xns: Roo.Toolbar,
38443                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38444                         },
38445                         {
38446                             xtype: 'Fill',
38447                             xns: Roo.Toolbar
38448                         },
38449                         {
38450                             xtype: 'Button',
38451                             xns: Roo.Toolbar,
38452                             listeners : {
38453                                 click : function (_self, e)
38454                                 {
38455                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38456                                     sd.setMonth(sd.getMonth()+1);
38457                                     _this.monthField.setValue(sd.format('Y-m-d'));
38458                                     _this.grid.ds.load({});
38459                                 }
38460                             },
38461                             text : "Next"
38462                         }
38463                     ]
38464                 },
38465                  
38466             }
38467         };
38468         
38469         *//*
38470  * Based on:
38471  * Ext JS Library 1.1.1
38472  * Copyright(c) 2006-2007, Ext JS, LLC.
38473  *
38474  * Originally Released Under LGPL - original licence link has changed is not relivant.
38475  *
38476  * Fork - LGPL
38477  * <script type="text/javascript">
38478  */
38479  
38480 /**
38481  * @class Roo.LoadMask
38482  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38483  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38484  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38485  * element's UpdateManager load indicator and will be destroyed after the initial load.
38486  * @constructor
38487  * Create a new LoadMask
38488  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38489  * @param {Object} config The config object
38490  */
38491 Roo.LoadMask = function(el, config){
38492     this.el = Roo.get(el);
38493     Roo.apply(this, config);
38494     if(this.store){
38495         this.store.on('beforeload', this.onBeforeLoad, this);
38496         this.store.on('load', this.onLoad, this);
38497         this.store.on('loadexception', this.onLoadException, this);
38498         this.removeMask = false;
38499     }else{
38500         var um = this.el.getUpdateManager();
38501         um.showLoadIndicator = false; // disable the default indicator
38502         um.on('beforeupdate', this.onBeforeLoad, this);
38503         um.on('update', this.onLoad, this);
38504         um.on('failure', this.onLoad, this);
38505         this.removeMask = true;
38506     }
38507 };
38508
38509 Roo.LoadMask.prototype = {
38510     /**
38511      * @cfg {Boolean} removeMask
38512      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38513      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38514      */
38515     removeMask : false,
38516     /**
38517      * @cfg {String} msg
38518      * The text to display in a centered loading message box (defaults to 'Loading...')
38519      */
38520     msg : 'Loading...',
38521     /**
38522      * @cfg {String} msgCls
38523      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38524      */
38525     msgCls : 'x-mask-loading',
38526
38527     /**
38528      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38529      * @type Boolean
38530      */
38531     disabled: false,
38532
38533     /**
38534      * Disables the mask to prevent it from being displayed
38535      */
38536     disable : function(){
38537        this.disabled = true;
38538     },
38539
38540     /**
38541      * Enables the mask so that it can be displayed
38542      */
38543     enable : function(){
38544         this.disabled = false;
38545     },
38546     
38547     onLoadException : function()
38548     {
38549         Roo.log(arguments);
38550         
38551         if (typeof(arguments[3]) != 'undefined') {
38552             Roo.MessageBox.alert("Error loading",arguments[3]);
38553         } 
38554         /*
38555         try {
38556             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38557                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38558             }   
38559         } catch(e) {
38560             
38561         }
38562         */
38563     
38564         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38565     },
38566     // private
38567     onLoad : function()
38568     {
38569         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38570     },
38571
38572     // private
38573     onBeforeLoad : function(){
38574         if(!this.disabled){
38575             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38576         }
38577     },
38578
38579     // private
38580     destroy : function(){
38581         if(this.store){
38582             this.store.un('beforeload', this.onBeforeLoad, this);
38583             this.store.un('load', this.onLoad, this);
38584             this.store.un('loadexception', this.onLoadException, this);
38585         }else{
38586             var um = this.el.getUpdateManager();
38587             um.un('beforeupdate', this.onBeforeLoad, this);
38588             um.un('update', this.onLoad, this);
38589             um.un('failure', this.onLoad, this);
38590         }
38591     }
38592 };/*
38593  * Based on:
38594  * Ext JS Library 1.1.1
38595  * Copyright(c) 2006-2007, Ext JS, LLC.
38596  *
38597  * Originally Released Under LGPL - original licence link has changed is not relivant.
38598  *
38599  * Fork - LGPL
38600  * <script type="text/javascript">
38601  */
38602
38603
38604 /**
38605  * @class Roo.XTemplate
38606  * @extends Roo.Template
38607  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38608 <pre><code>
38609 var t = new Roo.XTemplate(
38610         '&lt;select name="{name}"&gt;',
38611                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38612         '&lt;/select&gt;'
38613 );
38614  
38615 // then append, applying the master template values
38616  </code></pre>
38617  *
38618  * Supported features:
38619  *
38620  *  Tags:
38621
38622 <pre><code>
38623       {a_variable} - output encoded.
38624       {a_variable.format:("Y-m-d")} - call a method on the variable
38625       {a_variable:raw} - unencoded output
38626       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38627       {a_variable:this.method_on_template(...)} - call a method on the template object.
38628  
38629 </code></pre>
38630  *  The tpl tag:
38631 <pre><code>
38632         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38633         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38634         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38635         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38636   
38637         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38638         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38639 </code></pre>
38640  *      
38641  */
38642 Roo.XTemplate = function()
38643 {
38644     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38645     if (this.html) {
38646         this.compile();
38647     }
38648 };
38649
38650
38651 Roo.extend(Roo.XTemplate, Roo.Template, {
38652
38653     /**
38654      * The various sub templates
38655      */
38656     tpls : false,
38657     /**
38658      *
38659      * basic tag replacing syntax
38660      * WORD:WORD()
38661      *
38662      * // you can fake an object call by doing this
38663      *  x.t:(test,tesT) 
38664      * 
38665      */
38666     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38667
38668     /**
38669      * compile the template
38670      *
38671      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38672      *
38673      */
38674     compile: function()
38675     {
38676         var s = this.html;
38677      
38678         s = ['<tpl>', s, '</tpl>'].join('');
38679     
38680         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38681             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38682             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38683             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38684             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38685             m,
38686             id     = 0,
38687             tpls   = [];
38688     
38689         while(true == !!(m = s.match(re))){
38690             var forMatch   = m[0].match(nameRe),
38691                 ifMatch   = m[0].match(ifRe),
38692                 execMatch   = m[0].match(execRe),
38693                 namedMatch   = m[0].match(namedRe),
38694                 
38695                 exp  = null, 
38696                 fn   = null,
38697                 exec = null,
38698                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38699                 
38700             if (ifMatch) {
38701                 // if - puts fn into test..
38702                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38703                 if(exp){
38704                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38705                 }
38706             }
38707             
38708             if (execMatch) {
38709                 // exec - calls a function... returns empty if true is  returned.
38710                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38711                 if(exp){
38712                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38713                 }
38714             }
38715             
38716             
38717             if (name) {
38718                 // for = 
38719                 switch(name){
38720                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38721                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38722                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38723                 }
38724             }
38725             var uid = namedMatch ? namedMatch[1] : id;
38726             
38727             
38728             tpls.push({
38729                 id:     namedMatch ? namedMatch[1] : id,
38730                 target: name,
38731                 exec:   exec,
38732                 test:   fn,
38733                 body:   m[1] || ''
38734             });
38735             if (namedMatch) {
38736                 s = s.replace(m[0], '');
38737             } else { 
38738                 s = s.replace(m[0], '{xtpl'+ id + '}');
38739             }
38740             ++id;
38741         }
38742         this.tpls = [];
38743         for(var i = tpls.length-1; i >= 0; --i){
38744             this.compileTpl(tpls[i]);
38745             this.tpls[tpls[i].id] = tpls[i];
38746         }
38747         this.master = tpls[tpls.length-1];
38748         return this;
38749     },
38750     /**
38751      * same as applyTemplate, except it's done to one of the subTemplates
38752      * when using named templates, you can do:
38753      *
38754      * var str = pl.applySubTemplate('your-name', values);
38755      *
38756      * 
38757      * @param {Number} id of the template
38758      * @param {Object} values to apply to template
38759      * @param {Object} parent (normaly the instance of this object)
38760      */
38761     applySubTemplate : function(id, values, parent)
38762     {
38763         
38764         
38765         var t = this.tpls[id];
38766         
38767         
38768         try { 
38769             if(t.test && !t.test.call(this, values, parent)){
38770                 return '';
38771             }
38772         } catch(e) {
38773             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38774             Roo.log(e.toString());
38775             Roo.log(t.test);
38776             return ''
38777         }
38778         try { 
38779             
38780             if(t.exec && t.exec.call(this, values, parent)){
38781                 return '';
38782             }
38783         } catch(e) {
38784             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38785             Roo.log(e.toString());
38786             Roo.log(t.exec);
38787             return ''
38788         }
38789         try {
38790             var vs = t.target ? t.target.call(this, values, parent) : values;
38791             parent = t.target ? values : parent;
38792             if(t.target && vs instanceof Array){
38793                 var buf = [];
38794                 for(var i = 0, len = vs.length; i < len; i++){
38795                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38796                 }
38797                 return buf.join('');
38798             }
38799             return t.compiled.call(this, vs, parent);
38800         } catch (e) {
38801             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38802             Roo.log(e.toString());
38803             Roo.log(t.compiled);
38804             return '';
38805         }
38806     },
38807
38808     compileTpl : function(tpl)
38809     {
38810         var fm = Roo.util.Format;
38811         var useF = this.disableFormats !== true;
38812         var sep = Roo.isGecko ? "+" : ",";
38813         var undef = function(str) {
38814             Roo.log("Property not found :"  + str);
38815             return '';
38816         };
38817         
38818         var fn = function(m, name, format, args)
38819         {
38820             //Roo.log(arguments);
38821             args = args ? args.replace(/\\'/g,"'") : args;
38822             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38823             if (typeof(format) == 'undefined') {
38824                 format= 'htmlEncode';
38825             }
38826             if (format == 'raw' ) {
38827                 format = false;
38828             }
38829             
38830             if(name.substr(0, 4) == 'xtpl'){
38831                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38832             }
38833             
38834             // build an array of options to determine if value is undefined..
38835             
38836             // basically get 'xxxx.yyyy' then do
38837             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38838             //    (function () { Roo.log("Property not found"); return ''; })() :
38839             //    ......
38840             
38841             var udef_ar = [];
38842             var lookfor = '';
38843             Roo.each(name.split('.'), function(st) {
38844                 lookfor += (lookfor.length ? '.': '') + st;
38845                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38846             });
38847             
38848             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38849             
38850             
38851             if(format && useF){
38852                 
38853                 args = args ? ',' + args : "";
38854                  
38855                 if(format.substr(0, 5) != "this."){
38856                     format = "fm." + format + '(';
38857                 }else{
38858                     format = 'this.call("'+ format.substr(5) + '", ';
38859                     args = ", values";
38860                 }
38861                 
38862                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38863             }
38864              
38865             if (args.length) {
38866                 // called with xxyx.yuu:(test,test)
38867                 // change to ()
38868                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38869             }
38870             // raw.. - :raw modifier..
38871             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38872             
38873         };
38874         var body;
38875         // branched to use + in gecko and [].join() in others
38876         if(Roo.isGecko){
38877             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38878                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38879                     "';};};";
38880         }else{
38881             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38882             body.push(tpl.body.replace(/(\r\n|\n)/g,
38883                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38884             body.push("'].join('');};};");
38885             body = body.join('');
38886         }
38887         
38888         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38889        
38890         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38891         eval(body);
38892         
38893         return this;
38894     },
38895
38896     applyTemplate : function(values){
38897         return this.master.compiled.call(this, values, {});
38898         //var s = this.subs;
38899     },
38900
38901     apply : function(){
38902         return this.applyTemplate.apply(this, arguments);
38903     }
38904
38905  });
38906
38907 Roo.XTemplate.from = function(el){
38908     el = Roo.getDom(el);
38909     return new Roo.XTemplate(el.value || el.innerHTML);
38910 };