d06df651cc176019c803e0eab86864527d4cb018
[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.Reader} 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  * Base class for reading structured data from a data source.  This class is intended to be
1277  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1278  */
1279
1280 Roo.data.DataReader = function(meta, recordType){
1281     
1282     this.meta = meta;
1283     
1284     this.recordType = recordType instanceof Array ? 
1285         Roo.data.Record.create(recordType) : recordType;
1286 };
1287
1288 Roo.data.DataReader.prototype = {
1289     
1290     
1291     readerType : 'Data',
1292      /**
1293      * Create an empty record
1294      * @param {Object} data (optional) - overlay some values
1295      * @return {Roo.data.Record} record created.
1296      */
1297     newRow :  function(d) {
1298         var da =  {};
1299         this.recordType.prototype.fields.each(function(c) {
1300             switch( c.type) {
1301                 case 'int' : da[c.name] = 0; break;
1302                 case 'date' : da[c.name] = new Date(); break;
1303                 case 'float' : da[c.name] = 0.0; break;
1304                 case 'boolean' : da[c.name] = false; break;
1305                 default : da[c.name] = ""; break;
1306             }
1307             
1308         });
1309         return new this.recordType(Roo.apply(da, d));
1310     }
1311     
1312     
1313 };/*
1314  * Based on:
1315  * Ext JS Library 1.1.1
1316  * Copyright(c) 2006-2007, Ext JS, LLC.
1317  *
1318  * Originally Released Under LGPL - original licence link has changed is not relivant.
1319  *
1320  * Fork - LGPL
1321  * <script type="text/javascript">
1322  */
1323
1324 /**
1325  * @class Roo.data.DataProxy
1326  * @extends Roo.data.Observable
1327  * This class is an abstract base class for implementations which provide retrieval of
1328  * unformatted data objects.<br>
1329  * <p>
1330  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1331  * (of the appropriate type which knows how to parse the data object) to provide a block of
1332  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1333  * <p>
1334  * Custom implementations must implement the load method as described in
1335  * {@link Roo.data.HttpProxy#load}.
1336  */
1337 Roo.data.DataProxy = function(){
1338     this.addEvents({
1339         /**
1340          * @event beforeload
1341          * Fires before a network request is made to retrieve a data object.
1342          * @param {Object} This DataProxy object.
1343          * @param {Object} params The params parameter to the load function.
1344          */
1345         beforeload : true,
1346         /**
1347          * @event load
1348          * Fires before the load method's callback is called.
1349          * @param {Object} This DataProxy object.
1350          * @param {Object} o The data object.
1351          * @param {Object} arg The callback argument object passed to the load function.
1352          */
1353         load : true,
1354         /**
1355          * @event loadexception
1356          * Fires if an Exception occurs during data retrieval.
1357          * @param {Object} This DataProxy object.
1358          * @param {Object} o The data object.
1359          * @param {Object} arg The callback argument object passed to the load function.
1360          * @param {Object} e The Exception.
1361          */
1362         loadexception : true
1363     });
1364     Roo.data.DataProxy.superclass.constructor.call(this);
1365 };
1366
1367 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1368
1369     /**
1370      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1371      */
1372 /*
1373  * Based on:
1374  * Ext JS Library 1.1.1
1375  * Copyright(c) 2006-2007, Ext JS, LLC.
1376  *
1377  * Originally Released Under LGPL - original licence link has changed is not relivant.
1378  *
1379  * Fork - LGPL
1380  * <script type="text/javascript">
1381  */
1382 /**
1383  * @class Roo.data.MemoryProxy
1384  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1385  * to the Reader when its load method is called.
1386  * @constructor
1387  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1388  */
1389 Roo.data.MemoryProxy = function(data){
1390     if (data.data) {
1391         data = data.data;
1392     }
1393     Roo.data.MemoryProxy.superclass.constructor.call(this);
1394     this.data = data;
1395 };
1396
1397 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1398     
1399     /**
1400      * Load data from the requested source (in this case an in-memory
1401      * data object passed to the constructor), read the data object into
1402      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1403      * process that block using the passed callback.
1404      * @param {Object} params This parameter is not used by the MemoryProxy class.
1405      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1406      * object into a block of Roo.data.Records.
1407      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1408      * The function must be passed <ul>
1409      * <li>The Record block object</li>
1410      * <li>The "arg" argument from the load function</li>
1411      * <li>A boolean success indicator</li>
1412      * </ul>
1413      * @param {Object} scope The scope in which to call the callback
1414      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1415      */
1416     load : function(params, reader, callback, scope, arg){
1417         params = params || {};
1418         var result;
1419         try {
1420             result = reader.readRecords(params.data ? params.data :this.data);
1421         }catch(e){
1422             this.fireEvent("loadexception", this, arg, null, e);
1423             callback.call(scope, null, arg, false);
1424             return;
1425         }
1426         callback.call(scope, result, arg, true);
1427     },
1428     
1429     // private
1430     update : function(params, records){
1431         
1432     }
1433 });/*
1434  * Based on:
1435  * Ext JS Library 1.1.1
1436  * Copyright(c) 2006-2007, Ext JS, LLC.
1437  *
1438  * Originally Released Under LGPL - original licence link has changed is not relivant.
1439  *
1440  * Fork - LGPL
1441  * <script type="text/javascript">
1442  */
1443 /**
1444  * @class Roo.data.HttpProxy
1445  * @extends Roo.data.DataProxy
1446  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1447  * configured to reference a certain URL.<br><br>
1448  * <p>
1449  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1450  * from which the running page was served.<br><br>
1451  * <p>
1452  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1453  * <p>
1454  * Be aware that to enable the browser to parse an XML document, the server must set
1455  * the Content-Type header in the HTTP response to "text/xml".
1456  * @constructor
1457  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1458  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1459  * will be used to make the request.
1460  */
1461 Roo.data.HttpProxy = function(conn){
1462     Roo.data.HttpProxy.superclass.constructor.call(this);
1463     // is conn a conn config or a real conn?
1464     this.conn = conn;
1465     this.useAjax = !conn || !conn.events;
1466   
1467 };
1468
1469 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1470     // thse are take from connection...
1471     
1472     /**
1473      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1474      */
1475     /**
1476      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1477      * extra parameters to each request made by this object. (defaults to undefined)
1478      */
1479     /**
1480      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1481      *  to each request made by this object. (defaults to undefined)
1482      */
1483     /**
1484      * @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)
1485      */
1486     /**
1487      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1488      */
1489      /**
1490      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1491      * @type Boolean
1492      */
1493   
1494
1495     /**
1496      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1497      * @type Boolean
1498      */
1499     /**
1500      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1501      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1502      * a finer-grained basis than the DataProxy events.
1503      */
1504     getConnection : function(){
1505         return this.useAjax ? Roo.Ajax : this.conn;
1506     },
1507
1508     /**
1509      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1510      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1511      * process that block using the passed callback.
1512      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1513      * for the request to the remote server.
1514      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1515      * object into a block of Roo.data.Records.
1516      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1517      * The function must be passed <ul>
1518      * <li>The Record block object</li>
1519      * <li>The "arg" argument from the load function</li>
1520      * <li>A boolean success indicator</li>
1521      * </ul>
1522      * @param {Object} scope The scope in which to call the callback
1523      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1524      */
1525     load : function(params, reader, callback, scope, arg){
1526         if(this.fireEvent("beforeload", this, params) !== false){
1527             var  o = {
1528                 params : params || {},
1529                 request: {
1530                     callback : callback,
1531                     scope : scope,
1532                     arg : arg
1533                 },
1534                 reader: reader,
1535                 callback : this.loadResponse,
1536                 scope: this
1537             };
1538             if(this.useAjax){
1539                 Roo.applyIf(o, this.conn);
1540                 if(this.activeRequest){
1541                     Roo.Ajax.abort(this.activeRequest);
1542                 }
1543                 this.activeRequest = Roo.Ajax.request(o);
1544             }else{
1545                 this.conn.request(o);
1546             }
1547         }else{
1548             callback.call(scope||this, null, arg, false);
1549         }
1550     },
1551
1552     // private
1553     loadResponse : function(o, success, response){
1554         delete this.activeRequest;
1555         if(!success){
1556             this.fireEvent("loadexception", this, o, response);
1557             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1558             return;
1559         }
1560         var result;
1561         try {
1562             result = o.reader.read(response);
1563         }catch(e){
1564             this.fireEvent("loadexception", this, o, response, e);
1565             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1566             return;
1567         }
1568         
1569         this.fireEvent("load", this, o, o.request.arg);
1570         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1571     },
1572
1573     // private
1574     update : function(dataSet){
1575
1576     },
1577
1578     // private
1579     updateResponse : function(dataSet){
1580
1581     }
1582 });/*
1583  * Based on:
1584  * Ext JS Library 1.1.1
1585  * Copyright(c) 2006-2007, Ext JS, LLC.
1586  *
1587  * Originally Released Under LGPL - original licence link has changed is not relivant.
1588  *
1589  * Fork - LGPL
1590  * <script type="text/javascript">
1591  */
1592
1593 /**
1594  * @class Roo.data.ScriptTagProxy
1595  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1596  * other than the originating domain of the running page.<br><br>
1597  * <p>
1598  * <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
1599  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1600  * <p>
1601  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1602  * source code that is used as the source inside a &lt;script> tag.<br><br>
1603  * <p>
1604  * In order for the browser to process the returned data, the server must wrap the data object
1605  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1606  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1607  * depending on whether the callback name was passed:
1608  * <p>
1609  * <pre><code>
1610 boolean scriptTag = false;
1611 String cb = request.getParameter("callback");
1612 if (cb != null) {
1613     scriptTag = true;
1614     response.setContentType("text/javascript");
1615 } else {
1616     response.setContentType("application/x-json");
1617 }
1618 Writer out = response.getWriter();
1619 if (scriptTag) {
1620     out.write(cb + "(");
1621 }
1622 out.print(dataBlock.toJsonString());
1623 if (scriptTag) {
1624     out.write(");");
1625 }
1626 </pre></code>
1627  *
1628  * @constructor
1629  * @param {Object} config A configuration object.
1630  */
1631 Roo.data.ScriptTagProxy = function(config){
1632     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1633     Roo.apply(this, config);
1634     this.head = document.getElementsByTagName("head")[0];
1635 };
1636
1637 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1638
1639 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1640     /**
1641      * @cfg {String} url The URL from which to request the data object.
1642      */
1643     /**
1644      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1645      */
1646     timeout : 30000,
1647     /**
1648      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1649      * the server the name of the callback function set up by the load call to process the returned data object.
1650      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1651      * javascript output which calls this named function passing the data object as its only parameter.
1652      */
1653     callbackParam : "callback",
1654     /**
1655      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1656      * name to the request.
1657      */
1658     nocache : true,
1659
1660     /**
1661      * Load data from the configured URL, read the data object into
1662      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1663      * process that block using the passed callback.
1664      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1665      * for the request to the remote server.
1666      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1667      * object into a block of Roo.data.Records.
1668      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1669      * The function must be passed <ul>
1670      * <li>The Record block object</li>
1671      * <li>The "arg" argument from the load function</li>
1672      * <li>A boolean success indicator</li>
1673      * </ul>
1674      * @param {Object} scope The scope in which to call the callback
1675      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1676      */
1677     load : function(params, reader, callback, scope, arg){
1678         if(this.fireEvent("beforeload", this, params) !== false){
1679
1680             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1681
1682             var url = this.url;
1683             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1684             if(this.nocache){
1685                 url += "&_dc=" + (new Date().getTime());
1686             }
1687             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1688             var trans = {
1689                 id : transId,
1690                 cb : "stcCallback"+transId,
1691                 scriptId : "stcScript"+transId,
1692                 params : params,
1693                 arg : arg,
1694                 url : url,
1695                 callback : callback,
1696                 scope : scope,
1697                 reader : reader
1698             };
1699             var conn = this;
1700
1701             window[trans.cb] = function(o){
1702                 conn.handleResponse(o, trans);
1703             };
1704
1705             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1706
1707             if(this.autoAbort !== false){
1708                 this.abort();
1709             }
1710
1711             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1712
1713             var script = document.createElement("script");
1714             script.setAttribute("src", url);
1715             script.setAttribute("type", "text/javascript");
1716             script.setAttribute("id", trans.scriptId);
1717             this.head.appendChild(script);
1718
1719             this.trans = trans;
1720         }else{
1721             callback.call(scope||this, null, arg, false);
1722         }
1723     },
1724
1725     // private
1726     isLoading : function(){
1727         return this.trans ? true : false;
1728     },
1729
1730     /**
1731      * Abort the current server request.
1732      */
1733     abort : function(){
1734         if(this.isLoading()){
1735             this.destroyTrans(this.trans);
1736         }
1737     },
1738
1739     // private
1740     destroyTrans : function(trans, isLoaded){
1741         this.head.removeChild(document.getElementById(trans.scriptId));
1742         clearTimeout(trans.timeoutId);
1743         if(isLoaded){
1744             window[trans.cb] = undefined;
1745             try{
1746                 delete window[trans.cb];
1747             }catch(e){}
1748         }else{
1749             // if hasn't been loaded, wait for load to remove it to prevent script error
1750             window[trans.cb] = function(){
1751                 window[trans.cb] = undefined;
1752                 try{
1753                     delete window[trans.cb];
1754                 }catch(e){}
1755             };
1756         }
1757     },
1758
1759     // private
1760     handleResponse : function(o, trans){
1761         this.trans = false;
1762         this.destroyTrans(trans, true);
1763         var result;
1764         try {
1765             result = trans.reader.readRecords(o);
1766         }catch(e){
1767             this.fireEvent("loadexception", this, o, trans.arg, e);
1768             trans.callback.call(trans.scope||window, null, trans.arg, false);
1769             return;
1770         }
1771         this.fireEvent("load", this, o, trans.arg);
1772         trans.callback.call(trans.scope||window, result, trans.arg, true);
1773     },
1774
1775     // private
1776     handleFailure : function(trans){
1777         this.trans = false;
1778         this.destroyTrans(trans, false);
1779         this.fireEvent("loadexception", this, null, trans.arg);
1780         trans.callback.call(trans.scope||window, null, trans.arg, false);
1781     }
1782 });/*
1783  * Based on:
1784  * Ext JS Library 1.1.1
1785  * Copyright(c) 2006-2007, Ext JS, LLC.
1786  *
1787  * Originally Released Under LGPL - original licence link has changed is not relivant.
1788  *
1789  * Fork - LGPL
1790  * <script type="text/javascript">
1791  */
1792
1793 /**
1794  * @class Roo.data.JsonReader
1795  * @extends Roo.data.DataReader
1796  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1797  * based on mappings in a provided Roo.data.Record constructor.
1798  * 
1799  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1800  * in the reply previously. 
1801  * 
1802  * <p>
1803  * Example code:
1804  * <pre><code>
1805 var RecordDef = Roo.data.Record.create([
1806     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1807     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1808 ]);
1809 var myReader = new Roo.data.JsonReader({
1810     totalProperty: "results",    // The property which contains the total dataset size (optional)
1811     root: "rows",                // The property which contains an Array of row objects
1812     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1813 }, RecordDef);
1814 </code></pre>
1815  * <p>
1816  * This would consume a JSON file like this:
1817  * <pre><code>
1818 { 'results': 2, 'rows': [
1819     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1820     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1821 }
1822 </code></pre>
1823  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1824  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1825  * paged from the remote server.
1826  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1827  * @cfg {String} root name of the property which contains the Array of row objects.
1828  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1829  * @cfg {Array} fields Array of field definition objects
1830  * @constructor
1831  * Create a new JsonReader
1832  * @param {Object} meta Metadata configuration options
1833  * @param {Object} recordType Either an Array of field definition objects,
1834  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1835  */
1836 Roo.data.JsonReader = function(meta, recordType){
1837     
1838     meta = meta || {};
1839     // set some defaults:
1840     Roo.applyIf(meta, {
1841         totalProperty: 'total',
1842         successProperty : 'success',
1843         root : 'data',
1844         id : 'id'
1845     });
1846     
1847     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1848 };
1849 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1850     
1851     readerType : 'Json',
1852     
1853     /**
1854      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1855      * Used by Store query builder to append _requestMeta to params.
1856      * 
1857      */
1858     metaFromRemote : false,
1859     /**
1860      * This method is only used by a DataProxy which has retrieved data from a remote server.
1861      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1862      * @return {Object} data A data block which is used by an Roo.data.Store object as
1863      * a cache of Roo.data.Records.
1864      */
1865     read : function(response){
1866         var json = response.responseText;
1867        
1868         var o = /* eval:var:o */ eval("("+json+")");
1869         if(!o) {
1870             throw {message: "JsonReader.read: Json object not found"};
1871         }
1872         
1873         if(o.metaData){
1874             
1875             delete this.ef;
1876             this.metaFromRemote = true;
1877             this.meta = o.metaData;
1878             this.recordType = Roo.data.Record.create(o.metaData.fields);
1879             this.onMetaChange(this.meta, this.recordType, o);
1880         }
1881         return this.readRecords(o);
1882     },
1883
1884     // private function a store will implement
1885     onMetaChange : function(meta, recordType, o){
1886
1887     },
1888
1889     /**
1890          * @ignore
1891          */
1892     simpleAccess: function(obj, subsc) {
1893         return obj[subsc];
1894     },
1895
1896         /**
1897          * @ignore
1898          */
1899     getJsonAccessor: function(){
1900         var re = /[\[\.]/;
1901         return function(expr) {
1902             try {
1903                 return(re.test(expr))
1904                     ? new Function("obj", "return obj." + expr)
1905                     : function(obj){
1906                         return obj[expr];
1907                     };
1908             } catch(e){}
1909             return Roo.emptyFn;
1910         };
1911     }(),
1912
1913     /**
1914      * Create a data block containing Roo.data.Records from an XML document.
1915      * @param {Object} o An object which contains an Array of row objects in the property specified
1916      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1917      * which contains the total size of the dataset.
1918      * @return {Object} data A data block which is used by an Roo.data.Store object as
1919      * a cache of Roo.data.Records.
1920      */
1921     readRecords : function(o){
1922         /**
1923          * After any data loads, the raw JSON data is available for further custom processing.
1924          * @type Object
1925          */
1926         this.o = o;
1927         var s = this.meta, Record = this.recordType,
1928             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1929
1930 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1931         if (!this.ef) {
1932             if(s.totalProperty) {
1933                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1934                 }
1935                 if(s.successProperty) {
1936                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1937                 }
1938                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1939                 if (s.id) {
1940                         var g = this.getJsonAccessor(s.id);
1941                         this.getId = function(rec) {
1942                                 var r = g(rec);  
1943                                 return (r === undefined || r === "") ? null : r;
1944                         };
1945                 } else {
1946                         this.getId = function(){return null;};
1947                 }
1948             this.ef = [];
1949             for(var jj = 0; jj < fl; jj++){
1950                 f = fi[jj];
1951                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1952                 this.ef[jj] = this.getJsonAccessor(map);
1953             }
1954         }
1955
1956         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1957         if(s.totalProperty){
1958             var vt = parseInt(this.getTotal(o), 10);
1959             if(!isNaN(vt)){
1960                 totalRecords = vt;
1961             }
1962         }
1963         if(s.successProperty){
1964             var vs = this.getSuccess(o);
1965             if(vs === false || vs === 'false'){
1966                 success = false;
1967             }
1968         }
1969         var records = [];
1970         for(var i = 0; i < c; i++){
1971                 var n = root[i];
1972             var values = {};
1973             var id = this.getId(n);
1974             for(var j = 0; j < fl; j++){
1975                 f = fi[j];
1976             var v = this.ef[j](n);
1977             if (!f.convert) {
1978                 Roo.log('missing convert for ' + f.name);
1979                 Roo.log(f);
1980                 continue;
1981             }
1982             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1983             }
1984             var record = new Record(values, id);
1985             record.json = n;
1986             records[i] = record;
1987         }
1988         return {
1989             raw : o,
1990             success : success,
1991             records : records,
1992             totalRecords : totalRecords
1993         };
1994     },
1995     // used when loading children.. @see loadDataFromChildren
1996     toLoadData: function(rec)
1997     {
1998         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
1999         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2000         return { data : data, total : data.length };
2001         
2002     }
2003 });/*
2004  * Based on:
2005  * Ext JS Library 1.1.1
2006  * Copyright(c) 2006-2007, Ext JS, LLC.
2007  *
2008  * Originally Released Under LGPL - original licence link has changed is not relivant.
2009  *
2010  * Fork - LGPL
2011  * <script type="text/javascript">
2012  */
2013
2014 /**
2015  * @class Roo.data.XmlReader
2016  * @extends Roo.data.DataReader
2017  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2018  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2019  * <p>
2020  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2021  * header in the HTTP response must be set to "text/xml".</em>
2022  * <p>
2023  * Example code:
2024  * <pre><code>
2025 var RecordDef = Roo.data.Record.create([
2026    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2027    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2028 ]);
2029 var myReader = new Roo.data.XmlReader({
2030    totalRecords: "results", // The element which contains the total dataset size (optional)
2031    record: "row",           // The repeated element which contains row information
2032    id: "id"                 // The element within the row that provides an ID for the record (optional)
2033 }, RecordDef);
2034 </code></pre>
2035  * <p>
2036  * This would consume an XML file like this:
2037  * <pre><code>
2038 &lt;?xml?>
2039 &lt;dataset>
2040  &lt;results>2&lt;/results>
2041  &lt;row>
2042    &lt;id>1&lt;/id>
2043    &lt;name>Bill&lt;/name>
2044    &lt;occupation>Gardener&lt;/occupation>
2045  &lt;/row>
2046  &lt;row>
2047    &lt;id>2&lt;/id>
2048    &lt;name>Ben&lt;/name>
2049    &lt;occupation>Horticulturalist&lt;/occupation>
2050  &lt;/row>
2051 &lt;/dataset>
2052 </code></pre>
2053  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2054  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2055  * paged from the remote server.
2056  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2057  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2058  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2059  * a record identifier value.
2060  * @constructor
2061  * Create a new XmlReader
2062  * @param {Object} meta Metadata configuration options
2063  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2064  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2065  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2066  */
2067 Roo.data.XmlReader = function(meta, recordType){
2068     meta = meta || {};
2069     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2070 };
2071 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2072     
2073     readerType : 'Xml',
2074     
2075     /**
2076      * This method is only used by a DataProxy which has retrieved data from a remote server.
2077          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2078          * to contain a method called 'responseXML' that returns an XML document object.
2079      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2080      * a cache of Roo.data.Records.
2081      */
2082     read : function(response){
2083         var doc = response.responseXML;
2084         if(!doc) {
2085             throw {message: "XmlReader.read: XML Document not available"};
2086         }
2087         return this.readRecords(doc);
2088     },
2089
2090     /**
2091      * Create a data block containing Roo.data.Records from an XML document.
2092          * @param {Object} doc A parsed XML document.
2093      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2094      * a cache of Roo.data.Records.
2095      */
2096     readRecords : function(doc){
2097         /**
2098          * After any data loads/reads, the raw XML Document is available for further custom processing.
2099          * @type XMLDocument
2100          */
2101         this.xmlData = doc;
2102         var root = doc.documentElement || doc;
2103         var q = Roo.DomQuery;
2104         var recordType = this.recordType, fields = recordType.prototype.fields;
2105         var sid = this.meta.id;
2106         var totalRecords = 0, success = true;
2107         if(this.meta.totalRecords){
2108             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2109         }
2110         
2111         if(this.meta.success){
2112             var sv = q.selectValue(this.meta.success, root, true);
2113             success = sv !== false && sv !== 'false';
2114         }
2115         var records = [];
2116         var ns = q.select(this.meta.record, root);
2117         for(var i = 0, len = ns.length; i < len; i++) {
2118                 var n = ns[i];
2119                 var values = {};
2120                 var id = sid ? q.selectValue(sid, n) : undefined;
2121                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2122                     var f = fields.items[j];
2123                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2124                     v = f.convert(v);
2125                     values[f.name] = v;
2126                 }
2127                 var record = new recordType(values, id);
2128                 record.node = n;
2129                 records[records.length] = record;
2130             }
2131
2132             return {
2133                 success : success,
2134                 records : records,
2135                 totalRecords : totalRecords || records.length
2136             };
2137     }
2138 });/*
2139  * Based on:
2140  * Ext JS Library 1.1.1
2141  * Copyright(c) 2006-2007, Ext JS, LLC.
2142  *
2143  * Originally Released Under LGPL - original licence link has changed is not relivant.
2144  *
2145  * Fork - LGPL
2146  * <script type="text/javascript">
2147  */
2148
2149 /**
2150  * @class Roo.data.ArrayReader
2151  * @extends Roo.data.DataReader
2152  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2153  * Each element of that Array represents a row of data fields. The
2154  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2155  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2156  * <p>
2157  * Example code:.
2158  * <pre><code>
2159 var RecordDef = Roo.data.Record.create([
2160     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2161     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2162 ]);
2163 var myReader = new Roo.data.ArrayReader({
2164     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2165 }, RecordDef);
2166 </code></pre>
2167  * <p>
2168  * This would consume an Array like this:
2169  * <pre><code>
2170 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2171   </code></pre>
2172  
2173  * @constructor
2174  * Create a new JsonReader
2175  * @param {Object} meta Metadata configuration options.
2176  * @param {Object|Array} recordType Either an Array of field definition objects
2177  * 
2178  * @cfg {Array} fields Array of field definition objects
2179  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2180  * as specified to {@link Roo.data.Record#create},
2181  * or an {@link Roo.data.Record} object
2182  *
2183  * 
2184  * created using {@link Roo.data.Record#create}.
2185  */
2186 Roo.data.ArrayReader = function(meta, recordType)
2187 {    
2188     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2189 };
2190
2191 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2192     
2193       /**
2194      * Create a data block containing Roo.data.Records from an XML document.
2195      * @param {Object} o An Array of row objects which represents the dataset.
2196      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2197      * a cache of Roo.data.Records.
2198      */
2199     readRecords : function(o)
2200     {
2201         var sid = this.meta ? this.meta.id : null;
2202         var recordType = this.recordType, fields = recordType.prototype.fields;
2203         var records = [];
2204         var root = o;
2205         for(var i = 0; i < root.length; i++){
2206             var n = root[i];
2207             var values = {};
2208             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2209             for(var j = 0, jlen = fields.length; j < jlen; j++){
2210                 var f = fields.items[j];
2211                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2212                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2213                 v = f.convert(v);
2214                 values[f.name] = v;
2215             }
2216             var record = new recordType(values, id);
2217             record.json = n;
2218             records[records.length] = record;
2219         }
2220         return {
2221             records : records,
2222             totalRecords : records.length
2223         };
2224     },
2225     // used when loading children.. @see loadDataFromChildren
2226     toLoadData: function(rec)
2227     {
2228         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2229         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2230         
2231     }
2232     
2233     
2234 });/*
2235  * Based on:
2236  * Ext JS Library 1.1.1
2237  * Copyright(c) 2006-2007, Ext JS, LLC.
2238  *
2239  * Originally Released Under LGPL - original licence link has changed is not relivant.
2240  *
2241  * Fork - LGPL
2242  * <script type="text/javascript">
2243  */
2244
2245
2246 /**
2247  * @class Roo.data.Tree
2248  * @extends Roo.util.Observable
2249  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2250  * in the tree have most standard DOM functionality.
2251  * @constructor
2252  * @param {Node} root (optional) The root node
2253  */
2254 Roo.data.Tree = function(root){
2255    this.nodeHash = {};
2256    /**
2257     * The root node for this tree
2258     * @type Node
2259     */
2260    this.root = null;
2261    if(root){
2262        this.setRootNode(root);
2263    }
2264    this.addEvents({
2265        /**
2266         * @event append
2267         * Fires when a new child node is appended to a node in this tree.
2268         * @param {Tree} tree The owner tree
2269         * @param {Node} parent The parent node
2270         * @param {Node} node The newly appended node
2271         * @param {Number} index The index of the newly appended node
2272         */
2273        "append" : true,
2274        /**
2275         * @event remove
2276         * Fires when a child node is removed from a node in this tree.
2277         * @param {Tree} tree The owner tree
2278         * @param {Node} parent The parent node
2279         * @param {Node} node The child node removed
2280         */
2281        "remove" : true,
2282        /**
2283         * @event move
2284         * Fires when a node is moved to a new location in the tree
2285         * @param {Tree} tree The owner tree
2286         * @param {Node} node The node moved
2287         * @param {Node} oldParent The old parent of this node
2288         * @param {Node} newParent The new parent of this node
2289         * @param {Number} index The index it was moved to
2290         */
2291        "move" : true,
2292        /**
2293         * @event insert
2294         * Fires when a new child node is inserted in a node in this tree.
2295         * @param {Tree} tree The owner tree
2296         * @param {Node} parent The parent node
2297         * @param {Node} node The child node inserted
2298         * @param {Node} refNode The child node the node was inserted before
2299         */
2300        "insert" : true,
2301        /**
2302         * @event beforeappend
2303         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2304         * @param {Tree} tree The owner tree
2305         * @param {Node} parent The parent node
2306         * @param {Node} node The child node to be appended
2307         */
2308        "beforeappend" : true,
2309        /**
2310         * @event beforeremove
2311         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2312         * @param {Tree} tree The owner tree
2313         * @param {Node} parent The parent node
2314         * @param {Node} node The child node to be removed
2315         */
2316        "beforeremove" : true,
2317        /**
2318         * @event beforemove
2319         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2320         * @param {Tree} tree The owner tree
2321         * @param {Node} node The node being moved
2322         * @param {Node} oldParent The parent of the node
2323         * @param {Node} newParent The new parent the node is moving to
2324         * @param {Number} index The index it is being moved to
2325         */
2326        "beforemove" : true,
2327        /**
2328         * @event beforeinsert
2329         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2330         * @param {Tree} tree The owner tree
2331         * @param {Node} parent The parent node
2332         * @param {Node} node The child node to be inserted
2333         * @param {Node} refNode The child node the node is being inserted before
2334         */
2335        "beforeinsert" : true
2336    });
2337
2338     Roo.data.Tree.superclass.constructor.call(this);
2339 };
2340
2341 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2342     pathSeparator: "/",
2343
2344     proxyNodeEvent : function(){
2345         return this.fireEvent.apply(this, arguments);
2346     },
2347
2348     /**
2349      * Returns the root node for this tree.
2350      * @return {Node}
2351      */
2352     getRootNode : function(){
2353         return this.root;
2354     },
2355
2356     /**
2357      * Sets the root node for this tree.
2358      * @param {Node} node
2359      * @return {Node}
2360      */
2361     setRootNode : function(node){
2362         this.root = node;
2363         node.ownerTree = this;
2364         node.isRoot = true;
2365         this.registerNode(node);
2366         return node;
2367     },
2368
2369     /**
2370      * Gets a node in this tree by its id.
2371      * @param {String} id
2372      * @return {Node}
2373      */
2374     getNodeById : function(id){
2375         return this.nodeHash[id];
2376     },
2377
2378     registerNode : function(node){
2379         this.nodeHash[node.id] = node;
2380     },
2381
2382     unregisterNode : function(node){
2383         delete this.nodeHash[node.id];
2384     },
2385
2386     toString : function(){
2387         return "[Tree"+(this.id?" "+this.id:"")+"]";
2388     }
2389 });
2390
2391 /**
2392  * @class Roo.data.Node
2393  * @extends Roo.util.Observable
2394  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2395  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2396  * @constructor
2397  * @param {Object} attributes The attributes/config for the node
2398  */
2399 Roo.data.Node = function(attributes){
2400     /**
2401      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2402      * @type {Object}
2403      */
2404     this.attributes = attributes || {};
2405     this.leaf = this.attributes.leaf;
2406     /**
2407      * The node id. @type String
2408      */
2409     this.id = this.attributes.id;
2410     if(!this.id){
2411         this.id = Roo.id(null, "ynode-");
2412         this.attributes.id = this.id;
2413     }
2414      
2415     
2416     /**
2417      * All child nodes of this node. @type Array
2418      */
2419     this.childNodes = [];
2420     if(!this.childNodes.indexOf){ // indexOf is a must
2421         this.childNodes.indexOf = function(o){
2422             for(var i = 0, len = this.length; i < len; i++){
2423                 if(this[i] == o) {
2424                     return i;
2425                 }
2426             }
2427             return -1;
2428         };
2429     }
2430     /**
2431      * The parent node for this node. @type Node
2432      */
2433     this.parentNode = null;
2434     /**
2435      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2436      */
2437     this.firstChild = null;
2438     /**
2439      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2440      */
2441     this.lastChild = null;
2442     /**
2443      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2444      */
2445     this.previousSibling = null;
2446     /**
2447      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2448      */
2449     this.nextSibling = null;
2450
2451     this.addEvents({
2452        /**
2453         * @event append
2454         * Fires when a new child node is appended
2455         * @param {Tree} tree The owner tree
2456         * @param {Node} this This node
2457         * @param {Node} node The newly appended node
2458         * @param {Number} index The index of the newly appended node
2459         */
2460        "append" : true,
2461        /**
2462         * @event remove
2463         * Fires when a child node is removed
2464         * @param {Tree} tree The owner tree
2465         * @param {Node} this This node
2466         * @param {Node} node The removed node
2467         */
2468        "remove" : true,
2469        /**
2470         * @event move
2471         * Fires when this node is moved to a new location in the tree
2472         * @param {Tree} tree The owner tree
2473         * @param {Node} this This node
2474         * @param {Node} oldParent The old parent of this node
2475         * @param {Node} newParent The new parent of this node
2476         * @param {Number} index The index it was moved to
2477         */
2478        "move" : true,
2479        /**
2480         * @event insert
2481         * Fires when a new child node is inserted.
2482         * @param {Tree} tree The owner tree
2483         * @param {Node} this This node
2484         * @param {Node} node The child node inserted
2485         * @param {Node} refNode The child node the node was inserted before
2486         */
2487        "insert" : true,
2488        /**
2489         * @event beforeappend
2490         * Fires before a new child is appended, return false to cancel the append.
2491         * @param {Tree} tree The owner tree
2492         * @param {Node} this This node
2493         * @param {Node} node The child node to be appended
2494         */
2495        "beforeappend" : true,
2496        /**
2497         * @event beforeremove
2498         * Fires before a child is removed, return false to cancel the remove.
2499         * @param {Tree} tree The owner tree
2500         * @param {Node} this This node
2501         * @param {Node} node The child node to be removed
2502         */
2503        "beforeremove" : true,
2504        /**
2505         * @event beforemove
2506         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2507         * @param {Tree} tree The owner tree
2508         * @param {Node} this This node
2509         * @param {Node} oldParent The parent of this node
2510         * @param {Node} newParent The new parent this node is moving to
2511         * @param {Number} index The index it is being moved to
2512         */
2513        "beforemove" : true,
2514        /**
2515         * @event beforeinsert
2516         * Fires before a new child is inserted, return false to cancel the insert.
2517         * @param {Tree} tree The owner tree
2518         * @param {Node} this This node
2519         * @param {Node} node The child node to be inserted
2520         * @param {Node} refNode The child node the node is being inserted before
2521         */
2522        "beforeinsert" : true
2523    });
2524     this.listeners = this.attributes.listeners;
2525     Roo.data.Node.superclass.constructor.call(this);
2526 };
2527
2528 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2529     fireEvent : function(evtName){
2530         // first do standard event for this node
2531         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2532             return false;
2533         }
2534         // then bubble it up to the tree if the event wasn't cancelled
2535         var ot = this.getOwnerTree();
2536         if(ot){
2537             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2538                 return false;
2539             }
2540         }
2541         return true;
2542     },
2543
2544     /**
2545      * Returns true if this node is a leaf
2546      * @return {Boolean}
2547      */
2548     isLeaf : function(){
2549         return this.leaf === true;
2550     },
2551
2552     // private
2553     setFirstChild : function(node){
2554         this.firstChild = node;
2555     },
2556
2557     //private
2558     setLastChild : function(node){
2559         this.lastChild = node;
2560     },
2561
2562
2563     /**
2564      * Returns true if this node is the last child of its parent
2565      * @return {Boolean}
2566      */
2567     isLast : function(){
2568        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2569     },
2570
2571     /**
2572      * Returns true if this node is the first child of its parent
2573      * @return {Boolean}
2574      */
2575     isFirst : function(){
2576        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2577     },
2578
2579     hasChildNodes : function(){
2580         return !this.isLeaf() && this.childNodes.length > 0;
2581     },
2582
2583     /**
2584      * Insert node(s) as the last child node of this node.
2585      * @param {Node/Array} node The node or Array of nodes to append
2586      * @return {Node} The appended node if single append, or null if an array was passed
2587      */
2588     appendChild : function(node){
2589         var multi = false;
2590         if(node instanceof Array){
2591             multi = node;
2592         }else if(arguments.length > 1){
2593             multi = arguments;
2594         }
2595         
2596         // if passed an array or multiple args do them one by one
2597         if(multi){
2598             for(var i = 0, len = multi.length; i < len; i++) {
2599                 this.appendChild(multi[i]);
2600             }
2601         }else{
2602             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2603                 return false;
2604             }
2605             var index = this.childNodes.length;
2606             var oldParent = node.parentNode;
2607             // it's a move, make sure we move it cleanly
2608             if(oldParent){
2609                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2610                     return false;
2611                 }
2612                 oldParent.removeChild(node);
2613             }
2614             
2615             index = this.childNodes.length;
2616             if(index == 0){
2617                 this.setFirstChild(node);
2618             }
2619             this.childNodes.push(node);
2620             node.parentNode = this;
2621             var ps = this.childNodes[index-1];
2622             if(ps){
2623                 node.previousSibling = ps;
2624                 ps.nextSibling = node;
2625             }else{
2626                 node.previousSibling = null;
2627             }
2628             node.nextSibling = null;
2629             this.setLastChild(node);
2630             node.setOwnerTree(this.getOwnerTree());
2631             this.fireEvent("append", this.ownerTree, this, node, index);
2632             if(this.ownerTree) {
2633                 this.ownerTree.fireEvent("appendnode", this, node, index);
2634             }
2635             if(oldParent){
2636                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2637             }
2638             return node;
2639         }
2640     },
2641
2642     /**
2643      * Removes a child node from this node.
2644      * @param {Node} node The node to remove
2645      * @return {Node} The removed node
2646      */
2647     removeChild : function(node){
2648         var index = this.childNodes.indexOf(node);
2649         if(index == -1){
2650             return false;
2651         }
2652         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2653             return false;
2654         }
2655
2656         // remove it from childNodes collection
2657         this.childNodes.splice(index, 1);
2658
2659         // update siblings
2660         if(node.previousSibling){
2661             node.previousSibling.nextSibling = node.nextSibling;
2662         }
2663         if(node.nextSibling){
2664             node.nextSibling.previousSibling = node.previousSibling;
2665         }
2666
2667         // update child refs
2668         if(this.firstChild == node){
2669             this.setFirstChild(node.nextSibling);
2670         }
2671         if(this.lastChild == node){
2672             this.setLastChild(node.previousSibling);
2673         }
2674
2675         node.setOwnerTree(null);
2676         // clear any references from the node
2677         node.parentNode = null;
2678         node.previousSibling = null;
2679         node.nextSibling = null;
2680         this.fireEvent("remove", this.ownerTree, this, node);
2681         return node;
2682     },
2683
2684     /**
2685      * Inserts the first node before the second node in this nodes childNodes collection.
2686      * @param {Node} node The node to insert
2687      * @param {Node} refNode The node to insert before (if null the node is appended)
2688      * @return {Node} The inserted node
2689      */
2690     insertBefore : function(node, refNode){
2691         if(!refNode){ // like standard Dom, refNode can be null for append
2692             return this.appendChild(node);
2693         }
2694         // nothing to do
2695         if(node == refNode){
2696             return false;
2697         }
2698
2699         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2700             return false;
2701         }
2702         var index = this.childNodes.indexOf(refNode);
2703         var oldParent = node.parentNode;
2704         var refIndex = index;
2705
2706         // when moving internally, indexes will change after remove
2707         if(oldParent == this && this.childNodes.indexOf(node) < index){
2708             refIndex--;
2709         }
2710
2711         // it's a move, make sure we move it cleanly
2712         if(oldParent){
2713             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2714                 return false;
2715             }
2716             oldParent.removeChild(node);
2717         }
2718         if(refIndex == 0){
2719             this.setFirstChild(node);
2720         }
2721         this.childNodes.splice(refIndex, 0, node);
2722         node.parentNode = this;
2723         var ps = this.childNodes[refIndex-1];
2724         if(ps){
2725             node.previousSibling = ps;
2726             ps.nextSibling = node;
2727         }else{
2728             node.previousSibling = null;
2729         }
2730         node.nextSibling = refNode;
2731         refNode.previousSibling = node;
2732         node.setOwnerTree(this.getOwnerTree());
2733         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2734         if(oldParent){
2735             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2736         }
2737         return node;
2738     },
2739
2740     /**
2741      * Returns the child node at the specified index.
2742      * @param {Number} index
2743      * @return {Node}
2744      */
2745     item : function(index){
2746         return this.childNodes[index];
2747     },
2748
2749     /**
2750      * Replaces one child node in this node with another.
2751      * @param {Node} newChild The replacement node
2752      * @param {Node} oldChild The node to replace
2753      * @return {Node} The replaced node
2754      */
2755     replaceChild : function(newChild, oldChild){
2756         this.insertBefore(newChild, oldChild);
2757         this.removeChild(oldChild);
2758         return oldChild;
2759     },
2760
2761     /**
2762      * Returns the index of a child node
2763      * @param {Node} node
2764      * @return {Number} The index of the node or -1 if it was not found
2765      */
2766     indexOf : function(child){
2767         return this.childNodes.indexOf(child);
2768     },
2769
2770     /**
2771      * Returns the tree this node is in.
2772      * @return {Tree}
2773      */
2774     getOwnerTree : function(){
2775         // if it doesn't have one, look for one
2776         if(!this.ownerTree){
2777             var p = this;
2778             while(p){
2779                 if(p.ownerTree){
2780                     this.ownerTree = p.ownerTree;
2781                     break;
2782                 }
2783                 p = p.parentNode;
2784             }
2785         }
2786         return this.ownerTree;
2787     },
2788
2789     /**
2790      * Returns depth of this node (the root node has a depth of 0)
2791      * @return {Number}
2792      */
2793     getDepth : function(){
2794         var depth = 0;
2795         var p = this;
2796         while(p.parentNode){
2797             ++depth;
2798             p = p.parentNode;
2799         }
2800         return depth;
2801     },
2802
2803     // private
2804     setOwnerTree : function(tree){
2805         // if it's move, we need to update everyone
2806         if(tree != this.ownerTree){
2807             if(this.ownerTree){
2808                 this.ownerTree.unregisterNode(this);
2809             }
2810             this.ownerTree = tree;
2811             var cs = this.childNodes;
2812             for(var i = 0, len = cs.length; i < len; i++) {
2813                 cs[i].setOwnerTree(tree);
2814             }
2815             if(tree){
2816                 tree.registerNode(this);
2817             }
2818         }
2819     },
2820
2821     /**
2822      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2823      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2824      * @return {String} The path
2825      */
2826     getPath : function(attr){
2827         attr = attr || "id";
2828         var p = this.parentNode;
2829         var b = [this.attributes[attr]];
2830         while(p){
2831             b.unshift(p.attributes[attr]);
2832             p = p.parentNode;
2833         }
2834         var sep = this.getOwnerTree().pathSeparator;
2835         return sep + b.join(sep);
2836     },
2837
2838     /**
2839      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2840      * function call will be the scope provided or the current node. The arguments to the function
2841      * will be the args provided or the current node. If the function returns false at any point,
2842      * the bubble is stopped.
2843      * @param {Function} fn The function to call
2844      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2845      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2846      */
2847     bubble : function(fn, scope, args){
2848         var p = this;
2849         while(p){
2850             if(fn.call(scope || p, args || p) === false){
2851                 break;
2852             }
2853             p = p.parentNode;
2854         }
2855     },
2856
2857     /**
2858      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2859      * function call will be the scope provided or the current node. The arguments to the function
2860      * will be the args provided or the current node. If the function returns false at any point,
2861      * the cascade is stopped on that branch.
2862      * @param {Function} fn The function to call
2863      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2864      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2865      */
2866     cascade : function(fn, scope, args){
2867         if(fn.call(scope || this, args || this) !== false){
2868             var cs = this.childNodes;
2869             for(var i = 0, len = cs.length; i < len; i++) {
2870                 cs[i].cascade(fn, scope, args);
2871             }
2872         }
2873     },
2874
2875     /**
2876      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2877      * function call will be the scope provided or the current node. The arguments to the function
2878      * will be the args provided or the current node. If the function returns false at any point,
2879      * the iteration stops.
2880      * @param {Function} fn The function to call
2881      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2882      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2883      */
2884     eachChild : function(fn, scope, args){
2885         var cs = this.childNodes;
2886         for(var i = 0, len = cs.length; i < len; i++) {
2887                 if(fn.call(scope || this, args || cs[i]) === false){
2888                     break;
2889                 }
2890         }
2891     },
2892
2893     /**
2894      * Finds the first child that has the attribute with the specified value.
2895      * @param {String} attribute The attribute name
2896      * @param {Mixed} value The value to search for
2897      * @return {Node} The found child or null if none was found
2898      */
2899     findChild : function(attribute, value){
2900         var cs = this.childNodes;
2901         for(var i = 0, len = cs.length; i < len; i++) {
2902                 if(cs[i].attributes[attribute] == value){
2903                     return cs[i];
2904                 }
2905         }
2906         return null;
2907     },
2908
2909     /**
2910      * Finds the first child by a custom function. The child matches if the function passed
2911      * returns true.
2912      * @param {Function} fn
2913      * @param {Object} scope (optional)
2914      * @return {Node} The found child or null if none was found
2915      */
2916     findChildBy : function(fn, scope){
2917         var cs = this.childNodes;
2918         for(var i = 0, len = cs.length; i < len; i++) {
2919                 if(fn.call(scope||cs[i], cs[i]) === true){
2920                     return cs[i];
2921                 }
2922         }
2923         return null;
2924     },
2925
2926     /**
2927      * Sorts this nodes children using the supplied sort function
2928      * @param {Function} fn
2929      * @param {Object} scope (optional)
2930      */
2931     sort : function(fn, scope){
2932         var cs = this.childNodes;
2933         var len = cs.length;
2934         if(len > 0){
2935             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2936             cs.sort(sortFn);
2937             for(var i = 0; i < len; i++){
2938                 var n = cs[i];
2939                 n.previousSibling = cs[i-1];
2940                 n.nextSibling = cs[i+1];
2941                 if(i == 0){
2942                     this.setFirstChild(n);
2943                 }
2944                 if(i == len-1){
2945                     this.setLastChild(n);
2946                 }
2947             }
2948         }
2949     },
2950
2951     /**
2952      * Returns true if this node is an ancestor (at any point) of the passed node.
2953      * @param {Node} node
2954      * @return {Boolean}
2955      */
2956     contains : function(node){
2957         return node.isAncestor(this);
2958     },
2959
2960     /**
2961      * Returns true if the passed node is an ancestor (at any point) of this node.
2962      * @param {Node} node
2963      * @return {Boolean}
2964      */
2965     isAncestor : function(node){
2966         var p = this.parentNode;
2967         while(p){
2968             if(p == node){
2969                 return true;
2970             }
2971             p = p.parentNode;
2972         }
2973         return false;
2974     },
2975
2976     toString : function(){
2977         return "[Node"+(this.id?" "+this.id:"")+"]";
2978     }
2979 });/*
2980  * Based on:
2981  * Ext JS Library 1.1.1
2982  * Copyright(c) 2006-2007, Ext JS, LLC.
2983  *
2984  * Originally Released Under LGPL - original licence link has changed is not relivant.
2985  *
2986  * Fork - LGPL
2987  * <script type="text/javascript">
2988  */
2989
2990
2991 /**
2992  * @class Roo.Shadow
2993  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
2994  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
2995  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2996  * @constructor
2997  * Create a new Shadow
2998  * @param {Object} config The config object
2999  */
3000 Roo.Shadow = function(config){
3001     Roo.apply(this, config);
3002     if(typeof this.mode != "string"){
3003         this.mode = this.defaultMode;
3004     }
3005     var o = this.offset, a = {h: 0};
3006     var rad = Math.floor(this.offset/2);
3007     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3008         case "drop":
3009             a.w = 0;
3010             a.l = a.t = o;
3011             a.t -= 1;
3012             if(Roo.isIE){
3013                 a.l -= this.offset + rad;
3014                 a.t -= this.offset + rad;
3015                 a.w -= rad;
3016                 a.h -= rad;
3017                 a.t += 1;
3018             }
3019         break;
3020         case "sides":
3021             a.w = (o*2);
3022             a.l = -o;
3023             a.t = o-1;
3024             if(Roo.isIE){
3025                 a.l -= (this.offset - rad);
3026                 a.t -= this.offset + rad;
3027                 a.l += 1;
3028                 a.w -= (this.offset - rad)*2;
3029                 a.w -= rad + 1;
3030                 a.h -= 1;
3031             }
3032         break;
3033         case "frame":
3034             a.w = a.h = (o*2);
3035             a.l = a.t = -o;
3036             a.t += 1;
3037             a.h -= 2;
3038             if(Roo.isIE){
3039                 a.l -= (this.offset - rad);
3040                 a.t -= (this.offset - rad);
3041                 a.l += 1;
3042                 a.w -= (this.offset + rad + 1);
3043                 a.h -= (this.offset + rad);
3044                 a.h += 1;
3045             }
3046         break;
3047     };
3048
3049     this.adjusts = a;
3050 };
3051
3052 Roo.Shadow.prototype = {
3053     /**
3054      * @cfg {String} mode
3055      * The shadow display mode.  Supports the following options:<br />
3056      * sides: Shadow displays on both sides and bottom only<br />
3057      * frame: Shadow displays equally on all four sides<br />
3058      * drop: Traditional bottom-right drop shadow (default)
3059      */
3060     mode: false,
3061     /**
3062      * @cfg {String} offset
3063      * The number of pixels to offset the shadow from the element (defaults to 4)
3064      */
3065     offset: 4,
3066
3067     // private
3068     defaultMode: "drop",
3069
3070     /**
3071      * Displays the shadow under the target element
3072      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3073      */
3074     show : function(target){
3075         target = Roo.get(target);
3076         if(!this.el){
3077             this.el = Roo.Shadow.Pool.pull();
3078             if(this.el.dom.nextSibling != target.dom){
3079                 this.el.insertBefore(target);
3080             }
3081         }
3082         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3083         if(Roo.isIE){
3084             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3085         }
3086         this.realign(
3087             target.getLeft(true),
3088             target.getTop(true),
3089             target.getWidth(),
3090             target.getHeight()
3091         );
3092         this.el.dom.style.display = "block";
3093     },
3094
3095     /**
3096      * Returns true if the shadow is visible, else false
3097      */
3098     isVisible : function(){
3099         return this.el ? true : false;  
3100     },
3101
3102     /**
3103      * Direct alignment when values are already available. Show must be called at least once before
3104      * calling this method to ensure it is initialized.
3105      * @param {Number} left The target element left position
3106      * @param {Number} top The target element top position
3107      * @param {Number} width The target element width
3108      * @param {Number} height The target element height
3109      */
3110     realign : function(l, t, w, h){
3111         if(!this.el){
3112             return;
3113         }
3114         var a = this.adjusts, d = this.el.dom, s = d.style;
3115         var iea = 0;
3116         s.left = (l+a.l)+"px";
3117         s.top = (t+a.t)+"px";
3118         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3119  
3120         if(s.width != sws || s.height != shs){
3121             s.width = sws;
3122             s.height = shs;
3123             if(!Roo.isIE){
3124                 var cn = d.childNodes;
3125                 var sww = Math.max(0, (sw-12))+"px";
3126                 cn[0].childNodes[1].style.width = sww;
3127                 cn[1].childNodes[1].style.width = sww;
3128                 cn[2].childNodes[1].style.width = sww;
3129                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3130             }
3131         }
3132     },
3133
3134     /**
3135      * Hides this shadow
3136      */
3137     hide : function(){
3138         if(this.el){
3139             this.el.dom.style.display = "none";
3140             Roo.Shadow.Pool.push(this.el);
3141             delete this.el;
3142         }
3143     },
3144
3145     /**
3146      * Adjust the z-index of this shadow
3147      * @param {Number} zindex The new z-index
3148      */
3149     setZIndex : function(z){
3150         this.zIndex = z;
3151         if(this.el){
3152             this.el.setStyle("z-index", z);
3153         }
3154     }
3155 };
3156
3157 // Private utility class that manages the internal Shadow cache
3158 Roo.Shadow.Pool = function(){
3159     var p = [];
3160     var markup = Roo.isIE ?
3161                  '<div class="x-ie-shadow"></div>' :
3162                  '<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>';
3163     return {
3164         pull : function(){
3165             var sh = p.shift();
3166             if(!sh){
3167                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3168                 sh.autoBoxAdjust = false;
3169             }
3170             return sh;
3171         },
3172
3173         push : function(sh){
3174             p.push(sh);
3175         }
3176     };
3177 }();/*
3178  * Based on:
3179  * Ext JS Library 1.1.1
3180  * Copyright(c) 2006-2007, Ext JS, LLC.
3181  *
3182  * Originally Released Under LGPL - original licence link has changed is not relivant.
3183  *
3184  * Fork - LGPL
3185  * <script type="text/javascript">
3186  */
3187
3188
3189 /**
3190  * @class Roo.SplitBar
3191  * @extends Roo.util.Observable
3192  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3193  * <br><br>
3194  * Usage:
3195  * <pre><code>
3196 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3197                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3198 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3199 split.minSize = 100;
3200 split.maxSize = 600;
3201 split.animate = true;
3202 split.on('moved', splitterMoved);
3203 </code></pre>
3204  * @constructor
3205  * Create a new SplitBar
3206  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3207  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3208  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3209  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3210                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3211                         position of the SplitBar).
3212  */
3213 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3214     
3215     /** @private */
3216     this.el = Roo.get(dragElement, true);
3217     this.el.dom.unselectable = "on";
3218     /** @private */
3219     this.resizingEl = Roo.get(resizingElement, true);
3220
3221     /**
3222      * @private
3223      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3224      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3225      * @type Number
3226      */
3227     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3228     
3229     /**
3230      * The minimum size of the resizing element. (Defaults to 0)
3231      * @type Number
3232      */
3233     this.minSize = 0;
3234     
3235     /**
3236      * The maximum size of the resizing element. (Defaults to 2000)
3237      * @type Number
3238      */
3239     this.maxSize = 2000;
3240     
3241     /**
3242      * Whether to animate the transition to the new size
3243      * @type Boolean
3244      */
3245     this.animate = false;
3246     
3247     /**
3248      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3249      * @type Boolean
3250      */
3251     this.useShim = false;
3252     
3253     /** @private */
3254     this.shim = null;
3255     
3256     if(!existingProxy){
3257         /** @private */
3258         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3259     }else{
3260         this.proxy = Roo.get(existingProxy).dom;
3261     }
3262     /** @private */
3263     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3264     
3265     /** @private */
3266     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3267     
3268     /** @private */
3269     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3270     
3271     /** @private */
3272     this.dragSpecs = {};
3273     
3274     /**
3275      * @private The adapter to use to positon and resize elements
3276      */
3277     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3278     this.adapter.init(this);
3279     
3280     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3281         /** @private */
3282         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3283         this.el.addClass("x-splitbar-h");
3284     }else{
3285         /** @private */
3286         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3287         this.el.addClass("x-splitbar-v");
3288     }
3289     
3290     this.addEvents({
3291         /**
3292          * @event resize
3293          * Fires when the splitter is moved (alias for {@link #event-moved})
3294          * @param {Roo.SplitBar} this
3295          * @param {Number} newSize the new width or height
3296          */
3297         "resize" : true,
3298         /**
3299          * @event moved
3300          * Fires when the splitter is moved
3301          * @param {Roo.SplitBar} this
3302          * @param {Number} newSize the new width or height
3303          */
3304         "moved" : true,
3305         /**
3306          * @event beforeresize
3307          * Fires before the splitter is dragged
3308          * @param {Roo.SplitBar} this
3309          */
3310         "beforeresize" : true,
3311
3312         "beforeapply" : true
3313     });
3314
3315     Roo.util.Observable.call(this);
3316 };
3317
3318 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3319     onStartProxyDrag : function(x, y){
3320         this.fireEvent("beforeresize", this);
3321         if(!this.overlay){
3322             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3323             o.unselectable();
3324             o.enableDisplayMode("block");
3325             // all splitbars share the same overlay
3326             Roo.SplitBar.prototype.overlay = o;
3327         }
3328         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3329         this.overlay.show();
3330         Roo.get(this.proxy).setDisplayed("block");
3331         var size = this.adapter.getElementSize(this);
3332         this.activeMinSize = this.getMinimumSize();;
3333         this.activeMaxSize = this.getMaximumSize();;
3334         var c1 = size - this.activeMinSize;
3335         var c2 = Math.max(this.activeMaxSize - size, 0);
3336         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3337             this.dd.resetConstraints();
3338             this.dd.setXConstraint(
3339                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3340                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3341             );
3342             this.dd.setYConstraint(0, 0);
3343         }else{
3344             this.dd.resetConstraints();
3345             this.dd.setXConstraint(0, 0);
3346             this.dd.setYConstraint(
3347                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3348                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3349             );
3350          }
3351         this.dragSpecs.startSize = size;
3352         this.dragSpecs.startPoint = [x, y];
3353         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3354     },
3355     
3356     /** 
3357      * @private Called after the drag operation by the DDProxy
3358      */
3359     onEndProxyDrag : function(e){
3360         Roo.get(this.proxy).setDisplayed(false);
3361         var endPoint = Roo.lib.Event.getXY(e);
3362         if(this.overlay){
3363             this.overlay.hide();
3364         }
3365         var newSize;
3366         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3367             newSize = this.dragSpecs.startSize + 
3368                 (this.placement == Roo.SplitBar.LEFT ?
3369                     endPoint[0] - this.dragSpecs.startPoint[0] :
3370                     this.dragSpecs.startPoint[0] - endPoint[0]
3371                 );
3372         }else{
3373             newSize = this.dragSpecs.startSize + 
3374                 (this.placement == Roo.SplitBar.TOP ?
3375                     endPoint[1] - this.dragSpecs.startPoint[1] :
3376                     this.dragSpecs.startPoint[1] - endPoint[1]
3377                 );
3378         }
3379         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3380         if(newSize != this.dragSpecs.startSize){
3381             if(this.fireEvent('beforeapply', this, newSize) !== false){
3382                 this.adapter.setElementSize(this, newSize);
3383                 this.fireEvent("moved", this, newSize);
3384                 this.fireEvent("resize", this, newSize);
3385             }
3386         }
3387     },
3388     
3389     /**
3390      * Get the adapter this SplitBar uses
3391      * @return The adapter object
3392      */
3393     getAdapter : function(){
3394         return this.adapter;
3395     },
3396     
3397     /**
3398      * Set the adapter this SplitBar uses
3399      * @param {Object} adapter A SplitBar adapter object
3400      */
3401     setAdapter : function(adapter){
3402         this.adapter = adapter;
3403         this.adapter.init(this);
3404     },
3405     
3406     /**
3407      * Gets the minimum size for the resizing element
3408      * @return {Number} The minimum size
3409      */
3410     getMinimumSize : function(){
3411         return this.minSize;
3412     },
3413     
3414     /**
3415      * Sets the minimum size for the resizing element
3416      * @param {Number} minSize The minimum size
3417      */
3418     setMinimumSize : function(minSize){
3419         this.minSize = minSize;
3420     },
3421     
3422     /**
3423      * Gets the maximum size for the resizing element
3424      * @return {Number} The maximum size
3425      */
3426     getMaximumSize : function(){
3427         return this.maxSize;
3428     },
3429     
3430     /**
3431      * Sets the maximum size for the resizing element
3432      * @param {Number} maxSize The maximum size
3433      */
3434     setMaximumSize : function(maxSize){
3435         this.maxSize = maxSize;
3436     },
3437     
3438     /**
3439      * Sets the initialize size for the resizing element
3440      * @param {Number} size The initial size
3441      */
3442     setCurrentSize : function(size){
3443         var oldAnimate = this.animate;
3444         this.animate = false;
3445         this.adapter.setElementSize(this, size);
3446         this.animate = oldAnimate;
3447     },
3448     
3449     /**
3450      * Destroy this splitbar. 
3451      * @param {Boolean} removeEl True to remove the element
3452      */
3453     destroy : function(removeEl){
3454         if(this.shim){
3455             this.shim.remove();
3456         }
3457         this.dd.unreg();
3458         this.proxy.parentNode.removeChild(this.proxy);
3459         if(removeEl){
3460             this.el.remove();
3461         }
3462     }
3463 });
3464
3465 /**
3466  * @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.
3467  */
3468 Roo.SplitBar.createProxy = function(dir){
3469     var proxy = new Roo.Element(document.createElement("div"));
3470     proxy.unselectable();
3471     var cls = 'x-splitbar-proxy';
3472     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3473     document.body.appendChild(proxy.dom);
3474     return proxy.dom;
3475 };
3476
3477 /** 
3478  * @class Roo.SplitBar.BasicLayoutAdapter
3479  * Default Adapter. It assumes the splitter and resizing element are not positioned
3480  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3481  */
3482 Roo.SplitBar.BasicLayoutAdapter = function(){
3483 };
3484
3485 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3486     // do nothing for now
3487     init : function(s){
3488     
3489     },
3490     /**
3491      * Called before drag operations to get the current size of the resizing element. 
3492      * @param {Roo.SplitBar} s The SplitBar using this adapter
3493      */
3494      getElementSize : function(s){
3495         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3496             return s.resizingEl.getWidth();
3497         }else{
3498             return s.resizingEl.getHeight();
3499         }
3500     },
3501     
3502     /**
3503      * Called after drag operations to set the size of the resizing element.
3504      * @param {Roo.SplitBar} s The SplitBar using this adapter
3505      * @param {Number} newSize The new size to set
3506      * @param {Function} onComplete A function to be invoked when resizing is complete
3507      */
3508     setElementSize : function(s, newSize, onComplete){
3509         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3510             if(!s.animate){
3511                 s.resizingEl.setWidth(newSize);
3512                 if(onComplete){
3513                     onComplete(s, newSize);
3514                 }
3515             }else{
3516                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3517             }
3518         }else{
3519             
3520             if(!s.animate){
3521                 s.resizingEl.setHeight(newSize);
3522                 if(onComplete){
3523                     onComplete(s, newSize);
3524                 }
3525             }else{
3526                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3527             }
3528         }
3529     }
3530 };
3531
3532 /** 
3533  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3534  * @extends Roo.SplitBar.BasicLayoutAdapter
3535  * Adapter that  moves the splitter element to align with the resized sizing element. 
3536  * Used with an absolute positioned SplitBar.
3537  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3538  * document.body, make sure you assign an id to the body element.
3539  */
3540 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3541     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3542     this.container = Roo.get(container);
3543 };
3544
3545 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3546     init : function(s){
3547         this.basic.init(s);
3548     },
3549     
3550     getElementSize : function(s){
3551         return this.basic.getElementSize(s);
3552     },
3553     
3554     setElementSize : function(s, newSize, onComplete){
3555         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3556     },
3557     
3558     moveSplitter : function(s){
3559         var yes = Roo.SplitBar;
3560         switch(s.placement){
3561             case yes.LEFT:
3562                 s.el.setX(s.resizingEl.getRight());
3563                 break;
3564             case yes.RIGHT:
3565                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3566                 break;
3567             case yes.TOP:
3568                 s.el.setY(s.resizingEl.getBottom());
3569                 break;
3570             case yes.BOTTOM:
3571                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3572                 break;
3573         }
3574     }
3575 };
3576
3577 /**
3578  * Orientation constant - Create a vertical SplitBar
3579  * @static
3580  * @type Number
3581  */
3582 Roo.SplitBar.VERTICAL = 1;
3583
3584 /**
3585  * Orientation constant - Create a horizontal SplitBar
3586  * @static
3587  * @type Number
3588  */
3589 Roo.SplitBar.HORIZONTAL = 2;
3590
3591 /**
3592  * Placement constant - The resizing element is to the left of the splitter element
3593  * @static
3594  * @type Number
3595  */
3596 Roo.SplitBar.LEFT = 1;
3597
3598 /**
3599  * Placement constant - The resizing element is to the right of the splitter element
3600  * @static
3601  * @type Number
3602  */
3603 Roo.SplitBar.RIGHT = 2;
3604
3605 /**
3606  * Placement constant - The resizing element is positioned above the splitter element
3607  * @static
3608  * @type Number
3609  */
3610 Roo.SplitBar.TOP = 3;
3611
3612 /**
3613  * Placement constant - The resizing element is positioned under splitter element
3614  * @static
3615  * @type Number
3616  */
3617 Roo.SplitBar.BOTTOM = 4;
3618 /*
3619  * Based on:
3620  * Ext JS Library 1.1.1
3621  * Copyright(c) 2006-2007, Ext JS, LLC.
3622  *
3623  * Originally Released Under LGPL - original licence link has changed is not relivant.
3624  *
3625  * Fork - LGPL
3626  * <script type="text/javascript">
3627  */
3628
3629 /**
3630  * @class Roo.View
3631  * @extends Roo.util.Observable
3632  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3633  * This class also supports single and multi selection modes. <br>
3634  * Create a data model bound view:
3635  <pre><code>
3636  var store = new Roo.data.Store(...);
3637
3638  var view = new Roo.View({
3639     el : "my-element",
3640     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3641  
3642     singleSelect: true,
3643     selectedClass: "ydataview-selected",
3644     store: store
3645  });
3646
3647  // listen for node click?
3648  view.on("click", function(vw, index, node, e){
3649  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3650  });
3651
3652  // load XML data
3653  dataModel.load("foobar.xml");
3654  </code></pre>
3655  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3656  * <br><br>
3657  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3658  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3659  * 
3660  * Note: old style constructor is still suported (container, template, config)
3661  * 
3662  * @constructor
3663  * Create a new View
3664  * @param {Object} config The config object
3665  * 
3666  */
3667 Roo.View = function(config, depreciated_tpl, depreciated_config){
3668     
3669     this.parent = false;
3670     
3671     if (typeof(depreciated_tpl) == 'undefined') {
3672         // new way.. - universal constructor.
3673         Roo.apply(this, config);
3674         this.el  = Roo.get(this.el);
3675     } else {
3676         // old format..
3677         this.el  = Roo.get(config);
3678         this.tpl = depreciated_tpl;
3679         Roo.apply(this, depreciated_config);
3680     }
3681     this.wrapEl  = this.el.wrap().wrap();
3682     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3683     
3684     
3685     if(typeof(this.tpl) == "string"){
3686         this.tpl = new Roo.Template(this.tpl);
3687     } else {
3688         // support xtype ctors..
3689         this.tpl = new Roo.factory(this.tpl, Roo);
3690     }
3691     
3692     
3693     this.tpl.compile();
3694     
3695     /** @private */
3696     this.addEvents({
3697         /**
3698          * @event beforeclick
3699          * Fires before a click is processed. Returns false to cancel the default action.
3700          * @param {Roo.View} this
3701          * @param {Number} index The index of the target node
3702          * @param {HTMLElement} node The target node
3703          * @param {Roo.EventObject} e The raw event object
3704          */
3705             "beforeclick" : true,
3706         /**
3707          * @event click
3708          * Fires when a template node is clicked.
3709          * @param {Roo.View} this
3710          * @param {Number} index The index of the target node
3711          * @param {HTMLElement} node The target node
3712          * @param {Roo.EventObject} e The raw event object
3713          */
3714             "click" : true,
3715         /**
3716          * @event dblclick
3717          * Fires when a template node is double clicked.
3718          * @param {Roo.View} this
3719          * @param {Number} index The index of the target node
3720          * @param {HTMLElement} node The target node
3721          * @param {Roo.EventObject} e The raw event object
3722          */
3723             "dblclick" : true,
3724         /**
3725          * @event contextmenu
3726          * Fires when a template node is right clicked.
3727          * @param {Roo.View} this
3728          * @param {Number} index The index of the target node
3729          * @param {HTMLElement} node The target node
3730          * @param {Roo.EventObject} e The raw event object
3731          */
3732             "contextmenu" : true,
3733         /**
3734          * @event selectionchange
3735          * Fires when the selected nodes change.
3736          * @param {Roo.View} this
3737          * @param {Array} selections Array of the selected nodes
3738          */
3739             "selectionchange" : true,
3740     
3741         /**
3742          * @event beforeselect
3743          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3744          * @param {Roo.View} this
3745          * @param {HTMLElement} node The node to be selected
3746          * @param {Array} selections Array of currently selected nodes
3747          */
3748             "beforeselect" : true,
3749         /**
3750          * @event preparedata
3751          * Fires on every row to render, to allow you to change the data.
3752          * @param {Roo.View} this
3753          * @param {Object} data to be rendered (change this)
3754          */
3755           "preparedata" : true
3756           
3757           
3758         });
3759
3760
3761
3762     this.el.on({
3763         "click": this.onClick,
3764         "dblclick": this.onDblClick,
3765         "contextmenu": this.onContextMenu,
3766         scope:this
3767     });
3768
3769     this.selections = [];
3770     this.nodes = [];
3771     this.cmp = new Roo.CompositeElementLite([]);
3772     if(this.store){
3773         this.store = Roo.factory(this.store, Roo.data);
3774         this.setStore(this.store, true);
3775     }
3776     
3777     if ( this.footer && this.footer.xtype) {
3778            
3779          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3780         
3781         this.footer.dataSource = this.store;
3782         this.footer.container = fctr;
3783         this.footer = Roo.factory(this.footer, Roo);
3784         fctr.insertFirst(this.el);
3785         
3786         // this is a bit insane - as the paging toolbar seems to detach the el..
3787 //        dom.parentNode.parentNode.parentNode
3788          // they get detached?
3789     }
3790     
3791     
3792     Roo.View.superclass.constructor.call(this);
3793     
3794     
3795 };
3796
3797 Roo.extend(Roo.View, Roo.util.Observable, {
3798     
3799      /**
3800      * @cfg {Roo.data.Store} store Data store to load data from.
3801      */
3802     store : false,
3803     
3804     /**
3805      * @cfg {String|Roo.Element} el The container element.
3806      */
3807     el : '',
3808     
3809     /**
3810      * @cfg {String|Roo.Template} tpl The template used by this View 
3811      */
3812     tpl : false,
3813     /**
3814      * @cfg {String} dataName the named area of the template to use as the data area
3815      *                          Works with domtemplates roo-name="name"
3816      */
3817     dataName: false,
3818     /**
3819      * @cfg {String} selectedClass The css class to add to selected nodes
3820      */
3821     selectedClass : "x-view-selected",
3822      /**
3823      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3824      */
3825     emptyText : "",
3826     
3827     /**
3828      * @cfg {String} text to display on mask (default Loading)
3829      */
3830     mask : false,
3831     /**
3832      * @cfg {Boolean} multiSelect Allow multiple selection
3833      */
3834     multiSelect : false,
3835     /**
3836      * @cfg {Boolean} singleSelect Allow single selection
3837      */
3838     singleSelect:  false,
3839     
3840     /**
3841      * @cfg {Boolean} toggleSelect - selecting 
3842      */
3843     toggleSelect : false,
3844     
3845     /**
3846      * @cfg {Boolean} tickable - selecting 
3847      */
3848     tickable : false,
3849     
3850     /**
3851      * Returns the element this view is bound to.
3852      * @return {Roo.Element}
3853      */
3854     getEl : function(){
3855         return this.wrapEl;
3856     },
3857     
3858     
3859
3860     /**
3861      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3862      */
3863     refresh : function(){
3864         //Roo.log('refresh');
3865         var t = this.tpl;
3866         
3867         // if we are using something like 'domtemplate', then
3868         // the what gets used is:
3869         // t.applySubtemplate(NAME, data, wrapping data..)
3870         // the outer template then get' applied with
3871         //     the store 'extra data'
3872         // and the body get's added to the
3873         //      roo-name="data" node?
3874         //      <span class='roo-tpl-{name}'></span> ?????
3875         
3876         
3877         
3878         this.clearSelections();
3879         this.el.update("");
3880         var html = [];
3881         var records = this.store.getRange();
3882         if(records.length < 1) {
3883             
3884             // is this valid??  = should it render a template??
3885             
3886             this.el.update(this.emptyText);
3887             return;
3888         }
3889         var el = this.el;
3890         if (this.dataName) {
3891             this.el.update(t.apply(this.store.meta)); //????
3892             el = this.el.child('.roo-tpl-' + this.dataName);
3893         }
3894         
3895         for(var i = 0, len = records.length; i < len; i++){
3896             var data = this.prepareData(records[i].data, i, records[i]);
3897             this.fireEvent("preparedata", this, data, i, records[i]);
3898             
3899             var d = Roo.apply({}, data);
3900             
3901             if(this.tickable){
3902                 Roo.apply(d, {'roo-id' : Roo.id()});
3903                 
3904                 var _this = this;
3905             
3906                 Roo.each(this.parent.item, function(item){
3907                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3908                         return;
3909                     }
3910                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3911                 });
3912             }
3913             
3914             html[html.length] = Roo.util.Format.trim(
3915                 this.dataName ?
3916                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3917                     t.apply(d)
3918             );
3919         }
3920         
3921         
3922         
3923         el.update(html.join(""));
3924         this.nodes = el.dom.childNodes;
3925         this.updateIndexes(0);
3926     },
3927     
3928
3929     /**
3930      * Function to override to reformat the data that is sent to
3931      * the template for each node.
3932      * DEPRICATED - use the preparedata event handler.
3933      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3934      * a JSON object for an UpdateManager bound view).
3935      */
3936     prepareData : function(data, index, record)
3937     {
3938         this.fireEvent("preparedata", this, data, index, record);
3939         return data;
3940     },
3941
3942     onUpdate : function(ds, record){
3943         // Roo.log('on update');   
3944         this.clearSelections();
3945         var index = this.store.indexOf(record);
3946         var n = this.nodes[index];
3947         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3948         n.parentNode.removeChild(n);
3949         this.updateIndexes(index, index);
3950     },
3951
3952     
3953     
3954 // --------- FIXME     
3955     onAdd : function(ds, records, index)
3956     {
3957         //Roo.log(['on Add', ds, records, index] );        
3958         this.clearSelections();
3959         if(this.nodes.length == 0){
3960             this.refresh();
3961             return;
3962         }
3963         var n = this.nodes[index];
3964         for(var i = 0, len = records.length; i < len; i++){
3965             var d = this.prepareData(records[i].data, i, records[i]);
3966             if(n){
3967                 this.tpl.insertBefore(n, d);
3968             }else{
3969                 
3970                 this.tpl.append(this.el, d);
3971             }
3972         }
3973         this.updateIndexes(index);
3974     },
3975
3976     onRemove : function(ds, record, index){
3977        // Roo.log('onRemove');
3978         this.clearSelections();
3979         var el = this.dataName  ?
3980             this.el.child('.roo-tpl-' + this.dataName) :
3981             this.el; 
3982         
3983         el.dom.removeChild(this.nodes[index]);
3984         this.updateIndexes(index);
3985     },
3986
3987     /**
3988      * Refresh an individual node.
3989      * @param {Number} index
3990      */
3991     refreshNode : function(index){
3992         this.onUpdate(this.store, this.store.getAt(index));
3993     },
3994
3995     updateIndexes : function(startIndex, endIndex){
3996         var ns = this.nodes;
3997         startIndex = startIndex || 0;
3998         endIndex = endIndex || ns.length - 1;
3999         for(var i = startIndex; i <= endIndex; i++){
4000             ns[i].nodeIndex = i;
4001         }
4002     },
4003
4004     /**
4005      * Changes the data store this view uses and refresh the view.
4006      * @param {Store} store
4007      */
4008     setStore : function(store, initial){
4009         if(!initial && this.store){
4010             this.store.un("datachanged", this.refresh);
4011             this.store.un("add", this.onAdd);
4012             this.store.un("remove", this.onRemove);
4013             this.store.un("update", this.onUpdate);
4014             this.store.un("clear", this.refresh);
4015             this.store.un("beforeload", this.onBeforeLoad);
4016             this.store.un("load", this.onLoad);
4017             this.store.un("loadexception", this.onLoad);
4018         }
4019         if(store){
4020           
4021             store.on("datachanged", this.refresh, this);
4022             store.on("add", this.onAdd, this);
4023             store.on("remove", this.onRemove, this);
4024             store.on("update", this.onUpdate, this);
4025             store.on("clear", this.refresh, this);
4026             store.on("beforeload", this.onBeforeLoad, this);
4027             store.on("load", this.onLoad, this);
4028             store.on("loadexception", this.onLoad, this);
4029         }
4030         
4031         if(store){
4032             this.refresh();
4033         }
4034     },
4035     /**
4036      * onbeforeLoad - masks the loading area.
4037      *
4038      */
4039     onBeforeLoad : function(store,opts)
4040     {
4041          //Roo.log('onBeforeLoad');   
4042         if (!opts.add) {
4043             this.el.update("");
4044         }
4045         this.el.mask(this.mask ? this.mask : "Loading" ); 
4046     },
4047     onLoad : function ()
4048     {
4049         this.el.unmask();
4050     },
4051     
4052
4053     /**
4054      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4055      * @param {HTMLElement} node
4056      * @return {HTMLElement} The template node
4057      */
4058     findItemFromChild : function(node){
4059         var el = this.dataName  ?
4060             this.el.child('.roo-tpl-' + this.dataName,true) :
4061             this.el.dom; 
4062         
4063         if(!node || node.parentNode == el){
4064                     return node;
4065             }
4066             var p = node.parentNode;
4067             while(p && p != el){
4068             if(p.parentNode == el){
4069                 return p;
4070             }
4071             p = p.parentNode;
4072         }
4073             return null;
4074     },
4075
4076     /** @ignore */
4077     onClick : function(e){
4078         var item = this.findItemFromChild(e.getTarget());
4079         if(item){
4080             var index = this.indexOf(item);
4081             if(this.onItemClick(item, index, e) !== false){
4082                 this.fireEvent("click", this, index, item, e);
4083             }
4084         }else{
4085             this.clearSelections();
4086         }
4087     },
4088
4089     /** @ignore */
4090     onContextMenu : function(e){
4091         var item = this.findItemFromChild(e.getTarget());
4092         if(item){
4093             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4094         }
4095     },
4096
4097     /** @ignore */
4098     onDblClick : function(e){
4099         var item = this.findItemFromChild(e.getTarget());
4100         if(item){
4101             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4102         }
4103     },
4104
4105     onItemClick : function(item, index, e)
4106     {
4107         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4108             return false;
4109         }
4110         if (this.toggleSelect) {
4111             var m = this.isSelected(item) ? 'unselect' : 'select';
4112             //Roo.log(m);
4113             var _t = this;
4114             _t[m](item, true, false);
4115             return true;
4116         }
4117         if(this.multiSelect || this.singleSelect){
4118             if(this.multiSelect && e.shiftKey && this.lastSelection){
4119                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4120             }else{
4121                 this.select(item, this.multiSelect && e.ctrlKey);
4122                 this.lastSelection = item;
4123             }
4124             
4125             if(!this.tickable){
4126                 e.preventDefault();
4127             }
4128             
4129         }
4130         return true;
4131     },
4132
4133     /**
4134      * Get the number of selected nodes.
4135      * @return {Number}
4136      */
4137     getSelectionCount : function(){
4138         return this.selections.length;
4139     },
4140
4141     /**
4142      * Get the currently selected nodes.
4143      * @return {Array} An array of HTMLElements
4144      */
4145     getSelectedNodes : function(){
4146         return this.selections;
4147     },
4148
4149     /**
4150      * Get the indexes of the selected nodes.
4151      * @return {Array}
4152      */
4153     getSelectedIndexes : function(){
4154         var indexes = [], s = this.selections;
4155         for(var i = 0, len = s.length; i < len; i++){
4156             indexes.push(s[i].nodeIndex);
4157         }
4158         return indexes;
4159     },
4160
4161     /**
4162      * Clear all selections
4163      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4164      */
4165     clearSelections : function(suppressEvent){
4166         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4167             this.cmp.elements = this.selections;
4168             this.cmp.removeClass(this.selectedClass);
4169             this.selections = [];
4170             if(!suppressEvent){
4171                 this.fireEvent("selectionchange", this, this.selections);
4172             }
4173         }
4174     },
4175
4176     /**
4177      * Returns true if the passed node is selected
4178      * @param {HTMLElement/Number} node The node or node index
4179      * @return {Boolean}
4180      */
4181     isSelected : function(node){
4182         var s = this.selections;
4183         if(s.length < 1){
4184             return false;
4185         }
4186         node = this.getNode(node);
4187         return s.indexOf(node) !== -1;
4188     },
4189
4190     /**
4191      * Selects nodes.
4192      * @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
4193      * @param {Boolean} keepExisting (optional) true to keep existing selections
4194      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4195      */
4196     select : function(nodeInfo, keepExisting, suppressEvent){
4197         if(nodeInfo instanceof Array){
4198             if(!keepExisting){
4199                 this.clearSelections(true);
4200             }
4201             for(var i = 0, len = nodeInfo.length; i < len; i++){
4202                 this.select(nodeInfo[i], true, true);
4203             }
4204             return;
4205         } 
4206         var node = this.getNode(nodeInfo);
4207         if(!node || this.isSelected(node)){
4208             return; // already selected.
4209         }
4210         if(!keepExisting){
4211             this.clearSelections(true);
4212         }
4213         
4214         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4215             Roo.fly(node).addClass(this.selectedClass);
4216             this.selections.push(node);
4217             if(!suppressEvent){
4218                 this.fireEvent("selectionchange", this, this.selections);
4219             }
4220         }
4221         
4222         
4223     },
4224       /**
4225      * Unselects nodes.
4226      * @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
4227      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4228      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4229      */
4230     unselect : function(nodeInfo, keepExisting, suppressEvent)
4231     {
4232         if(nodeInfo instanceof Array){
4233             Roo.each(this.selections, function(s) {
4234                 this.unselect(s, nodeInfo);
4235             }, this);
4236             return;
4237         }
4238         var node = this.getNode(nodeInfo);
4239         if(!node || !this.isSelected(node)){
4240             //Roo.log("not selected");
4241             return; // not selected.
4242         }
4243         // fireevent???
4244         var ns = [];
4245         Roo.each(this.selections, function(s) {
4246             if (s == node ) {
4247                 Roo.fly(node).removeClass(this.selectedClass);
4248
4249                 return;
4250             }
4251             ns.push(s);
4252         },this);
4253         
4254         this.selections= ns;
4255         this.fireEvent("selectionchange", this, this.selections);
4256     },
4257
4258     /**
4259      * Gets a template node.
4260      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4261      * @return {HTMLElement} The node or null if it wasn't found
4262      */
4263     getNode : function(nodeInfo){
4264         if(typeof nodeInfo == "string"){
4265             return document.getElementById(nodeInfo);
4266         }else if(typeof nodeInfo == "number"){
4267             return this.nodes[nodeInfo];
4268         }
4269         return nodeInfo;
4270     },
4271
4272     /**
4273      * Gets a range template nodes.
4274      * @param {Number} startIndex
4275      * @param {Number} endIndex
4276      * @return {Array} An array of nodes
4277      */
4278     getNodes : function(start, end){
4279         var ns = this.nodes;
4280         start = start || 0;
4281         end = typeof end == "undefined" ? ns.length - 1 : end;
4282         var nodes = [];
4283         if(start <= end){
4284             for(var i = start; i <= end; i++){
4285                 nodes.push(ns[i]);
4286             }
4287         } else{
4288             for(var i = start; i >= end; i--){
4289                 nodes.push(ns[i]);
4290             }
4291         }
4292         return nodes;
4293     },
4294
4295     /**
4296      * Finds the index of the passed node
4297      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4298      * @return {Number} The index of the node or -1
4299      */
4300     indexOf : function(node){
4301         node = this.getNode(node);
4302         if(typeof node.nodeIndex == "number"){
4303             return node.nodeIndex;
4304         }
4305         var ns = this.nodes;
4306         for(var i = 0, len = ns.length; i < len; i++){
4307             if(ns[i] == node){
4308                 return i;
4309             }
4310         }
4311         return -1;
4312     }
4313 });
4314 /*
4315  * Based on:
4316  * Ext JS Library 1.1.1
4317  * Copyright(c) 2006-2007, Ext JS, LLC.
4318  *
4319  * Originally Released Under LGPL - original licence link has changed is not relivant.
4320  *
4321  * Fork - LGPL
4322  * <script type="text/javascript">
4323  */
4324
4325 /**
4326  * @class Roo.JsonView
4327  * @extends Roo.View
4328  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4329 <pre><code>
4330 var view = new Roo.JsonView({
4331     container: "my-element",
4332     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4333     multiSelect: true, 
4334     jsonRoot: "data" 
4335 });
4336
4337 // listen for node click?
4338 view.on("click", function(vw, index, node, e){
4339     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4340 });
4341
4342 // direct load of JSON data
4343 view.load("foobar.php");
4344
4345 // Example from my blog list
4346 var tpl = new Roo.Template(
4347     '&lt;div class="entry"&gt;' +
4348     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4349     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4350     "&lt;/div&gt;&lt;hr /&gt;"
4351 );
4352
4353 var moreView = new Roo.JsonView({
4354     container :  "entry-list", 
4355     template : tpl,
4356     jsonRoot: "posts"
4357 });
4358 moreView.on("beforerender", this.sortEntries, this);
4359 moreView.load({
4360     url: "/blog/get-posts.php",
4361     params: "allposts=true",
4362     text: "Loading Blog Entries..."
4363 });
4364 </code></pre>
4365
4366 * Note: old code is supported with arguments : (container, template, config)
4367
4368
4369  * @constructor
4370  * Create a new JsonView
4371  * 
4372  * @param {Object} config The config object
4373  * 
4374  */
4375 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4376     
4377     
4378     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4379
4380     var um = this.el.getUpdateManager();
4381     um.setRenderer(this);
4382     um.on("update", this.onLoad, this);
4383     um.on("failure", this.onLoadException, this);
4384
4385     /**
4386      * @event beforerender
4387      * Fires before rendering of the downloaded JSON data.
4388      * @param {Roo.JsonView} this
4389      * @param {Object} data The JSON data loaded
4390      */
4391     /**
4392      * @event load
4393      * Fires when data is loaded.
4394      * @param {Roo.JsonView} this
4395      * @param {Object} data The JSON data loaded
4396      * @param {Object} response The raw Connect response object
4397      */
4398     /**
4399      * @event loadexception
4400      * Fires when loading fails.
4401      * @param {Roo.JsonView} this
4402      * @param {Object} response The raw Connect response object
4403      */
4404     this.addEvents({
4405         'beforerender' : true,
4406         'load' : true,
4407         'loadexception' : true
4408     });
4409 };
4410 Roo.extend(Roo.JsonView, Roo.View, {
4411     /**
4412      * @type {String} The root property in the loaded JSON object that contains the data
4413      */
4414     jsonRoot : "",
4415
4416     /**
4417      * Refreshes the view.
4418      */
4419     refresh : function(){
4420         this.clearSelections();
4421         this.el.update("");
4422         var html = [];
4423         var o = this.jsonData;
4424         if(o && o.length > 0){
4425             for(var i = 0, len = o.length; i < len; i++){
4426                 var data = this.prepareData(o[i], i, o);
4427                 html[html.length] = this.tpl.apply(data);
4428             }
4429         }else{
4430             html.push(this.emptyText);
4431         }
4432         this.el.update(html.join(""));
4433         this.nodes = this.el.dom.childNodes;
4434         this.updateIndexes(0);
4435     },
4436
4437     /**
4438      * 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.
4439      * @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:
4440      <pre><code>
4441      view.load({
4442          url: "your-url.php",
4443          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4444          callback: yourFunction,
4445          scope: yourObject, //(optional scope)
4446          discardUrl: false,
4447          nocache: false,
4448          text: "Loading...",
4449          timeout: 30,
4450          scripts: false
4451      });
4452      </code></pre>
4453      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4454      * 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.
4455      * @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}
4456      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4457      * @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.
4458      */
4459     load : function(){
4460         var um = this.el.getUpdateManager();
4461         um.update.apply(um, arguments);
4462     },
4463
4464     // note - render is a standard framework call...
4465     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4466     render : function(el, response){
4467         
4468         this.clearSelections();
4469         this.el.update("");
4470         var o;
4471         try{
4472             if (response != '') {
4473                 o = Roo.util.JSON.decode(response.responseText);
4474                 if(this.jsonRoot){
4475                     
4476                     o = o[this.jsonRoot];
4477                 }
4478             }
4479         } catch(e){
4480         }
4481         /**
4482          * The current JSON data or null
4483          */
4484         this.jsonData = o;
4485         this.beforeRender();
4486         this.refresh();
4487     },
4488
4489 /**
4490  * Get the number of records in the current JSON dataset
4491  * @return {Number}
4492  */
4493     getCount : function(){
4494         return this.jsonData ? this.jsonData.length : 0;
4495     },
4496
4497 /**
4498  * Returns the JSON object for the specified node(s)
4499  * @param {HTMLElement/Array} node The node or an array of nodes
4500  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4501  * you get the JSON object for the node
4502  */
4503     getNodeData : function(node){
4504         if(node instanceof Array){
4505             var data = [];
4506             for(var i = 0, len = node.length; i < len; i++){
4507                 data.push(this.getNodeData(node[i]));
4508             }
4509             return data;
4510         }
4511         return this.jsonData[this.indexOf(node)] || null;
4512     },
4513
4514     beforeRender : function(){
4515         this.snapshot = this.jsonData;
4516         if(this.sortInfo){
4517             this.sort.apply(this, this.sortInfo);
4518         }
4519         this.fireEvent("beforerender", this, this.jsonData);
4520     },
4521
4522     onLoad : function(el, o){
4523         this.fireEvent("load", this, this.jsonData, o);
4524     },
4525
4526     onLoadException : function(el, o){
4527         this.fireEvent("loadexception", this, o);
4528     },
4529
4530 /**
4531  * Filter the data by a specific property.
4532  * @param {String} property A property on your JSON objects
4533  * @param {String/RegExp} value Either string that the property values
4534  * should start with, or a RegExp to test against the property
4535  */
4536     filter : function(property, value){
4537         if(this.jsonData){
4538             var data = [];
4539             var ss = this.snapshot;
4540             if(typeof value == "string"){
4541                 var vlen = value.length;
4542                 if(vlen == 0){
4543                     this.clearFilter();
4544                     return;
4545                 }
4546                 value = value.toLowerCase();
4547                 for(var i = 0, len = ss.length; i < len; i++){
4548                     var o = ss[i];
4549                     if(o[property].substr(0, vlen).toLowerCase() == value){
4550                         data.push(o);
4551                     }
4552                 }
4553             } else if(value.exec){ // regex?
4554                 for(var i = 0, len = ss.length; i < len; i++){
4555                     var o = ss[i];
4556                     if(value.test(o[property])){
4557                         data.push(o);
4558                     }
4559                 }
4560             } else{
4561                 return;
4562             }
4563             this.jsonData = data;
4564             this.refresh();
4565         }
4566     },
4567
4568 /**
4569  * Filter by a function. The passed function will be called with each
4570  * object in the current dataset. If the function returns true the value is kept,
4571  * otherwise it is filtered.
4572  * @param {Function} fn
4573  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4574  */
4575     filterBy : function(fn, scope){
4576         if(this.jsonData){
4577             var data = [];
4578             var ss = this.snapshot;
4579             for(var i = 0, len = ss.length; i < len; i++){
4580                 var o = ss[i];
4581                 if(fn.call(scope || this, o)){
4582                     data.push(o);
4583                 }
4584             }
4585             this.jsonData = data;
4586             this.refresh();
4587         }
4588     },
4589
4590 /**
4591  * Clears the current filter.
4592  */
4593     clearFilter : function(){
4594         if(this.snapshot && this.jsonData != this.snapshot){
4595             this.jsonData = this.snapshot;
4596             this.refresh();
4597         }
4598     },
4599
4600
4601 /**
4602  * Sorts the data for this view and refreshes it.
4603  * @param {String} property A property on your JSON objects to sort on
4604  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4605  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4606  */
4607     sort : function(property, dir, sortType){
4608         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4609         if(this.jsonData){
4610             var p = property;
4611             var dsc = dir && dir.toLowerCase() == "desc";
4612             var f = function(o1, o2){
4613                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4614                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4615                 ;
4616                 if(v1 < v2){
4617                     return dsc ? +1 : -1;
4618                 } else if(v1 > v2){
4619                     return dsc ? -1 : +1;
4620                 } else{
4621                     return 0;
4622                 }
4623             };
4624             this.jsonData.sort(f);
4625             this.refresh();
4626             if(this.jsonData != this.snapshot){
4627                 this.snapshot.sort(f);
4628             }
4629         }
4630     }
4631 });/*
4632  * Based on:
4633  * Ext JS Library 1.1.1
4634  * Copyright(c) 2006-2007, Ext JS, LLC.
4635  *
4636  * Originally Released Under LGPL - original licence link has changed is not relivant.
4637  *
4638  * Fork - LGPL
4639  * <script type="text/javascript">
4640  */
4641  
4642
4643 /**
4644  * @class Roo.ColorPalette
4645  * @extends Roo.Component
4646  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4647  * Here's an example of typical usage:
4648  * <pre><code>
4649 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4650 cp.render('my-div');
4651
4652 cp.on('select', function(palette, selColor){
4653     // do something with selColor
4654 });
4655 </code></pre>
4656  * @constructor
4657  * Create a new ColorPalette
4658  * @param {Object} config The config object
4659  */
4660 Roo.ColorPalette = function(config){
4661     Roo.ColorPalette.superclass.constructor.call(this, config);
4662     this.addEvents({
4663         /**
4664              * @event select
4665              * Fires when a color is selected
4666              * @param {ColorPalette} this
4667              * @param {String} color The 6-digit color hex code (without the # symbol)
4668              */
4669         select: true
4670     });
4671
4672     if(this.handler){
4673         this.on("select", this.handler, this.scope, true);
4674     }
4675 };
4676 Roo.extend(Roo.ColorPalette, Roo.Component, {
4677     /**
4678      * @cfg {String} itemCls
4679      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4680      */
4681     itemCls : "x-color-palette",
4682     /**
4683      * @cfg {String} value
4684      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4685      * the hex codes are case-sensitive.
4686      */
4687     value : null,
4688     clickEvent:'click',
4689     // private
4690     ctype: "Roo.ColorPalette",
4691
4692     /**
4693      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4694      */
4695     allowReselect : false,
4696
4697     /**
4698      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4699      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4700      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4701      * of colors with the width setting until the box is symmetrical.</p>
4702      * <p>You can override individual colors if needed:</p>
4703      * <pre><code>
4704 var cp = new Roo.ColorPalette();
4705 cp.colors[0] = "FF0000";  // change the first box to red
4706 </code></pre>
4707
4708 Or you can provide a custom array of your own for complete control:
4709 <pre><code>
4710 var cp = new Roo.ColorPalette();
4711 cp.colors = ["000000", "993300", "333300"];
4712 </code></pre>
4713      * @type Array
4714      */
4715     colors : [
4716         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4717         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4718         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4719         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4720         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4721     ],
4722
4723     // private
4724     onRender : function(container, position){
4725         var t = new Roo.MasterTemplate(
4726             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4727         );
4728         var c = this.colors;
4729         for(var i = 0, len = c.length; i < len; i++){
4730             t.add([c[i]]);
4731         }
4732         var el = document.createElement("div");
4733         el.className = this.itemCls;
4734         t.overwrite(el);
4735         container.dom.insertBefore(el, position);
4736         this.el = Roo.get(el);
4737         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4738         if(this.clickEvent != 'click'){
4739             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4740         }
4741     },
4742
4743     // private
4744     afterRender : function(){
4745         Roo.ColorPalette.superclass.afterRender.call(this);
4746         if(this.value){
4747             var s = this.value;
4748             this.value = null;
4749             this.select(s);
4750         }
4751     },
4752
4753     // private
4754     handleClick : function(e, t){
4755         e.preventDefault();
4756         if(!this.disabled){
4757             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4758             this.select(c.toUpperCase());
4759         }
4760     },
4761
4762     /**
4763      * Selects the specified color in the palette (fires the select event)
4764      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4765      */
4766     select : function(color){
4767         color = color.replace("#", "");
4768         if(color != this.value || this.allowReselect){
4769             var el = this.el;
4770             if(this.value){
4771                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4772             }
4773             el.child("a.color-"+color).addClass("x-color-palette-sel");
4774             this.value = color;
4775             this.fireEvent("select", this, color);
4776         }
4777     }
4778 });/*
4779  * Based on:
4780  * Ext JS Library 1.1.1
4781  * Copyright(c) 2006-2007, Ext JS, LLC.
4782  *
4783  * Originally Released Under LGPL - original licence link has changed is not relivant.
4784  *
4785  * Fork - LGPL
4786  * <script type="text/javascript">
4787  */
4788  
4789 /**
4790  * @class Roo.DatePicker
4791  * @extends Roo.Component
4792  * Simple date picker class.
4793  * @constructor
4794  * Create a new DatePicker
4795  * @param {Object} config The config object
4796  */
4797 Roo.DatePicker = function(config){
4798     Roo.DatePicker.superclass.constructor.call(this, config);
4799
4800     this.value = config && config.value ?
4801                  config.value.clearTime() : new Date().clearTime();
4802
4803     this.addEvents({
4804         /**
4805              * @event select
4806              * Fires when a date is selected
4807              * @param {DatePicker} this
4808              * @param {Date} date The selected date
4809              */
4810         'select': true,
4811         /**
4812              * @event monthchange
4813              * Fires when the displayed month changes 
4814              * @param {DatePicker} this
4815              * @param {Date} date The selected month
4816              */
4817         'monthchange': true
4818     });
4819
4820     if(this.handler){
4821         this.on("select", this.handler,  this.scope || this);
4822     }
4823     // build the disabledDatesRE
4824     if(!this.disabledDatesRE && this.disabledDates){
4825         var dd = this.disabledDates;
4826         var re = "(?:";
4827         for(var i = 0; i < dd.length; i++){
4828             re += dd[i];
4829             if(i != dd.length-1) {
4830                 re += "|";
4831             }
4832         }
4833         this.disabledDatesRE = new RegExp(re + ")");
4834     }
4835 };
4836
4837 Roo.extend(Roo.DatePicker, Roo.Component, {
4838     /**
4839      * @cfg {String} todayText
4840      * The text to display on the button that selects the current date (defaults to "Today")
4841      */
4842     todayText : "Today",
4843     /**
4844      * @cfg {String} okText
4845      * The text to display on the ok button
4846      */
4847     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4848     /**
4849      * @cfg {String} cancelText
4850      * The text to display on the cancel button
4851      */
4852     cancelText : "Cancel",
4853     /**
4854      * @cfg {String} todayTip
4855      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4856      */
4857     todayTip : "{0} (Spacebar)",
4858     /**
4859      * @cfg {Date} minDate
4860      * Minimum allowable date (JavaScript date object, defaults to null)
4861      */
4862     minDate : null,
4863     /**
4864      * @cfg {Date} maxDate
4865      * Maximum allowable date (JavaScript date object, defaults to null)
4866      */
4867     maxDate : null,
4868     /**
4869      * @cfg {String} minText
4870      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4871      */
4872     minText : "This date is before the minimum date",
4873     /**
4874      * @cfg {String} maxText
4875      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4876      */
4877     maxText : "This date is after the maximum date",
4878     /**
4879      * @cfg {String} format
4880      * The default date format string which can be overriden for localization support.  The format must be
4881      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4882      */
4883     format : "m/d/y",
4884     /**
4885      * @cfg {Array} disabledDays
4886      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4887      */
4888     disabledDays : null,
4889     /**
4890      * @cfg {String} disabledDaysText
4891      * The tooltip to display when the date falls on a disabled day (defaults to "")
4892      */
4893     disabledDaysText : "",
4894     /**
4895      * @cfg {RegExp} disabledDatesRE
4896      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4897      */
4898     disabledDatesRE : null,
4899     /**
4900      * @cfg {String} disabledDatesText
4901      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4902      */
4903     disabledDatesText : "",
4904     /**
4905      * @cfg {Boolean} constrainToViewport
4906      * True to constrain the date picker to the viewport (defaults to true)
4907      */
4908     constrainToViewport : true,
4909     /**
4910      * @cfg {Array} monthNames
4911      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4912      */
4913     monthNames : Date.monthNames,
4914     /**
4915      * @cfg {Array} dayNames
4916      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4917      */
4918     dayNames : Date.dayNames,
4919     /**
4920      * @cfg {String} nextText
4921      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4922      */
4923     nextText: 'Next Month (Control+Right)',
4924     /**
4925      * @cfg {String} prevText
4926      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4927      */
4928     prevText: 'Previous Month (Control+Left)',
4929     /**
4930      * @cfg {String} monthYearText
4931      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4932      */
4933     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4934     /**
4935      * @cfg {Number} startDay
4936      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4937      */
4938     startDay : 0,
4939     /**
4940      * @cfg {Bool} showClear
4941      * Show a clear button (usefull for date form elements that can be blank.)
4942      */
4943     
4944     showClear: false,
4945     
4946     /**
4947      * Sets the value of the date field
4948      * @param {Date} value The date to set
4949      */
4950     setValue : function(value){
4951         var old = this.value;
4952         
4953         if (typeof(value) == 'string') {
4954          
4955             value = Date.parseDate(value, this.format);
4956         }
4957         if (!value) {
4958             value = new Date();
4959         }
4960         
4961         this.value = value.clearTime(true);
4962         if(this.el){
4963             this.update(this.value);
4964         }
4965     },
4966
4967     /**
4968      * Gets the current selected value of the date field
4969      * @return {Date} The selected date
4970      */
4971     getValue : function(){
4972         return this.value;
4973     },
4974
4975     // private
4976     focus : function(){
4977         if(this.el){
4978             this.update(this.activeDate);
4979         }
4980     },
4981
4982     // privateval
4983     onRender : function(container, position){
4984         
4985         var m = [
4986              '<table cellspacing="0">',
4987                 '<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>',
4988                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4989         var dn = this.dayNames;
4990         for(var i = 0; i < 7; i++){
4991             var d = this.startDay+i;
4992             if(d > 6){
4993                 d = d-7;
4994             }
4995             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4996         }
4997         m[m.length] = "</tr></thead><tbody><tr>";
4998         for(var i = 0; i < 42; i++) {
4999             if(i % 7 == 0 && i != 0){
5000                 m[m.length] = "</tr><tr>";
5001             }
5002             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5003         }
5004         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5005             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5006
5007         var el = document.createElement("div");
5008         el.className = "x-date-picker";
5009         el.innerHTML = m.join("");
5010
5011         container.dom.insertBefore(el, position);
5012
5013         this.el = Roo.get(el);
5014         this.eventEl = Roo.get(el.firstChild);
5015
5016         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5017             handler: this.showPrevMonth,
5018             scope: this,
5019             preventDefault:true,
5020             stopDefault:true
5021         });
5022
5023         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5024             handler: this.showNextMonth,
5025             scope: this,
5026             preventDefault:true,
5027             stopDefault:true
5028         });
5029
5030         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5031
5032         this.monthPicker = this.el.down('div.x-date-mp');
5033         this.monthPicker.enableDisplayMode('block');
5034         
5035         var kn = new Roo.KeyNav(this.eventEl, {
5036             "left" : function(e){
5037                 e.ctrlKey ?
5038                     this.showPrevMonth() :
5039                     this.update(this.activeDate.add("d", -1));
5040             },
5041
5042             "right" : function(e){
5043                 e.ctrlKey ?
5044                     this.showNextMonth() :
5045                     this.update(this.activeDate.add("d", 1));
5046             },
5047
5048             "up" : function(e){
5049                 e.ctrlKey ?
5050                     this.showNextYear() :
5051                     this.update(this.activeDate.add("d", -7));
5052             },
5053
5054             "down" : function(e){
5055                 e.ctrlKey ?
5056                     this.showPrevYear() :
5057                     this.update(this.activeDate.add("d", 7));
5058             },
5059
5060             "pageUp" : function(e){
5061                 this.showNextMonth();
5062             },
5063
5064             "pageDown" : function(e){
5065                 this.showPrevMonth();
5066             },
5067
5068             "enter" : function(e){
5069                 e.stopPropagation();
5070                 return true;
5071             },
5072
5073             scope : this
5074         });
5075
5076         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5077
5078         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5079
5080         this.el.unselectable();
5081         
5082         this.cells = this.el.select("table.x-date-inner tbody td");
5083         this.textNodes = this.el.query("table.x-date-inner tbody span");
5084
5085         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5086             text: "&#160;",
5087             tooltip: this.monthYearText
5088         });
5089
5090         this.mbtn.on('click', this.showMonthPicker, this);
5091         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5092
5093
5094         var today = (new Date()).dateFormat(this.format);
5095         
5096         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5097         if (this.showClear) {
5098             baseTb.add( new Roo.Toolbar.Fill());
5099         }
5100         baseTb.add({
5101             text: String.format(this.todayText, today),
5102             tooltip: String.format(this.todayTip, today),
5103             handler: this.selectToday,
5104             scope: this
5105         });
5106         
5107         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5108             
5109         //});
5110         if (this.showClear) {
5111             
5112             baseTb.add( new Roo.Toolbar.Fill());
5113             baseTb.add({
5114                 text: '&#160;',
5115                 cls: 'x-btn-icon x-btn-clear',
5116                 handler: function() {
5117                     //this.value = '';
5118                     this.fireEvent("select", this, '');
5119                 },
5120                 scope: this
5121             });
5122         }
5123         
5124         
5125         if(Roo.isIE){
5126             this.el.repaint();
5127         }
5128         this.update(this.value);
5129     },
5130
5131     createMonthPicker : function(){
5132         if(!this.monthPicker.dom.firstChild){
5133             var buf = ['<table border="0" cellspacing="0">'];
5134             for(var i = 0; i < 6; i++){
5135                 buf.push(
5136                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5137                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5138                     i == 0 ?
5139                     '<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>' :
5140                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5141                 );
5142             }
5143             buf.push(
5144                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5145                     this.okText,
5146                     '</button><button type="button" class="x-date-mp-cancel">',
5147                     this.cancelText,
5148                     '</button></td></tr>',
5149                 '</table>'
5150             );
5151             this.monthPicker.update(buf.join(''));
5152             this.monthPicker.on('click', this.onMonthClick, this);
5153             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5154
5155             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5156             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5157
5158             this.mpMonths.each(function(m, a, i){
5159                 i += 1;
5160                 if((i%2) == 0){
5161                     m.dom.xmonth = 5 + Math.round(i * .5);
5162                 }else{
5163                     m.dom.xmonth = Math.round((i-1) * .5);
5164                 }
5165             });
5166         }
5167     },
5168
5169     showMonthPicker : function(){
5170         this.createMonthPicker();
5171         var size = this.el.getSize();
5172         this.monthPicker.setSize(size);
5173         this.monthPicker.child('table').setSize(size);
5174
5175         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5176         this.updateMPMonth(this.mpSelMonth);
5177         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5178         this.updateMPYear(this.mpSelYear);
5179
5180         this.monthPicker.slideIn('t', {duration:.2});
5181     },
5182
5183     updateMPYear : function(y){
5184         this.mpyear = y;
5185         var ys = this.mpYears.elements;
5186         for(var i = 1; i <= 10; i++){
5187             var td = ys[i-1], y2;
5188             if((i%2) == 0){
5189                 y2 = y + Math.round(i * .5);
5190                 td.firstChild.innerHTML = y2;
5191                 td.xyear = y2;
5192             }else{
5193                 y2 = y - (5-Math.round(i * .5));
5194                 td.firstChild.innerHTML = y2;
5195                 td.xyear = y2;
5196             }
5197             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5198         }
5199     },
5200
5201     updateMPMonth : function(sm){
5202         this.mpMonths.each(function(m, a, i){
5203             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5204         });
5205     },
5206
5207     selectMPMonth: function(m){
5208         
5209     },
5210
5211     onMonthClick : function(e, t){
5212         e.stopEvent();
5213         var el = new Roo.Element(t), pn;
5214         if(el.is('button.x-date-mp-cancel')){
5215             this.hideMonthPicker();
5216         }
5217         else if(el.is('button.x-date-mp-ok')){
5218             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5219             this.hideMonthPicker();
5220         }
5221         else if(pn = el.up('td.x-date-mp-month', 2)){
5222             this.mpMonths.removeClass('x-date-mp-sel');
5223             pn.addClass('x-date-mp-sel');
5224             this.mpSelMonth = pn.dom.xmonth;
5225         }
5226         else if(pn = el.up('td.x-date-mp-year', 2)){
5227             this.mpYears.removeClass('x-date-mp-sel');
5228             pn.addClass('x-date-mp-sel');
5229             this.mpSelYear = pn.dom.xyear;
5230         }
5231         else if(el.is('a.x-date-mp-prev')){
5232             this.updateMPYear(this.mpyear-10);
5233         }
5234         else if(el.is('a.x-date-mp-next')){
5235             this.updateMPYear(this.mpyear+10);
5236         }
5237     },
5238
5239     onMonthDblClick : function(e, t){
5240         e.stopEvent();
5241         var el = new Roo.Element(t), pn;
5242         if(pn = el.up('td.x-date-mp-month', 2)){
5243             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5244             this.hideMonthPicker();
5245         }
5246         else if(pn = el.up('td.x-date-mp-year', 2)){
5247             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5248             this.hideMonthPicker();
5249         }
5250     },
5251
5252     hideMonthPicker : function(disableAnim){
5253         if(this.monthPicker){
5254             if(disableAnim === true){
5255                 this.monthPicker.hide();
5256             }else{
5257                 this.monthPicker.slideOut('t', {duration:.2});
5258             }
5259         }
5260     },
5261
5262     // private
5263     showPrevMonth : function(e){
5264         this.update(this.activeDate.add("mo", -1));
5265     },
5266
5267     // private
5268     showNextMonth : function(e){
5269         this.update(this.activeDate.add("mo", 1));
5270     },
5271
5272     // private
5273     showPrevYear : function(){
5274         this.update(this.activeDate.add("y", -1));
5275     },
5276
5277     // private
5278     showNextYear : function(){
5279         this.update(this.activeDate.add("y", 1));
5280     },
5281
5282     // private
5283     handleMouseWheel : function(e){
5284         var delta = e.getWheelDelta();
5285         if(delta > 0){
5286             this.showPrevMonth();
5287             e.stopEvent();
5288         } else if(delta < 0){
5289             this.showNextMonth();
5290             e.stopEvent();
5291         }
5292     },
5293
5294     // private
5295     handleDateClick : function(e, t){
5296         e.stopEvent();
5297         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5298             this.setValue(new Date(t.dateValue));
5299             this.fireEvent("select", this, this.value);
5300         }
5301     },
5302
5303     // private
5304     selectToday : function(){
5305         this.setValue(new Date().clearTime());
5306         this.fireEvent("select", this, this.value);
5307     },
5308
5309     // private
5310     update : function(date)
5311     {
5312         var vd = this.activeDate;
5313         this.activeDate = date;
5314         if(vd && this.el){
5315             var t = date.getTime();
5316             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5317                 this.cells.removeClass("x-date-selected");
5318                 this.cells.each(function(c){
5319                    if(c.dom.firstChild.dateValue == t){
5320                        c.addClass("x-date-selected");
5321                        setTimeout(function(){
5322                             try{c.dom.firstChild.focus();}catch(e){}
5323                        }, 50);
5324                        return false;
5325                    }
5326                 });
5327                 return;
5328             }
5329         }
5330         
5331         var days = date.getDaysInMonth();
5332         var firstOfMonth = date.getFirstDateOfMonth();
5333         var startingPos = firstOfMonth.getDay()-this.startDay;
5334
5335         if(startingPos <= this.startDay){
5336             startingPos += 7;
5337         }
5338
5339         var pm = date.add("mo", -1);
5340         var prevStart = pm.getDaysInMonth()-startingPos;
5341
5342         var cells = this.cells.elements;
5343         var textEls = this.textNodes;
5344         days += startingPos;
5345
5346         // convert everything to numbers so it's fast
5347         var day = 86400000;
5348         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5349         var today = new Date().clearTime().getTime();
5350         var sel = date.clearTime().getTime();
5351         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5352         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5353         var ddMatch = this.disabledDatesRE;
5354         var ddText = this.disabledDatesText;
5355         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5356         var ddaysText = this.disabledDaysText;
5357         var format = this.format;
5358
5359         var setCellClass = function(cal, cell){
5360             cell.title = "";
5361             var t = d.getTime();
5362             cell.firstChild.dateValue = t;
5363             if(t == today){
5364                 cell.className += " x-date-today";
5365                 cell.title = cal.todayText;
5366             }
5367             if(t == sel){
5368                 cell.className += " x-date-selected";
5369                 setTimeout(function(){
5370                     try{cell.firstChild.focus();}catch(e){}
5371                 }, 50);
5372             }
5373             // disabling
5374             if(t < min) {
5375                 cell.className = " x-date-disabled";
5376                 cell.title = cal.minText;
5377                 return;
5378             }
5379             if(t > max) {
5380                 cell.className = " x-date-disabled";
5381                 cell.title = cal.maxText;
5382                 return;
5383             }
5384             if(ddays){
5385                 if(ddays.indexOf(d.getDay()) != -1){
5386                     cell.title = ddaysText;
5387                     cell.className = " x-date-disabled";
5388                 }
5389             }
5390             if(ddMatch && format){
5391                 var fvalue = d.dateFormat(format);
5392                 if(ddMatch.test(fvalue)){
5393                     cell.title = ddText.replace("%0", fvalue);
5394                     cell.className = " x-date-disabled";
5395                 }
5396             }
5397         };
5398
5399         var i = 0;
5400         for(; i < startingPos; i++) {
5401             textEls[i].innerHTML = (++prevStart);
5402             d.setDate(d.getDate()+1);
5403             cells[i].className = "x-date-prevday";
5404             setCellClass(this, cells[i]);
5405         }
5406         for(; i < days; i++){
5407             intDay = i - startingPos + 1;
5408             textEls[i].innerHTML = (intDay);
5409             d.setDate(d.getDate()+1);
5410             cells[i].className = "x-date-active";
5411             setCellClass(this, cells[i]);
5412         }
5413         var extraDays = 0;
5414         for(; i < 42; i++) {
5415              textEls[i].innerHTML = (++extraDays);
5416              d.setDate(d.getDate()+1);
5417              cells[i].className = "x-date-nextday";
5418              setCellClass(this, cells[i]);
5419         }
5420
5421         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5422         this.fireEvent('monthchange', this, date);
5423         
5424         if(!this.internalRender){
5425             var main = this.el.dom.firstChild;
5426             var w = main.offsetWidth;
5427             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5428             Roo.fly(main).setWidth(w);
5429             this.internalRender = true;
5430             // opera does not respect the auto grow header center column
5431             // then, after it gets a width opera refuses to recalculate
5432             // without a second pass
5433             if(Roo.isOpera && !this.secondPass){
5434                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5435                 this.secondPass = true;
5436                 this.update.defer(10, this, [date]);
5437             }
5438         }
5439         
5440         
5441     }
5442 });        /*
5443  * Based on:
5444  * Ext JS Library 1.1.1
5445  * Copyright(c) 2006-2007, Ext JS, LLC.
5446  *
5447  * Originally Released Under LGPL - original licence link has changed is not relivant.
5448  *
5449  * Fork - LGPL
5450  * <script type="text/javascript">
5451  */
5452 /**
5453  * @class Roo.TabPanel
5454  * @extends Roo.util.Observable
5455  * A lightweight tab container.
5456  * <br><br>
5457  * Usage:
5458  * <pre><code>
5459 // basic tabs 1, built from existing content
5460 var tabs = new Roo.TabPanel("tabs1");
5461 tabs.addTab("script", "View Script");
5462 tabs.addTab("markup", "View Markup");
5463 tabs.activate("script");
5464
5465 // more advanced tabs, built from javascript
5466 var jtabs = new Roo.TabPanel("jtabs");
5467 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5468
5469 // set up the UpdateManager
5470 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5471 var updater = tab2.getUpdateManager();
5472 updater.setDefaultUrl("ajax1.htm");
5473 tab2.on('activate', updater.refresh, updater, true);
5474
5475 // Use setUrl for Ajax loading
5476 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5477 tab3.setUrl("ajax2.htm", null, true);
5478
5479 // Disabled tab
5480 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5481 tab4.disable();
5482
5483 jtabs.activate("jtabs-1");
5484  * </code></pre>
5485  * @constructor
5486  * Create a new TabPanel.
5487  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5488  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5489  */
5490 Roo.TabPanel = function(container, config){
5491     /**
5492     * The container element for this TabPanel.
5493     * @type Roo.Element
5494     */
5495     this.el = Roo.get(container, true);
5496     if(config){
5497         if(typeof config == "boolean"){
5498             this.tabPosition = config ? "bottom" : "top";
5499         }else{
5500             Roo.apply(this, config);
5501         }
5502     }
5503     if(this.tabPosition == "bottom"){
5504         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5505         this.el.addClass("x-tabs-bottom");
5506     }
5507     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5508     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5509     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5510     if(Roo.isIE){
5511         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5512     }
5513     if(this.tabPosition != "bottom"){
5514         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5515          * @type Roo.Element
5516          */
5517         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5518         this.el.addClass("x-tabs-top");
5519     }
5520     this.items = [];
5521
5522     this.bodyEl.setStyle("position", "relative");
5523
5524     this.active = null;
5525     this.activateDelegate = this.activate.createDelegate(this);
5526
5527     this.addEvents({
5528         /**
5529          * @event tabchange
5530          * Fires when the active tab changes
5531          * @param {Roo.TabPanel} this
5532          * @param {Roo.TabPanelItem} activePanel The new active tab
5533          */
5534         "tabchange": true,
5535         /**
5536          * @event beforetabchange
5537          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5538          * @param {Roo.TabPanel} this
5539          * @param {Object} e Set cancel to true on this object to cancel the tab change
5540          * @param {Roo.TabPanelItem} tab The tab being changed to
5541          */
5542         "beforetabchange" : true
5543     });
5544
5545     Roo.EventManager.onWindowResize(this.onResize, this);
5546     this.cpad = this.el.getPadding("lr");
5547     this.hiddenCount = 0;
5548
5549
5550     // toolbar on the tabbar support...
5551     if (this.toolbar) {
5552         var tcfg = this.toolbar;
5553         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5554         this.toolbar = new Roo.Toolbar(tcfg);
5555         if (Roo.isSafari) {
5556             var tbl = tcfg.container.child('table', true);
5557             tbl.setAttribute('width', '100%');
5558         }
5559         
5560     }
5561    
5562
5563
5564     Roo.TabPanel.superclass.constructor.call(this);
5565 };
5566
5567 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5568     /*
5569      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5570      */
5571     tabPosition : "top",
5572     /*
5573      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5574      */
5575     currentTabWidth : 0,
5576     /*
5577      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5578      */
5579     minTabWidth : 40,
5580     /*
5581      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5582      */
5583     maxTabWidth : 250,
5584     /*
5585      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5586      */
5587     preferredTabWidth : 175,
5588     /*
5589      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5590      */
5591     resizeTabs : false,
5592     /*
5593      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5594      */
5595     monitorResize : true,
5596     /*
5597      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5598      */
5599     toolbar : false,
5600
5601     /**
5602      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5603      * @param {String} id The id of the div to use <b>or create</b>
5604      * @param {String} text The text for the tab
5605      * @param {String} content (optional) Content to put in the TabPanelItem body
5606      * @param {Boolean} closable (optional) True to create a close icon on the tab
5607      * @return {Roo.TabPanelItem} The created TabPanelItem
5608      */
5609     addTab : function(id, text, content, closable){
5610         var item = new Roo.TabPanelItem(this, id, text, closable);
5611         this.addTabItem(item);
5612         if(content){
5613             item.setContent(content);
5614         }
5615         return item;
5616     },
5617
5618     /**
5619      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5620      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5621      * @return {Roo.TabPanelItem}
5622      */
5623     getTab : function(id){
5624         return this.items[id];
5625     },
5626
5627     /**
5628      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5629      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5630      */
5631     hideTab : function(id){
5632         var t = this.items[id];
5633         if(!t.isHidden()){
5634            t.setHidden(true);
5635            this.hiddenCount++;
5636            this.autoSizeTabs();
5637         }
5638     },
5639
5640     /**
5641      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5642      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5643      */
5644     unhideTab : function(id){
5645         var t = this.items[id];
5646         if(t.isHidden()){
5647            t.setHidden(false);
5648            this.hiddenCount--;
5649            this.autoSizeTabs();
5650         }
5651     },
5652
5653     /**
5654      * Adds an existing {@link Roo.TabPanelItem}.
5655      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5656      */
5657     addTabItem : function(item){
5658         this.items[item.id] = item;
5659         this.items.push(item);
5660         if(this.resizeTabs){
5661            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5662            this.autoSizeTabs();
5663         }else{
5664             item.autoSize();
5665         }
5666     },
5667
5668     /**
5669      * Removes a {@link Roo.TabPanelItem}.
5670      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5671      */
5672     removeTab : function(id){
5673         var items = this.items;
5674         var tab = items[id];
5675         if(!tab) { return; }
5676         var index = items.indexOf(tab);
5677         if(this.active == tab && items.length > 1){
5678             var newTab = this.getNextAvailable(index);
5679             if(newTab) {
5680                 newTab.activate();
5681             }
5682         }
5683         this.stripEl.dom.removeChild(tab.pnode.dom);
5684         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5685             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5686         }
5687         items.splice(index, 1);
5688         delete this.items[tab.id];
5689         tab.fireEvent("close", tab);
5690         tab.purgeListeners();
5691         this.autoSizeTabs();
5692     },
5693
5694     getNextAvailable : function(start){
5695         var items = this.items;
5696         var index = start;
5697         // look for a next tab that will slide over to
5698         // replace the one being removed
5699         while(index < items.length){
5700             var item = items[++index];
5701             if(item && !item.isHidden()){
5702                 return item;
5703             }
5704         }
5705         // if one isn't found select the previous tab (on the left)
5706         index = start;
5707         while(index >= 0){
5708             var item = items[--index];
5709             if(item && !item.isHidden()){
5710                 return item;
5711             }
5712         }
5713         return null;
5714     },
5715
5716     /**
5717      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5718      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5719      */
5720     disableTab : function(id){
5721         var tab = this.items[id];
5722         if(tab && this.active != tab){
5723             tab.disable();
5724         }
5725     },
5726
5727     /**
5728      * Enables a {@link Roo.TabPanelItem} that is disabled.
5729      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5730      */
5731     enableTab : function(id){
5732         var tab = this.items[id];
5733         tab.enable();
5734     },
5735
5736     /**
5737      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5738      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5739      * @return {Roo.TabPanelItem} The TabPanelItem.
5740      */
5741     activate : function(id){
5742         var tab = this.items[id];
5743         if(!tab){
5744             return null;
5745         }
5746         if(tab == this.active || tab.disabled){
5747             return tab;
5748         }
5749         var e = {};
5750         this.fireEvent("beforetabchange", this, e, tab);
5751         if(e.cancel !== true && !tab.disabled){
5752             if(this.active){
5753                 this.active.hide();
5754             }
5755             this.active = this.items[id];
5756             this.active.show();
5757             this.fireEvent("tabchange", this, this.active);
5758         }
5759         return tab;
5760     },
5761
5762     /**
5763      * Gets the active {@link Roo.TabPanelItem}.
5764      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5765      */
5766     getActiveTab : function(){
5767         return this.active;
5768     },
5769
5770     /**
5771      * Updates the tab body element to fit the height of the container element
5772      * for overflow scrolling
5773      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5774      */
5775     syncHeight : function(targetHeight){
5776         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5777         var bm = this.bodyEl.getMargins();
5778         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5779         this.bodyEl.setHeight(newHeight);
5780         return newHeight;
5781     },
5782
5783     onResize : function(){
5784         if(this.monitorResize){
5785             this.autoSizeTabs();
5786         }
5787     },
5788
5789     /**
5790      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5791      */
5792     beginUpdate : function(){
5793         this.updating = true;
5794     },
5795
5796     /**
5797      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5798      */
5799     endUpdate : function(){
5800         this.updating = false;
5801         this.autoSizeTabs();
5802     },
5803
5804     /**
5805      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5806      */
5807     autoSizeTabs : function(){
5808         var count = this.items.length;
5809         var vcount = count - this.hiddenCount;
5810         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5811             return;
5812         }
5813         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5814         var availWidth = Math.floor(w / vcount);
5815         var b = this.stripBody;
5816         if(b.getWidth() > w){
5817             var tabs = this.items;
5818             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5819             if(availWidth < this.minTabWidth){
5820                 /*if(!this.sleft){    // incomplete scrolling code
5821                     this.createScrollButtons();
5822                 }
5823                 this.showScroll();
5824                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5825             }
5826         }else{
5827             if(this.currentTabWidth < this.preferredTabWidth){
5828                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5829             }
5830         }
5831     },
5832
5833     /**
5834      * Returns the number of tabs in this TabPanel.
5835      * @return {Number}
5836      */
5837      getCount : function(){
5838          return this.items.length;
5839      },
5840
5841     /**
5842      * Resizes all the tabs to the passed width
5843      * @param {Number} The new width
5844      */
5845     setTabWidth : function(width){
5846         this.currentTabWidth = width;
5847         for(var i = 0, len = this.items.length; i < len; i++) {
5848                 if(!this.items[i].isHidden()) {
5849                 this.items[i].setWidth(width);
5850             }
5851         }
5852     },
5853
5854     /**
5855      * Destroys this TabPanel
5856      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5857      */
5858     destroy : function(removeEl){
5859         Roo.EventManager.removeResizeListener(this.onResize, this);
5860         for(var i = 0, len = this.items.length; i < len; i++){
5861             this.items[i].purgeListeners();
5862         }
5863         if(removeEl === true){
5864             this.el.update("");
5865             this.el.remove();
5866         }
5867     }
5868 });
5869
5870 /**
5871  * @class Roo.TabPanelItem
5872  * @extends Roo.util.Observable
5873  * Represents an individual item (tab plus body) in a TabPanel.
5874  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5875  * @param {String} id The id of this TabPanelItem
5876  * @param {String} text The text for the tab of this TabPanelItem
5877  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5878  */
5879 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5880     /**
5881      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5882      * @type Roo.TabPanel
5883      */
5884     this.tabPanel = tabPanel;
5885     /**
5886      * The id for this TabPanelItem
5887      * @type String
5888      */
5889     this.id = id;
5890     /** @private */
5891     this.disabled = false;
5892     /** @private */
5893     this.text = text;
5894     /** @private */
5895     this.loaded = false;
5896     this.closable = closable;
5897
5898     /**
5899      * The body element for this TabPanelItem.
5900      * @type Roo.Element
5901      */
5902     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5903     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5904     this.bodyEl.setStyle("display", "block");
5905     this.bodyEl.setStyle("zoom", "1");
5906     this.hideAction();
5907
5908     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5909     /** @private */
5910     this.el = Roo.get(els.el, true);
5911     this.inner = Roo.get(els.inner, true);
5912     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5913     this.pnode = Roo.get(els.el.parentNode, true);
5914     this.el.on("mousedown", this.onTabMouseDown, this);
5915     this.el.on("click", this.onTabClick, this);
5916     /** @private */
5917     if(closable){
5918         var c = Roo.get(els.close, true);
5919         c.dom.title = this.closeText;
5920         c.addClassOnOver("close-over");
5921         c.on("click", this.closeClick, this);
5922      }
5923
5924     this.addEvents({
5925          /**
5926          * @event activate
5927          * Fires when this tab becomes the active tab.
5928          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5929          * @param {Roo.TabPanelItem} this
5930          */
5931         "activate": true,
5932         /**
5933          * @event beforeclose
5934          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5935          * @param {Roo.TabPanelItem} this
5936          * @param {Object} e Set cancel to true on this object to cancel the close.
5937          */
5938         "beforeclose": true,
5939         /**
5940          * @event close
5941          * Fires when this tab is closed.
5942          * @param {Roo.TabPanelItem} this
5943          */
5944          "close": true,
5945         /**
5946          * @event deactivate
5947          * Fires when this tab is no longer the active tab.
5948          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5949          * @param {Roo.TabPanelItem} this
5950          */
5951          "deactivate" : true
5952     });
5953     this.hidden = false;
5954
5955     Roo.TabPanelItem.superclass.constructor.call(this);
5956 };
5957
5958 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5959     purgeListeners : function(){
5960        Roo.util.Observable.prototype.purgeListeners.call(this);
5961        this.el.removeAllListeners();
5962     },
5963     /**
5964      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5965      */
5966     show : function(){
5967         this.pnode.addClass("on");
5968         this.showAction();
5969         if(Roo.isOpera){
5970             this.tabPanel.stripWrap.repaint();
5971         }
5972         this.fireEvent("activate", this.tabPanel, this);
5973     },
5974
5975     /**
5976      * Returns true if this tab is the active tab.
5977      * @return {Boolean}
5978      */
5979     isActive : function(){
5980         return this.tabPanel.getActiveTab() == this;
5981     },
5982
5983     /**
5984      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5985      */
5986     hide : function(){
5987         this.pnode.removeClass("on");
5988         this.hideAction();
5989         this.fireEvent("deactivate", this.tabPanel, this);
5990     },
5991
5992     hideAction : function(){
5993         this.bodyEl.hide();
5994         this.bodyEl.setStyle("position", "absolute");
5995         this.bodyEl.setLeft("-20000px");
5996         this.bodyEl.setTop("-20000px");
5997     },
5998
5999     showAction : function(){
6000         this.bodyEl.setStyle("position", "relative");
6001         this.bodyEl.setTop("");
6002         this.bodyEl.setLeft("");
6003         this.bodyEl.show();
6004     },
6005
6006     /**
6007      * Set the tooltip for the tab.
6008      * @param {String} tooltip The tab's tooltip
6009      */
6010     setTooltip : function(text){
6011         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6012             this.textEl.dom.qtip = text;
6013             this.textEl.dom.removeAttribute('title');
6014         }else{
6015             this.textEl.dom.title = text;
6016         }
6017     },
6018
6019     onTabClick : function(e){
6020         e.preventDefault();
6021         this.tabPanel.activate(this.id);
6022     },
6023
6024     onTabMouseDown : function(e){
6025         e.preventDefault();
6026         this.tabPanel.activate(this.id);
6027     },
6028
6029     getWidth : function(){
6030         return this.inner.getWidth();
6031     },
6032
6033     setWidth : function(width){
6034         var iwidth = width - this.pnode.getPadding("lr");
6035         this.inner.setWidth(iwidth);
6036         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6037         this.pnode.setWidth(width);
6038     },
6039
6040     /**
6041      * Show or hide the tab
6042      * @param {Boolean} hidden True to hide or false to show.
6043      */
6044     setHidden : function(hidden){
6045         this.hidden = hidden;
6046         this.pnode.setStyle("display", hidden ? "none" : "");
6047     },
6048
6049     /**
6050      * Returns true if this tab is "hidden"
6051      * @return {Boolean}
6052      */
6053     isHidden : function(){
6054         return this.hidden;
6055     },
6056
6057     /**
6058      * Returns the text for this tab
6059      * @return {String}
6060      */
6061     getText : function(){
6062         return this.text;
6063     },
6064
6065     autoSize : function(){
6066         //this.el.beginMeasure();
6067         this.textEl.setWidth(1);
6068         /*
6069          *  #2804 [new] Tabs in Roojs
6070          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6071          */
6072         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6073         //this.el.endMeasure();
6074     },
6075
6076     /**
6077      * Sets the text for the tab (Note: this also sets the tooltip text)
6078      * @param {String} text The tab's text and tooltip
6079      */
6080     setText : function(text){
6081         this.text = text;
6082         this.textEl.update(text);
6083         this.setTooltip(text);
6084         if(!this.tabPanel.resizeTabs){
6085             this.autoSize();
6086         }
6087     },
6088     /**
6089      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6090      */
6091     activate : function(){
6092         this.tabPanel.activate(this.id);
6093     },
6094
6095     /**
6096      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6097      */
6098     disable : function(){
6099         if(this.tabPanel.active != this){
6100             this.disabled = true;
6101             this.pnode.addClass("disabled");
6102         }
6103     },
6104
6105     /**
6106      * Enables this TabPanelItem if it was previously disabled.
6107      */
6108     enable : function(){
6109         this.disabled = false;
6110         this.pnode.removeClass("disabled");
6111     },
6112
6113     /**
6114      * Sets the content for this TabPanelItem.
6115      * @param {String} content The content
6116      * @param {Boolean} loadScripts true to look for and load scripts
6117      */
6118     setContent : function(content, loadScripts){
6119         this.bodyEl.update(content, loadScripts);
6120     },
6121
6122     /**
6123      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6124      * @return {Roo.UpdateManager} The UpdateManager
6125      */
6126     getUpdateManager : function(){
6127         return this.bodyEl.getUpdateManager();
6128     },
6129
6130     /**
6131      * Set a URL to be used to load the content for this TabPanelItem.
6132      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6133      * @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)
6134      * @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)
6135      * @return {Roo.UpdateManager} The UpdateManager
6136      */
6137     setUrl : function(url, params, loadOnce){
6138         if(this.refreshDelegate){
6139             this.un('activate', this.refreshDelegate);
6140         }
6141         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6142         this.on("activate", this.refreshDelegate);
6143         return this.bodyEl.getUpdateManager();
6144     },
6145
6146     /** @private */
6147     _handleRefresh : function(url, params, loadOnce){
6148         if(!loadOnce || !this.loaded){
6149             var updater = this.bodyEl.getUpdateManager();
6150             updater.update(url, params, this._setLoaded.createDelegate(this));
6151         }
6152     },
6153
6154     /**
6155      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6156      *   Will fail silently if the setUrl method has not been called.
6157      *   This does not activate the panel, just updates its content.
6158      */
6159     refresh : function(){
6160         if(this.refreshDelegate){
6161            this.loaded = false;
6162            this.refreshDelegate();
6163         }
6164     },
6165
6166     /** @private */
6167     _setLoaded : function(){
6168         this.loaded = true;
6169     },
6170
6171     /** @private */
6172     closeClick : function(e){
6173         var o = {};
6174         e.stopEvent();
6175         this.fireEvent("beforeclose", this, o);
6176         if(o.cancel !== true){
6177             this.tabPanel.removeTab(this.id);
6178         }
6179     },
6180     /**
6181      * The text displayed in the tooltip for the close icon.
6182      * @type String
6183      */
6184     closeText : "Close this tab"
6185 });
6186
6187 /** @private */
6188 Roo.TabPanel.prototype.createStrip = function(container){
6189     var strip = document.createElement("div");
6190     strip.className = "x-tabs-wrap";
6191     container.appendChild(strip);
6192     return strip;
6193 };
6194 /** @private */
6195 Roo.TabPanel.prototype.createStripList = function(strip){
6196     // div wrapper for retard IE
6197     // returns the "tr" element.
6198     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6199         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6200         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6201     return strip.firstChild.firstChild.firstChild.firstChild;
6202 };
6203 /** @private */
6204 Roo.TabPanel.prototype.createBody = function(container){
6205     var body = document.createElement("div");
6206     Roo.id(body, "tab-body");
6207     Roo.fly(body).addClass("x-tabs-body");
6208     container.appendChild(body);
6209     return body;
6210 };
6211 /** @private */
6212 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6213     var body = Roo.getDom(id);
6214     if(!body){
6215         body = document.createElement("div");
6216         body.id = id;
6217     }
6218     Roo.fly(body).addClass("x-tabs-item-body");
6219     bodyEl.insertBefore(body, bodyEl.firstChild);
6220     return body;
6221 };
6222 /** @private */
6223 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6224     var td = document.createElement("td");
6225     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6226     //stripEl.appendChild(td);
6227     if(closable){
6228         td.className = "x-tabs-closable";
6229         if(!this.closeTpl){
6230             this.closeTpl = new Roo.Template(
6231                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6232                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6233                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6234             );
6235         }
6236         var el = this.closeTpl.overwrite(td, {"text": text});
6237         var close = el.getElementsByTagName("div")[0];
6238         var inner = el.getElementsByTagName("em")[0];
6239         return {"el": el, "close": close, "inner": inner};
6240     } else {
6241         if(!this.tabTpl){
6242             this.tabTpl = new Roo.Template(
6243                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6244                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6245             );
6246         }
6247         var el = this.tabTpl.overwrite(td, {"text": text});
6248         var inner = el.getElementsByTagName("em")[0];
6249         return {"el": el, "inner": inner};
6250     }
6251 };/*
6252  * Based on:
6253  * Ext JS Library 1.1.1
6254  * Copyright(c) 2006-2007, Ext JS, LLC.
6255  *
6256  * Originally Released Under LGPL - original licence link has changed is not relivant.
6257  *
6258  * Fork - LGPL
6259  * <script type="text/javascript">
6260  */
6261
6262 /**
6263  * @class Roo.Button
6264  * @extends Roo.util.Observable
6265  * Simple Button class
6266  * @cfg {String} text The button text
6267  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6268  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6269  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6270  * @cfg {Object} scope The scope of the handler
6271  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6272  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6273  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6274  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6275  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6276  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6277    applies if enableToggle = true)
6278  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6279  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6280   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6281  * @constructor
6282  * Create a new button
6283  * @param {Object} config The config object
6284  */
6285 Roo.Button = function(renderTo, config)
6286 {
6287     if (!config) {
6288         config = renderTo;
6289         renderTo = config.renderTo || false;
6290     }
6291     
6292     Roo.apply(this, config);
6293     this.addEvents({
6294         /**
6295              * @event click
6296              * Fires when this button is clicked
6297              * @param {Button} this
6298              * @param {EventObject} e The click event
6299              */
6300             "click" : true,
6301         /**
6302              * @event toggle
6303              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6304              * @param {Button} this
6305              * @param {Boolean} pressed
6306              */
6307             "toggle" : true,
6308         /**
6309              * @event mouseover
6310              * Fires when the mouse hovers over the button
6311              * @param {Button} this
6312              * @param {Event} e The event object
6313              */
6314         'mouseover' : true,
6315         /**
6316              * @event mouseout
6317              * Fires when the mouse exits the button
6318              * @param {Button} this
6319              * @param {Event} e The event object
6320              */
6321         'mouseout': true,
6322          /**
6323              * @event render
6324              * Fires when the button is rendered
6325              * @param {Button} this
6326              */
6327         'render': true
6328     });
6329     if(this.menu){
6330         this.menu = Roo.menu.MenuMgr.get(this.menu);
6331     }
6332     // register listeners first!!  - so render can be captured..
6333     Roo.util.Observable.call(this);
6334     if(renderTo){
6335         this.render(renderTo);
6336     }
6337     
6338   
6339 };
6340
6341 Roo.extend(Roo.Button, Roo.util.Observable, {
6342     /**
6343      * 
6344      */
6345     
6346     /**
6347      * Read-only. True if this button is hidden
6348      * @type Boolean
6349      */
6350     hidden : false,
6351     /**
6352      * Read-only. True if this button is disabled
6353      * @type Boolean
6354      */
6355     disabled : false,
6356     /**
6357      * Read-only. True if this button is pressed (only if enableToggle = true)
6358      * @type Boolean
6359      */
6360     pressed : false,
6361
6362     /**
6363      * @cfg {Number} tabIndex 
6364      * The DOM tabIndex for this button (defaults to undefined)
6365      */
6366     tabIndex : undefined,
6367
6368     /**
6369      * @cfg {Boolean} enableToggle
6370      * True to enable pressed/not pressed toggling (defaults to false)
6371      */
6372     enableToggle: false,
6373     /**
6374      * @cfg {Roo.menu.Menu} menu
6375      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6376      */
6377     menu : undefined,
6378     /**
6379      * @cfg {String} menuAlign
6380      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6381      */
6382     menuAlign : "tl-bl?",
6383
6384     /**
6385      * @cfg {String} iconCls
6386      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6387      */
6388     iconCls : undefined,
6389     /**
6390      * @cfg {String} type
6391      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6392      */
6393     type : 'button',
6394
6395     // private
6396     menuClassTarget: 'tr',
6397
6398     /**
6399      * @cfg {String} clickEvent
6400      * The type of event to map to the button's event handler (defaults to 'click')
6401      */
6402     clickEvent : 'click',
6403
6404     /**
6405      * @cfg {Boolean} handleMouseEvents
6406      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6407      */
6408     handleMouseEvents : true,
6409
6410     /**
6411      * @cfg {String} tooltipType
6412      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6413      */
6414     tooltipType : 'qtip',
6415
6416     /**
6417      * @cfg {String} cls
6418      * A CSS class to apply to the button's main element.
6419      */
6420     
6421     /**
6422      * @cfg {Roo.Template} template (Optional)
6423      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6424      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6425      * require code modifications if required elements (e.g. a button) aren't present.
6426      */
6427
6428     // private
6429     render : function(renderTo){
6430         var btn;
6431         if(this.hideParent){
6432             this.parentEl = Roo.get(renderTo);
6433         }
6434         if(!this.dhconfig){
6435             if(!this.template){
6436                 if(!Roo.Button.buttonTemplate){
6437                     // hideous table template
6438                     Roo.Button.buttonTemplate = new Roo.Template(
6439                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6440                         '<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>',
6441                         "</tr></tbody></table>");
6442                 }
6443                 this.template = Roo.Button.buttonTemplate;
6444             }
6445             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6446             var btnEl = btn.child("button:first");
6447             btnEl.on('focus', this.onFocus, this);
6448             btnEl.on('blur', this.onBlur, this);
6449             if(this.cls){
6450                 btn.addClass(this.cls);
6451             }
6452             if(this.icon){
6453                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6454             }
6455             if(this.iconCls){
6456                 btnEl.addClass(this.iconCls);
6457                 if(!this.cls){
6458                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6459                 }
6460             }
6461             if(this.tabIndex !== undefined){
6462                 btnEl.dom.tabIndex = this.tabIndex;
6463             }
6464             if(this.tooltip){
6465                 if(typeof this.tooltip == 'object'){
6466                     Roo.QuickTips.tips(Roo.apply({
6467                           target: btnEl.id
6468                     }, this.tooltip));
6469                 } else {
6470                     btnEl.dom[this.tooltipType] = this.tooltip;
6471                 }
6472             }
6473         }else{
6474             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6475         }
6476         this.el = btn;
6477         if(this.id){
6478             this.el.dom.id = this.el.id = this.id;
6479         }
6480         if(this.menu){
6481             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6482             this.menu.on("show", this.onMenuShow, this);
6483             this.menu.on("hide", this.onMenuHide, this);
6484         }
6485         btn.addClass("x-btn");
6486         if(Roo.isIE && !Roo.isIE7){
6487             this.autoWidth.defer(1, this);
6488         }else{
6489             this.autoWidth();
6490         }
6491         if(this.handleMouseEvents){
6492             btn.on("mouseover", this.onMouseOver, this);
6493             btn.on("mouseout", this.onMouseOut, this);
6494             btn.on("mousedown", this.onMouseDown, this);
6495         }
6496         btn.on(this.clickEvent, this.onClick, this);
6497         //btn.on("mouseup", this.onMouseUp, this);
6498         if(this.hidden){
6499             this.hide();
6500         }
6501         if(this.disabled){
6502             this.disable();
6503         }
6504         Roo.ButtonToggleMgr.register(this);
6505         if(this.pressed){
6506             this.el.addClass("x-btn-pressed");
6507         }
6508         if(this.repeat){
6509             var repeater = new Roo.util.ClickRepeater(btn,
6510                 typeof this.repeat == "object" ? this.repeat : {}
6511             );
6512             repeater.on("click", this.onClick,  this);
6513         }
6514         
6515         this.fireEvent('render', this);
6516         
6517     },
6518     /**
6519      * Returns the button's underlying element
6520      * @return {Roo.Element} The element
6521      */
6522     getEl : function(){
6523         return this.el;  
6524     },
6525     
6526     /**
6527      * Destroys this Button and removes any listeners.
6528      */
6529     destroy : function(){
6530         Roo.ButtonToggleMgr.unregister(this);
6531         this.el.removeAllListeners();
6532         this.purgeListeners();
6533         this.el.remove();
6534     },
6535
6536     // private
6537     autoWidth : function(){
6538         if(this.el){
6539             this.el.setWidth("auto");
6540             if(Roo.isIE7 && Roo.isStrict){
6541                 var ib = this.el.child('button');
6542                 if(ib && ib.getWidth() > 20){
6543                     ib.clip();
6544                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6545                 }
6546             }
6547             if(this.minWidth){
6548                 if(this.hidden){
6549                     this.el.beginMeasure();
6550                 }
6551                 if(this.el.getWidth() < this.minWidth){
6552                     this.el.setWidth(this.minWidth);
6553                 }
6554                 if(this.hidden){
6555                     this.el.endMeasure();
6556                 }
6557             }
6558         }
6559     },
6560
6561     /**
6562      * Assigns this button's click handler
6563      * @param {Function} handler The function to call when the button is clicked
6564      * @param {Object} scope (optional) Scope for the function passed in
6565      */
6566     setHandler : function(handler, scope){
6567         this.handler = handler;
6568         this.scope = scope;  
6569     },
6570     
6571     /**
6572      * Sets this button's text
6573      * @param {String} text The button text
6574      */
6575     setText : function(text){
6576         this.text = text;
6577         if(this.el){
6578             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6579         }
6580         this.autoWidth();
6581     },
6582     
6583     /**
6584      * Gets the text for this button
6585      * @return {String} The button text
6586      */
6587     getText : function(){
6588         return this.text;  
6589     },
6590     
6591     /**
6592      * Show this button
6593      */
6594     show: function(){
6595         this.hidden = false;
6596         if(this.el){
6597             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6598         }
6599     },
6600     
6601     /**
6602      * Hide this button
6603      */
6604     hide: function(){
6605         this.hidden = true;
6606         if(this.el){
6607             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6608         }
6609     },
6610     
6611     /**
6612      * Convenience function for boolean show/hide
6613      * @param {Boolean} visible True to show, false to hide
6614      */
6615     setVisible: function(visible){
6616         if(visible) {
6617             this.show();
6618         }else{
6619             this.hide();
6620         }
6621     },
6622     
6623     /**
6624      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6625      * @param {Boolean} state (optional) Force a particular state
6626      */
6627     toggle : function(state){
6628         state = state === undefined ? !this.pressed : state;
6629         if(state != this.pressed){
6630             if(state){
6631                 this.el.addClass("x-btn-pressed");
6632                 this.pressed = true;
6633                 this.fireEvent("toggle", this, true);
6634             }else{
6635                 this.el.removeClass("x-btn-pressed");
6636                 this.pressed = false;
6637                 this.fireEvent("toggle", this, false);
6638             }
6639             if(this.toggleHandler){
6640                 this.toggleHandler.call(this.scope || this, this, state);
6641             }
6642         }
6643     },
6644     
6645     /**
6646      * Focus the button
6647      */
6648     focus : function(){
6649         this.el.child('button:first').focus();
6650     },
6651     
6652     /**
6653      * Disable this button
6654      */
6655     disable : function(){
6656         if(this.el){
6657             this.el.addClass("x-btn-disabled");
6658         }
6659         this.disabled = true;
6660     },
6661     
6662     /**
6663      * Enable this button
6664      */
6665     enable : function(){
6666         if(this.el){
6667             this.el.removeClass("x-btn-disabled");
6668         }
6669         this.disabled = false;
6670     },
6671
6672     /**
6673      * Convenience function for boolean enable/disable
6674      * @param {Boolean} enabled True to enable, false to disable
6675      */
6676     setDisabled : function(v){
6677         this[v !== true ? "enable" : "disable"]();
6678     },
6679
6680     // private
6681     onClick : function(e)
6682     {
6683         if(e){
6684             e.preventDefault();
6685         }
6686         if(e.button != 0){
6687             return;
6688         }
6689         if(!this.disabled){
6690             if(this.enableToggle){
6691                 this.toggle();
6692             }
6693             if(this.menu && !this.menu.isVisible()){
6694                 this.menu.show(this.el, this.menuAlign);
6695             }
6696             this.fireEvent("click", this, e);
6697             if(this.handler){
6698                 this.el.removeClass("x-btn-over");
6699                 this.handler.call(this.scope || this, this, e);
6700             }
6701         }
6702     },
6703     // private
6704     onMouseOver : function(e){
6705         if(!this.disabled){
6706             this.el.addClass("x-btn-over");
6707             this.fireEvent('mouseover', this, e);
6708         }
6709     },
6710     // private
6711     onMouseOut : function(e){
6712         if(!e.within(this.el,  true)){
6713             this.el.removeClass("x-btn-over");
6714             this.fireEvent('mouseout', this, e);
6715         }
6716     },
6717     // private
6718     onFocus : function(e){
6719         if(!this.disabled){
6720             this.el.addClass("x-btn-focus");
6721         }
6722     },
6723     // private
6724     onBlur : function(e){
6725         this.el.removeClass("x-btn-focus");
6726     },
6727     // private
6728     onMouseDown : function(e){
6729         if(!this.disabled && e.button == 0){
6730             this.el.addClass("x-btn-click");
6731             Roo.get(document).on('mouseup', this.onMouseUp, this);
6732         }
6733     },
6734     // private
6735     onMouseUp : function(e){
6736         if(e.button == 0){
6737             this.el.removeClass("x-btn-click");
6738             Roo.get(document).un('mouseup', this.onMouseUp, this);
6739         }
6740     },
6741     // private
6742     onMenuShow : function(e){
6743         this.el.addClass("x-btn-menu-active");
6744     },
6745     // private
6746     onMenuHide : function(e){
6747         this.el.removeClass("x-btn-menu-active");
6748     }   
6749 });
6750
6751 // Private utility class used by Button
6752 Roo.ButtonToggleMgr = function(){
6753    var groups = {};
6754    
6755    function toggleGroup(btn, state){
6756        if(state){
6757            var g = groups[btn.toggleGroup];
6758            for(var i = 0, l = g.length; i < l; i++){
6759                if(g[i] != btn){
6760                    g[i].toggle(false);
6761                }
6762            }
6763        }
6764    }
6765    
6766    return {
6767        register : function(btn){
6768            if(!btn.toggleGroup){
6769                return;
6770            }
6771            var g = groups[btn.toggleGroup];
6772            if(!g){
6773                g = groups[btn.toggleGroup] = [];
6774            }
6775            g.push(btn);
6776            btn.on("toggle", toggleGroup);
6777        },
6778        
6779        unregister : function(btn){
6780            if(!btn.toggleGroup){
6781                return;
6782            }
6783            var g = groups[btn.toggleGroup];
6784            if(g){
6785                g.remove(btn);
6786                btn.un("toggle", toggleGroup);
6787            }
6788        }
6789    };
6790 }();/*
6791  * Based on:
6792  * Ext JS Library 1.1.1
6793  * Copyright(c) 2006-2007, Ext JS, LLC.
6794  *
6795  * Originally Released Under LGPL - original licence link has changed is not relivant.
6796  *
6797  * Fork - LGPL
6798  * <script type="text/javascript">
6799  */
6800  
6801 /**
6802  * @class Roo.SplitButton
6803  * @extends Roo.Button
6804  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6805  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6806  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6807  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6808  * @cfg {String} arrowTooltip The title attribute of the arrow
6809  * @constructor
6810  * Create a new menu button
6811  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6812  * @param {Object} config The config object
6813  */
6814 Roo.SplitButton = function(renderTo, config){
6815     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6816     /**
6817      * @event arrowclick
6818      * Fires when this button's arrow is clicked
6819      * @param {SplitButton} this
6820      * @param {EventObject} e The click event
6821      */
6822     this.addEvents({"arrowclick":true});
6823 };
6824
6825 Roo.extend(Roo.SplitButton, Roo.Button, {
6826     render : function(renderTo){
6827         // this is one sweet looking template!
6828         var tpl = new Roo.Template(
6829             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6830             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6831             '<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>',
6832             "</tbody></table></td><td>",
6833             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6834             '<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>',
6835             "</tbody></table></td></tr></table>"
6836         );
6837         var btn = tpl.append(renderTo, [this.text, this.type], true);
6838         var btnEl = btn.child("button");
6839         if(this.cls){
6840             btn.addClass(this.cls);
6841         }
6842         if(this.icon){
6843             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6844         }
6845         if(this.iconCls){
6846             btnEl.addClass(this.iconCls);
6847             if(!this.cls){
6848                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6849             }
6850         }
6851         this.el = btn;
6852         if(this.handleMouseEvents){
6853             btn.on("mouseover", this.onMouseOver, this);
6854             btn.on("mouseout", this.onMouseOut, this);
6855             btn.on("mousedown", this.onMouseDown, this);
6856             btn.on("mouseup", this.onMouseUp, this);
6857         }
6858         btn.on(this.clickEvent, this.onClick, this);
6859         if(this.tooltip){
6860             if(typeof this.tooltip == 'object'){
6861                 Roo.QuickTips.tips(Roo.apply({
6862                       target: btnEl.id
6863                 }, this.tooltip));
6864             } else {
6865                 btnEl.dom[this.tooltipType] = this.tooltip;
6866             }
6867         }
6868         if(this.arrowTooltip){
6869             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6870         }
6871         if(this.hidden){
6872             this.hide();
6873         }
6874         if(this.disabled){
6875             this.disable();
6876         }
6877         if(this.pressed){
6878             this.el.addClass("x-btn-pressed");
6879         }
6880         if(Roo.isIE && !Roo.isIE7){
6881             this.autoWidth.defer(1, this);
6882         }else{
6883             this.autoWidth();
6884         }
6885         if(this.menu){
6886             this.menu.on("show", this.onMenuShow, this);
6887             this.menu.on("hide", this.onMenuHide, this);
6888         }
6889         this.fireEvent('render', this);
6890     },
6891
6892     // private
6893     autoWidth : function(){
6894         if(this.el){
6895             var tbl = this.el.child("table:first");
6896             var tbl2 = this.el.child("table:last");
6897             this.el.setWidth("auto");
6898             tbl.setWidth("auto");
6899             if(Roo.isIE7 && Roo.isStrict){
6900                 var ib = this.el.child('button:first');
6901                 if(ib && ib.getWidth() > 20){
6902                     ib.clip();
6903                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6904                 }
6905             }
6906             if(this.minWidth){
6907                 if(this.hidden){
6908                     this.el.beginMeasure();
6909                 }
6910                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6911                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6912                 }
6913                 if(this.hidden){
6914                     this.el.endMeasure();
6915                 }
6916             }
6917             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6918         } 
6919     },
6920     /**
6921      * Sets this button's click handler
6922      * @param {Function} handler The function to call when the button is clicked
6923      * @param {Object} scope (optional) Scope for the function passed above
6924      */
6925     setHandler : function(handler, scope){
6926         this.handler = handler;
6927         this.scope = scope;  
6928     },
6929     
6930     /**
6931      * Sets this button's arrow click handler
6932      * @param {Function} handler The function to call when the arrow is clicked
6933      * @param {Object} scope (optional) Scope for the function passed above
6934      */
6935     setArrowHandler : function(handler, scope){
6936         this.arrowHandler = handler;
6937         this.scope = scope;  
6938     },
6939     
6940     /**
6941      * Focus the button
6942      */
6943     focus : function(){
6944         if(this.el){
6945             this.el.child("button:first").focus();
6946         }
6947     },
6948
6949     // private
6950     onClick : function(e){
6951         e.preventDefault();
6952         if(!this.disabled){
6953             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6954                 if(this.menu && !this.menu.isVisible()){
6955                     this.menu.show(this.el, this.menuAlign);
6956                 }
6957                 this.fireEvent("arrowclick", this, e);
6958                 if(this.arrowHandler){
6959                     this.arrowHandler.call(this.scope || this, this, e);
6960                 }
6961             }else{
6962                 this.fireEvent("click", this, e);
6963                 if(this.handler){
6964                     this.handler.call(this.scope || this, this, e);
6965                 }
6966             }
6967         }
6968     },
6969     // private
6970     onMouseDown : function(e){
6971         if(!this.disabled){
6972             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6973         }
6974     },
6975     // private
6976     onMouseUp : function(e){
6977         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6978     }   
6979 });
6980
6981
6982 // backwards compat
6983 Roo.MenuButton = Roo.SplitButton;/*
6984  * Based on:
6985  * Ext JS Library 1.1.1
6986  * Copyright(c) 2006-2007, Ext JS, LLC.
6987  *
6988  * Originally Released Under LGPL - original licence link has changed is not relivant.
6989  *
6990  * Fork - LGPL
6991  * <script type="text/javascript">
6992  */
6993
6994 /**
6995  * @class Roo.Toolbar
6996  * @children   Roo.Toolbar.Item Roo.form.Field
6997  * Basic Toolbar class.
6998  * @constructor
6999  * Creates a new Toolbar
7000  * @param {Object} container The config object
7001  */ 
7002 Roo.Toolbar = function(container, buttons, config)
7003 {
7004     /// old consturctor format still supported..
7005     if(container instanceof Array){ // omit the container for later rendering
7006         buttons = container;
7007         config = buttons;
7008         container = null;
7009     }
7010     if (typeof(container) == 'object' && container.xtype) {
7011         config = container;
7012         container = config.container;
7013         buttons = config.buttons || []; // not really - use items!!
7014     }
7015     var xitems = [];
7016     if (config && config.items) {
7017         xitems = config.items;
7018         delete config.items;
7019     }
7020     Roo.apply(this, config);
7021     this.buttons = buttons;
7022     
7023     if(container){
7024         this.render(container);
7025     }
7026     this.xitems = xitems;
7027     Roo.each(xitems, function(b) {
7028         this.add(b);
7029     }, this);
7030     
7031 };
7032
7033 Roo.Toolbar.prototype = {
7034     /**
7035      * @cfg {Array} items
7036      * array of button configs or elements to add (will be converted to a MixedCollection)
7037      */
7038     items: false,
7039     /**
7040      * @cfg {String/HTMLElement/Element} container
7041      * The id or element that will contain the toolbar
7042      */
7043     // private
7044     render : function(ct){
7045         this.el = Roo.get(ct);
7046         if(this.cls){
7047             this.el.addClass(this.cls);
7048         }
7049         // using a table allows for vertical alignment
7050         // 100% width is needed by Safari...
7051         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7052         this.tr = this.el.child("tr", true);
7053         var autoId = 0;
7054         this.items = new Roo.util.MixedCollection(false, function(o){
7055             return o.id || ("item" + (++autoId));
7056         });
7057         if(this.buttons){
7058             this.add.apply(this, this.buttons);
7059             delete this.buttons;
7060         }
7061     },
7062
7063     /**
7064      * Adds element(s) to the toolbar -- this function takes a variable number of 
7065      * arguments of mixed type and adds them to the toolbar.
7066      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7067      * <ul>
7068      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7069      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7070      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7071      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7072      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7073      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7074      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7075      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7076      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7077      * </ul>
7078      * @param {Mixed} arg2
7079      * @param {Mixed} etc.
7080      */
7081     add : function(){
7082         var a = arguments, l = a.length;
7083         for(var i = 0; i < l; i++){
7084             this._add(a[i]);
7085         }
7086     },
7087     // private..
7088     _add : function(el) {
7089         
7090         if (el.xtype) {
7091             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7092         }
7093         
7094         if (el.applyTo){ // some kind of form field
7095             return this.addField(el);
7096         } 
7097         if (el.render){ // some kind of Toolbar.Item
7098             return this.addItem(el);
7099         }
7100         if (typeof el == "string"){ // string
7101             if(el == "separator" || el == "-"){
7102                 return this.addSeparator();
7103             }
7104             if (el == " "){
7105                 return this.addSpacer();
7106             }
7107             if(el == "->"){
7108                 return this.addFill();
7109             }
7110             return this.addText(el);
7111             
7112         }
7113         if(el.tagName){ // element
7114             return this.addElement(el);
7115         }
7116         if(typeof el == "object"){ // must be button config?
7117             return this.addButton(el);
7118         }
7119         // and now what?!?!
7120         return false;
7121         
7122     },
7123     
7124     /**
7125      * Add an Xtype element
7126      * @param {Object} xtype Xtype Object
7127      * @return {Object} created Object
7128      */
7129     addxtype : function(e){
7130         return this.add(e);  
7131     },
7132     
7133     /**
7134      * Returns the Element for this toolbar.
7135      * @return {Roo.Element}
7136      */
7137     getEl : function(){
7138         return this.el;  
7139     },
7140     
7141     /**
7142      * Adds a separator
7143      * @return {Roo.Toolbar.Item} The separator item
7144      */
7145     addSeparator : function(){
7146         return this.addItem(new Roo.Toolbar.Separator());
7147     },
7148
7149     /**
7150      * Adds a spacer element
7151      * @return {Roo.Toolbar.Spacer} The spacer item
7152      */
7153     addSpacer : function(){
7154         return this.addItem(new Roo.Toolbar.Spacer());
7155     },
7156
7157     /**
7158      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7159      * @return {Roo.Toolbar.Fill} The fill item
7160      */
7161     addFill : function(){
7162         return this.addItem(new Roo.Toolbar.Fill());
7163     },
7164
7165     /**
7166      * Adds any standard HTML element to the toolbar
7167      * @param {String/HTMLElement/Element} el The element or id of the element to add
7168      * @return {Roo.Toolbar.Item} The element's item
7169      */
7170     addElement : function(el){
7171         return this.addItem(new Roo.Toolbar.Item(el));
7172     },
7173     /**
7174      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7175      * @type Roo.util.MixedCollection  
7176      */
7177     items : false,
7178      
7179     /**
7180      * Adds any Toolbar.Item or subclass
7181      * @param {Roo.Toolbar.Item} item
7182      * @return {Roo.Toolbar.Item} The item
7183      */
7184     addItem : function(item){
7185         var td = this.nextBlock();
7186         item.render(td);
7187         this.items.add(item);
7188         return item;
7189     },
7190     
7191     /**
7192      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7193      * @param {Object/Array} config A button config or array of configs
7194      * @return {Roo.Toolbar.Button/Array}
7195      */
7196     addButton : function(config){
7197         if(config instanceof Array){
7198             var buttons = [];
7199             for(var i = 0, len = config.length; i < len; i++) {
7200                 buttons.push(this.addButton(config[i]));
7201             }
7202             return buttons;
7203         }
7204         var b = config;
7205         if(!(config instanceof Roo.Toolbar.Button)){
7206             b = config.split ?
7207                 new Roo.Toolbar.SplitButton(config) :
7208                 new Roo.Toolbar.Button(config);
7209         }
7210         var td = this.nextBlock();
7211         b.render(td);
7212         this.items.add(b);
7213         return b;
7214     },
7215     
7216     /**
7217      * Adds text to the toolbar
7218      * @param {String} text The text to add
7219      * @return {Roo.Toolbar.Item} The element's item
7220      */
7221     addText : function(text){
7222         return this.addItem(new Roo.Toolbar.TextItem(text));
7223     },
7224     
7225     /**
7226      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7227      * @param {Number} index The index where the item is to be inserted
7228      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7229      * @return {Roo.Toolbar.Button/Item}
7230      */
7231     insertButton : function(index, item){
7232         if(item instanceof Array){
7233             var buttons = [];
7234             for(var i = 0, len = item.length; i < len; i++) {
7235                buttons.push(this.insertButton(index + i, item[i]));
7236             }
7237             return buttons;
7238         }
7239         if (!(item instanceof Roo.Toolbar.Button)){
7240            item = new Roo.Toolbar.Button(item);
7241         }
7242         var td = document.createElement("td");
7243         this.tr.insertBefore(td, this.tr.childNodes[index]);
7244         item.render(td);
7245         this.items.insert(index, item);
7246         return item;
7247     },
7248     
7249     /**
7250      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7251      * @param {Object} config
7252      * @return {Roo.Toolbar.Item} The element's item
7253      */
7254     addDom : function(config, returnEl){
7255         var td = this.nextBlock();
7256         Roo.DomHelper.overwrite(td, config);
7257         var ti = new Roo.Toolbar.Item(td.firstChild);
7258         ti.render(td);
7259         this.items.add(ti);
7260         return ti;
7261     },
7262
7263     /**
7264      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7265      * @type Roo.util.MixedCollection  
7266      */
7267     fields : false,
7268     
7269     /**
7270      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7271      * Note: the field should not have been rendered yet. For a field that has already been
7272      * rendered, use {@link #addElement}.
7273      * @param {Roo.form.Field} field
7274      * @return {Roo.ToolbarItem}
7275      */
7276      
7277       
7278     addField : function(field) {
7279         if (!this.fields) {
7280             var autoId = 0;
7281             this.fields = new Roo.util.MixedCollection(false, function(o){
7282                 return o.id || ("item" + (++autoId));
7283             });
7284
7285         }
7286         
7287         var td = this.nextBlock();
7288         field.render(td);
7289         var ti = new Roo.Toolbar.Item(td.firstChild);
7290         ti.render(td);
7291         this.items.add(ti);
7292         this.fields.add(field);
7293         return ti;
7294     },
7295     /**
7296      * Hide the toolbar
7297      * @method hide
7298      */
7299      
7300       
7301     hide : function()
7302     {
7303         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7304         this.el.child('div').hide();
7305     },
7306     /**
7307      * Show the toolbar
7308      * @method show
7309      */
7310     show : function()
7311     {
7312         this.el.child('div').show();
7313     },
7314       
7315     // private
7316     nextBlock : function(){
7317         var td = document.createElement("td");
7318         this.tr.appendChild(td);
7319         return td;
7320     },
7321
7322     // private
7323     destroy : function(){
7324         if(this.items){ // rendered?
7325             Roo.destroy.apply(Roo, this.items.items);
7326         }
7327         if(this.fields){ // rendered?
7328             Roo.destroy.apply(Roo, this.fields.items);
7329         }
7330         Roo.Element.uncache(this.el, this.tr);
7331     }
7332 };
7333
7334 /**
7335  * @class Roo.Toolbar.Item
7336  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7337  * @constructor
7338  * Creates a new Item
7339  * @param {HTMLElement} el 
7340  */
7341 Roo.Toolbar.Item = function(el){
7342     var cfg = {};
7343     if (typeof (el.xtype) != 'undefined') {
7344         cfg = el;
7345         el = cfg.el;
7346     }
7347     
7348     this.el = Roo.getDom(el);
7349     this.id = Roo.id(this.el);
7350     this.hidden = false;
7351     
7352     this.addEvents({
7353          /**
7354              * @event render
7355              * Fires when the button is rendered
7356              * @param {Button} this
7357              */
7358         'render': true
7359     });
7360     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7361 };
7362 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7363 //Roo.Toolbar.Item.prototype = {
7364     
7365     /**
7366      * Get this item's HTML Element
7367      * @return {HTMLElement}
7368      */
7369     getEl : function(){
7370        return this.el;  
7371     },
7372
7373     // private
7374     render : function(td){
7375         
7376          this.td = td;
7377         td.appendChild(this.el);
7378         
7379         this.fireEvent('render', this);
7380     },
7381     
7382     /**
7383      * Removes and destroys this item.
7384      */
7385     destroy : function(){
7386         this.td.parentNode.removeChild(this.td);
7387     },
7388     
7389     /**
7390      * Shows this item.
7391      */
7392     show: function(){
7393         this.hidden = false;
7394         this.td.style.display = "";
7395     },
7396     
7397     /**
7398      * Hides this item.
7399      */
7400     hide: function(){
7401         this.hidden = true;
7402         this.td.style.display = "none";
7403     },
7404     
7405     /**
7406      * Convenience function for boolean show/hide.
7407      * @param {Boolean} visible true to show/false to hide
7408      */
7409     setVisible: function(visible){
7410         if(visible) {
7411             this.show();
7412         }else{
7413             this.hide();
7414         }
7415     },
7416     
7417     /**
7418      * Try to focus this item.
7419      */
7420     focus : function(){
7421         Roo.fly(this.el).focus();
7422     },
7423     
7424     /**
7425      * Disables this item.
7426      */
7427     disable : function(){
7428         Roo.fly(this.td).addClass("x-item-disabled");
7429         this.disabled = true;
7430         this.el.disabled = true;
7431     },
7432     
7433     /**
7434      * Enables this item.
7435      */
7436     enable : function(){
7437         Roo.fly(this.td).removeClass("x-item-disabled");
7438         this.disabled = false;
7439         this.el.disabled = false;
7440     }
7441 });
7442
7443
7444 /**
7445  * @class Roo.Toolbar.Separator
7446  * @extends Roo.Toolbar.Item
7447  * A simple toolbar separator class
7448  * @constructor
7449  * Creates a new Separator
7450  */
7451 Roo.Toolbar.Separator = function(cfg){
7452     
7453     var s = document.createElement("span");
7454     s.className = "ytb-sep";
7455     if (cfg) {
7456         cfg.el = s;
7457     }
7458     
7459     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7460 };
7461 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7462     enable:Roo.emptyFn,
7463     disable:Roo.emptyFn,
7464     focus:Roo.emptyFn
7465 });
7466
7467 /**
7468  * @class Roo.Toolbar.Spacer
7469  * @extends Roo.Toolbar.Item
7470  * A simple element that adds extra horizontal space to a toolbar.
7471  * @constructor
7472  * Creates a new Spacer
7473  */
7474 Roo.Toolbar.Spacer = function(cfg){
7475     var s = document.createElement("div");
7476     s.className = "ytb-spacer";
7477     if (cfg) {
7478         cfg.el = s;
7479     }
7480     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7481 };
7482 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7483     enable:Roo.emptyFn,
7484     disable:Roo.emptyFn,
7485     focus:Roo.emptyFn
7486 });
7487
7488 /**
7489  * @class Roo.Toolbar.Fill
7490  * @extends Roo.Toolbar.Spacer
7491  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7492  * @constructor
7493  * Creates a new Spacer
7494  */
7495 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7496     // private
7497     render : function(td){
7498         td.style.width = '100%';
7499         Roo.Toolbar.Fill.superclass.render.call(this, td);
7500     }
7501 });
7502
7503 /**
7504  * @class Roo.Toolbar.TextItem
7505  * @extends Roo.Toolbar.Item
7506  * A simple class that renders text directly into a toolbar.
7507  * @constructor
7508  * Creates a new TextItem
7509  * @cfg {string} text 
7510  */
7511 Roo.Toolbar.TextItem = function(cfg){
7512     var  text = cfg || "";
7513     if (typeof(cfg) == 'object') {
7514         text = cfg.text || "";
7515     }  else {
7516         cfg = null;
7517     }
7518     var s = document.createElement("span");
7519     s.className = "ytb-text";
7520     s.innerHTML = text;
7521     if (cfg) {
7522         cfg.el  = s;
7523     }
7524     
7525     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7526 };
7527 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7528     
7529      
7530     enable:Roo.emptyFn,
7531     disable:Roo.emptyFn,
7532     focus:Roo.emptyFn
7533 });
7534
7535 /**
7536  * @class Roo.Toolbar.Button
7537  * @extends Roo.Button
7538  * A button that renders into a toolbar.
7539  * @constructor
7540  * Creates a new Button
7541  * @param {Object} config A standard {@link Roo.Button} config object
7542  */
7543 Roo.Toolbar.Button = function(config){
7544     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7545 };
7546 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7547 {
7548     
7549     
7550     render : function(td){
7551         this.td = td;
7552         Roo.Toolbar.Button.superclass.render.call(this, td);
7553     },
7554     
7555     /**
7556      * Removes and destroys this button
7557      */
7558     destroy : function(){
7559         Roo.Toolbar.Button.superclass.destroy.call(this);
7560         this.td.parentNode.removeChild(this.td);
7561     },
7562     
7563     /**
7564      * Shows this button
7565      */
7566     show: function(){
7567         this.hidden = false;
7568         this.td.style.display = "";
7569     },
7570     
7571     /**
7572      * Hides this button
7573      */
7574     hide: function(){
7575         this.hidden = true;
7576         this.td.style.display = "none";
7577     },
7578
7579     /**
7580      * Disables this item
7581      */
7582     disable : function(){
7583         Roo.fly(this.td).addClass("x-item-disabled");
7584         this.disabled = true;
7585     },
7586
7587     /**
7588      * Enables this item
7589      */
7590     enable : function(){
7591         Roo.fly(this.td).removeClass("x-item-disabled");
7592         this.disabled = false;
7593     }
7594 });
7595 // backwards compat
7596 Roo.ToolbarButton = Roo.Toolbar.Button;
7597
7598 /**
7599  * @class Roo.Toolbar.SplitButton
7600  * @extends Roo.SplitButton
7601  * A menu button that renders into a toolbar.
7602  * @constructor
7603  * Creates a new SplitButton
7604  * @param {Object} config A standard {@link Roo.SplitButton} config object
7605  */
7606 Roo.Toolbar.SplitButton = function(config){
7607     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7608 };
7609 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7610     render : function(td){
7611         this.td = td;
7612         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7613     },
7614     
7615     /**
7616      * Removes and destroys this button
7617      */
7618     destroy : function(){
7619         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7620         this.td.parentNode.removeChild(this.td);
7621     },
7622     
7623     /**
7624      * Shows this button
7625      */
7626     show: function(){
7627         this.hidden = false;
7628         this.td.style.display = "";
7629     },
7630     
7631     /**
7632      * Hides this button
7633      */
7634     hide: function(){
7635         this.hidden = true;
7636         this.td.style.display = "none";
7637     }
7638 });
7639
7640 // backwards compat
7641 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7642  * Based on:
7643  * Ext JS Library 1.1.1
7644  * Copyright(c) 2006-2007, Ext JS, LLC.
7645  *
7646  * Originally Released Under LGPL - original licence link has changed is not relivant.
7647  *
7648  * Fork - LGPL
7649  * <script type="text/javascript">
7650  */
7651  
7652 /**
7653  * @class Roo.PagingToolbar
7654  * @extends Roo.Toolbar
7655  * @children   Roo.Toolbar.Item Roo.form.Field
7656  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7657  * @constructor
7658  * Create a new PagingToolbar
7659  * @param {Object} config The config object
7660  */
7661 Roo.PagingToolbar = function(el, ds, config)
7662 {
7663     // old args format still supported... - xtype is prefered..
7664     if (typeof(el) == 'object' && el.xtype) {
7665         // created from xtype...
7666         config = el;
7667         ds = el.dataSource;
7668         el = config.container;
7669     }
7670     var items = [];
7671     if (config.items) {
7672         items = config.items;
7673         config.items = [];
7674     }
7675     
7676     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7677     this.ds = ds;
7678     this.cursor = 0;
7679     this.renderButtons(this.el);
7680     this.bind(ds);
7681     
7682     // supprot items array.
7683    
7684     Roo.each(items, function(e) {
7685         this.add(Roo.factory(e));
7686     },this);
7687     
7688 };
7689
7690 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7691     /**
7692      * @cfg {Roo.data.Store} dataSource
7693      * The underlying data store providing the paged data
7694      */
7695     /**
7696      * @cfg {String/HTMLElement/Element} container
7697      * container The id or element that will contain the toolbar
7698      */
7699     /**
7700      * @cfg {Boolean} displayInfo
7701      * True to display the displayMsg (defaults to false)
7702      */
7703     /**
7704      * @cfg {Number} pageSize
7705      * The number of records to display per page (defaults to 20)
7706      */
7707     pageSize: 20,
7708     /**
7709      * @cfg {String} displayMsg
7710      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7711      */
7712     displayMsg : 'Displaying {0} - {1} of {2}',
7713     /**
7714      * @cfg {String} emptyMsg
7715      * The message to display when no records are found (defaults to "No data to display")
7716      */
7717     emptyMsg : 'No data to display',
7718     /**
7719      * Customizable piece of the default paging text (defaults to "Page")
7720      * @type String
7721      */
7722     beforePageText : "Page",
7723     /**
7724      * Customizable piece of the default paging text (defaults to "of %0")
7725      * @type String
7726      */
7727     afterPageText : "of {0}",
7728     /**
7729      * Customizable piece of the default paging text (defaults to "First Page")
7730      * @type String
7731      */
7732     firstText : "First Page",
7733     /**
7734      * Customizable piece of the default paging text (defaults to "Previous Page")
7735      * @type String
7736      */
7737     prevText : "Previous Page",
7738     /**
7739      * Customizable piece of the default paging text (defaults to "Next Page")
7740      * @type String
7741      */
7742     nextText : "Next Page",
7743     /**
7744      * Customizable piece of the default paging text (defaults to "Last Page")
7745      * @type String
7746      */
7747     lastText : "Last Page",
7748     /**
7749      * Customizable piece of the default paging text (defaults to "Refresh")
7750      * @type String
7751      */
7752     refreshText : "Refresh",
7753
7754     // private
7755     renderButtons : function(el){
7756         Roo.PagingToolbar.superclass.render.call(this, el);
7757         this.first = this.addButton({
7758             tooltip: this.firstText,
7759             cls: "x-btn-icon x-grid-page-first",
7760             disabled: true,
7761             handler: this.onClick.createDelegate(this, ["first"])
7762         });
7763         this.prev = this.addButton({
7764             tooltip: this.prevText,
7765             cls: "x-btn-icon x-grid-page-prev",
7766             disabled: true,
7767             handler: this.onClick.createDelegate(this, ["prev"])
7768         });
7769         //this.addSeparator();
7770         this.add(this.beforePageText);
7771         this.field = Roo.get(this.addDom({
7772            tag: "input",
7773            type: "text",
7774            size: "3",
7775            value: "1",
7776            cls: "x-grid-page-number"
7777         }).el);
7778         this.field.on("keydown", this.onPagingKeydown, this);
7779         this.field.on("focus", function(){this.dom.select();});
7780         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7781         this.field.setHeight(18);
7782         //this.addSeparator();
7783         this.next = this.addButton({
7784             tooltip: this.nextText,
7785             cls: "x-btn-icon x-grid-page-next",
7786             disabled: true,
7787             handler: this.onClick.createDelegate(this, ["next"])
7788         });
7789         this.last = this.addButton({
7790             tooltip: this.lastText,
7791             cls: "x-btn-icon x-grid-page-last",
7792             disabled: true,
7793             handler: this.onClick.createDelegate(this, ["last"])
7794         });
7795         //this.addSeparator();
7796         this.loading = this.addButton({
7797             tooltip: this.refreshText,
7798             cls: "x-btn-icon x-grid-loading",
7799             handler: this.onClick.createDelegate(this, ["refresh"])
7800         });
7801
7802         if(this.displayInfo){
7803             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7804         }
7805     },
7806
7807     // private
7808     updateInfo : function(){
7809         if(this.displayEl){
7810             var count = this.ds.getCount();
7811             var msg = count == 0 ?
7812                 this.emptyMsg :
7813                 String.format(
7814                     this.displayMsg,
7815                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7816                 );
7817             this.displayEl.update(msg);
7818         }
7819     },
7820
7821     // private
7822     onLoad : function(ds, r, o){
7823        this.cursor = o.params ? o.params.start : 0;
7824        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7825
7826        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7827        this.field.dom.value = ap;
7828        this.first.setDisabled(ap == 1);
7829        this.prev.setDisabled(ap == 1);
7830        this.next.setDisabled(ap == ps);
7831        this.last.setDisabled(ap == ps);
7832        this.loading.enable();
7833        this.updateInfo();
7834     },
7835
7836     // private
7837     getPageData : function(){
7838         var total = this.ds.getTotalCount();
7839         return {
7840             total : total,
7841             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7842             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7843         };
7844     },
7845
7846     // private
7847     onLoadError : function(){
7848         this.loading.enable();
7849     },
7850
7851     // private
7852     onPagingKeydown : function(e){
7853         var k = e.getKey();
7854         var d = this.getPageData();
7855         if(k == e.RETURN){
7856             var v = this.field.dom.value, pageNum;
7857             if(!v || isNaN(pageNum = parseInt(v, 10))){
7858                 this.field.dom.value = d.activePage;
7859                 return;
7860             }
7861             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7862             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7863             e.stopEvent();
7864         }
7865         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))
7866         {
7867           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7868           this.field.dom.value = pageNum;
7869           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7870           e.stopEvent();
7871         }
7872         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7873         {
7874           var v = this.field.dom.value, pageNum; 
7875           var increment = (e.shiftKey) ? 10 : 1;
7876           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7877             increment *= -1;
7878           }
7879           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7880             this.field.dom.value = d.activePage;
7881             return;
7882           }
7883           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7884           {
7885             this.field.dom.value = parseInt(v, 10) + increment;
7886             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7887             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7888           }
7889           e.stopEvent();
7890         }
7891     },
7892
7893     // private
7894     beforeLoad : function(){
7895         if(this.loading){
7896             this.loading.disable();
7897         }
7898     },
7899
7900     // private
7901     onClick : function(which){
7902         var ds = this.ds;
7903         switch(which){
7904             case "first":
7905                 ds.load({params:{start: 0, limit: this.pageSize}});
7906             break;
7907             case "prev":
7908                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7909             break;
7910             case "next":
7911                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7912             break;
7913             case "last":
7914                 var total = ds.getTotalCount();
7915                 var extra = total % this.pageSize;
7916                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7917                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7918             break;
7919             case "refresh":
7920                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7921             break;
7922         }
7923     },
7924
7925     /**
7926      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7927      * @param {Roo.data.Store} store The data store to unbind
7928      */
7929     unbind : function(ds){
7930         ds.un("beforeload", this.beforeLoad, this);
7931         ds.un("load", this.onLoad, this);
7932         ds.un("loadexception", this.onLoadError, this);
7933         ds.un("remove", this.updateInfo, this);
7934         ds.un("add", this.updateInfo, this);
7935         this.ds = undefined;
7936     },
7937
7938     /**
7939      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7940      * @param {Roo.data.Store} store The data store to bind
7941      */
7942     bind : function(ds){
7943         ds.on("beforeload", this.beforeLoad, this);
7944         ds.on("load", this.onLoad, this);
7945         ds.on("loadexception", this.onLoadError, this);
7946         ds.on("remove", this.updateInfo, this);
7947         ds.on("add", this.updateInfo, this);
7948         this.ds = ds;
7949     }
7950 });/*
7951  * Based on:
7952  * Ext JS Library 1.1.1
7953  * Copyright(c) 2006-2007, Ext JS, LLC.
7954  *
7955  * Originally Released Under LGPL - original licence link has changed is not relivant.
7956  *
7957  * Fork - LGPL
7958  * <script type="text/javascript">
7959  */
7960
7961 /**
7962  * @class Roo.Resizable
7963  * @extends Roo.util.Observable
7964  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7965  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7966  * 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
7967  * the element will be wrapped for you automatically.</p>
7968  * <p>Here is the list of valid resize handles:</p>
7969  * <pre>
7970 Value   Description
7971 ------  -------------------
7972  'n'     north
7973  's'     south
7974  'e'     east
7975  'w'     west
7976  'nw'    northwest
7977  'sw'    southwest
7978  'se'    southeast
7979  'ne'    northeast
7980  'hd'    horizontal drag
7981  'all'   all
7982 </pre>
7983  * <p>Here's an example showing the creation of a typical Resizable:</p>
7984  * <pre><code>
7985 var resizer = new Roo.Resizable("element-id", {
7986     handles: 'all',
7987     minWidth: 200,
7988     minHeight: 100,
7989     maxWidth: 500,
7990     maxHeight: 400,
7991     pinned: true
7992 });
7993 resizer.on("resize", myHandler);
7994 </code></pre>
7995  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7996  * resizer.east.setDisplayed(false);</p>
7997  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
7998  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
7999  * resize operation's new size (defaults to [0, 0])
8000  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8001  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8002  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8003  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8004  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8005  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8006  * @cfg {Number} width The width of the element in pixels (defaults to null)
8007  * @cfg {Number} height The height of the element in pixels (defaults to null)
8008  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8009  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8010  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8011  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8012  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8013  * in favor of the handles config option (defaults to false)
8014  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8015  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8016  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8017  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8018  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8019  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8020  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8021  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8022  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8023  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8024  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8025  * @constructor
8026  * Create a new resizable component
8027  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8028  * @param {Object} config configuration options
8029   */
8030 Roo.Resizable = function(el, config)
8031 {
8032     this.el = Roo.get(el);
8033
8034     if(config && config.wrap){
8035         config.resizeChild = this.el;
8036         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8037         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8038         this.el.setStyle("overflow", "hidden");
8039         this.el.setPositioning(config.resizeChild.getPositioning());
8040         config.resizeChild.clearPositioning();
8041         if(!config.width || !config.height){
8042             var csize = config.resizeChild.getSize();
8043             this.el.setSize(csize.width, csize.height);
8044         }
8045         if(config.pinned && !config.adjustments){
8046             config.adjustments = "auto";
8047         }
8048     }
8049
8050     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8051     this.proxy.unselectable();
8052     this.proxy.enableDisplayMode('block');
8053
8054     Roo.apply(this, config);
8055
8056     if(this.pinned){
8057         this.disableTrackOver = true;
8058         this.el.addClass("x-resizable-pinned");
8059     }
8060     // if the element isn't positioned, make it relative
8061     var position = this.el.getStyle("position");
8062     if(position != "absolute" && position != "fixed"){
8063         this.el.setStyle("position", "relative");
8064     }
8065     if(!this.handles){ // no handles passed, must be legacy style
8066         this.handles = 's,e,se';
8067         if(this.multiDirectional){
8068             this.handles += ',n,w';
8069         }
8070     }
8071     if(this.handles == "all"){
8072         this.handles = "n s e w ne nw se sw";
8073     }
8074     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8075     var ps = Roo.Resizable.positions;
8076     for(var i = 0, len = hs.length; i < len; i++){
8077         if(hs[i] && ps[hs[i]]){
8078             var pos = ps[hs[i]];
8079             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8080         }
8081     }
8082     // legacy
8083     this.corner = this.southeast;
8084     
8085     // updateBox = the box can move..
8086     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8087         this.updateBox = true;
8088     }
8089
8090     this.activeHandle = null;
8091
8092     if(this.resizeChild){
8093         if(typeof this.resizeChild == "boolean"){
8094             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8095         }else{
8096             this.resizeChild = Roo.get(this.resizeChild, true);
8097         }
8098     }
8099     
8100     if(this.adjustments == "auto"){
8101         var rc = this.resizeChild;
8102         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8103         if(rc && (hw || hn)){
8104             rc.position("relative");
8105             rc.setLeft(hw ? hw.el.getWidth() : 0);
8106             rc.setTop(hn ? hn.el.getHeight() : 0);
8107         }
8108         this.adjustments = [
8109             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8110             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8111         ];
8112     }
8113
8114     if(this.draggable){
8115         this.dd = this.dynamic ?
8116             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8117         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8118     }
8119
8120     // public events
8121     this.addEvents({
8122         /**
8123          * @event beforeresize
8124          * Fired before resize is allowed. Set enabled to false to cancel resize.
8125          * @param {Roo.Resizable} this
8126          * @param {Roo.EventObject} e The mousedown event
8127          */
8128         "beforeresize" : true,
8129         /**
8130          * @event resizing
8131          * Fired a resizing.
8132          * @param {Roo.Resizable} this
8133          * @param {Number} x The new x position
8134          * @param {Number} y The new y position
8135          * @param {Number} w The new w width
8136          * @param {Number} h The new h hight
8137          * @param {Roo.EventObject} e The mouseup event
8138          */
8139         "resizing" : true,
8140         /**
8141          * @event resize
8142          * Fired after a resize.
8143          * @param {Roo.Resizable} this
8144          * @param {Number} width The new width
8145          * @param {Number} height The new height
8146          * @param {Roo.EventObject} e The mouseup event
8147          */
8148         "resize" : true
8149     });
8150
8151     if(this.width !== null && this.height !== null){
8152         this.resizeTo(this.width, this.height);
8153     }else{
8154         this.updateChildSize();
8155     }
8156     if(Roo.isIE){
8157         this.el.dom.style.zoom = 1;
8158     }
8159     Roo.Resizable.superclass.constructor.call(this);
8160 };
8161
8162 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8163         resizeChild : false,
8164         adjustments : [0, 0],
8165         minWidth : 5,
8166         minHeight : 5,
8167         maxWidth : 10000,
8168         maxHeight : 10000,
8169         enabled : true,
8170         animate : false,
8171         duration : .35,
8172         dynamic : false,
8173         handles : false,
8174         multiDirectional : false,
8175         disableTrackOver : false,
8176         easing : 'easeOutStrong',
8177         widthIncrement : 0,
8178         heightIncrement : 0,
8179         pinned : false,
8180         width : null,
8181         height : null,
8182         preserveRatio : false,
8183         transparent: false,
8184         minX: 0,
8185         minY: 0,
8186         draggable: false,
8187
8188         /**
8189          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8190          */
8191         constrainTo: undefined,
8192         /**
8193          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8194          */
8195         resizeRegion: undefined,
8196
8197
8198     /**
8199      * Perform a manual resize
8200      * @param {Number} width
8201      * @param {Number} height
8202      */
8203     resizeTo : function(width, height){
8204         this.el.setSize(width, height);
8205         this.updateChildSize();
8206         this.fireEvent("resize", this, width, height, null);
8207     },
8208
8209     // private
8210     startSizing : function(e, handle){
8211         this.fireEvent("beforeresize", this, e);
8212         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8213
8214             if(!this.overlay){
8215                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8216                 this.overlay.unselectable();
8217                 this.overlay.enableDisplayMode("block");
8218                 this.overlay.on("mousemove", this.onMouseMove, this);
8219                 this.overlay.on("mouseup", this.onMouseUp, this);
8220             }
8221             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8222
8223             this.resizing = true;
8224             this.startBox = this.el.getBox();
8225             this.startPoint = e.getXY();
8226             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8227                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8228
8229             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8230             this.overlay.show();
8231
8232             if(this.constrainTo) {
8233                 var ct = Roo.get(this.constrainTo);
8234                 this.resizeRegion = ct.getRegion().adjust(
8235                     ct.getFrameWidth('t'),
8236                     ct.getFrameWidth('l'),
8237                     -ct.getFrameWidth('b'),
8238                     -ct.getFrameWidth('r')
8239                 );
8240             }
8241
8242             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8243             this.proxy.show();
8244             this.proxy.setBox(this.startBox);
8245             if(!this.dynamic){
8246                 this.proxy.setStyle('visibility', 'visible');
8247             }
8248         }
8249     },
8250
8251     // private
8252     onMouseDown : function(handle, e){
8253         if(this.enabled){
8254             e.stopEvent();
8255             this.activeHandle = handle;
8256             this.startSizing(e, handle);
8257         }
8258     },
8259
8260     // private
8261     onMouseUp : function(e){
8262         var size = this.resizeElement();
8263         this.resizing = false;
8264         this.handleOut();
8265         this.overlay.hide();
8266         this.proxy.hide();
8267         this.fireEvent("resize", this, size.width, size.height, e);
8268     },
8269
8270     // private
8271     updateChildSize : function(){
8272         
8273         if(this.resizeChild){
8274             var el = this.el;
8275             var child = this.resizeChild;
8276             var adj = this.adjustments;
8277             if(el.dom.offsetWidth){
8278                 var b = el.getSize(true);
8279                 child.setSize(b.width+adj[0], b.height+adj[1]);
8280             }
8281             // Second call here for IE
8282             // The first call enables instant resizing and
8283             // the second call corrects scroll bars if they
8284             // exist
8285             if(Roo.isIE){
8286                 setTimeout(function(){
8287                     if(el.dom.offsetWidth){
8288                         var b = el.getSize(true);
8289                         child.setSize(b.width+adj[0], b.height+adj[1]);
8290                     }
8291                 }, 10);
8292             }
8293         }
8294     },
8295
8296     // private
8297     snap : function(value, inc, min){
8298         if(!inc || !value) {
8299             return value;
8300         }
8301         var newValue = value;
8302         var m = value % inc;
8303         if(m > 0){
8304             if(m > (inc/2)){
8305                 newValue = value + (inc-m);
8306             }else{
8307                 newValue = value - m;
8308             }
8309         }
8310         return Math.max(min, newValue);
8311     },
8312
8313     // private
8314     resizeElement : function(){
8315         var box = this.proxy.getBox();
8316         if(this.updateBox){
8317             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8318         }else{
8319             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8320         }
8321         this.updateChildSize();
8322         if(!this.dynamic){
8323             this.proxy.hide();
8324         }
8325         return box;
8326     },
8327
8328     // private
8329     constrain : function(v, diff, m, mx){
8330         if(v - diff < m){
8331             diff = v - m;
8332         }else if(v - diff > mx){
8333             diff = mx - v;
8334         }
8335         return diff;
8336     },
8337
8338     // private
8339     onMouseMove : function(e){
8340         
8341         if(this.enabled){
8342             try{// try catch so if something goes wrong the user doesn't get hung
8343
8344             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8345                 return;
8346             }
8347
8348             //var curXY = this.startPoint;
8349             var curSize = this.curSize || this.startBox;
8350             var x = this.startBox.x, y = this.startBox.y;
8351             var ox = x, oy = y;
8352             var w = curSize.width, h = curSize.height;
8353             var ow = w, oh = h;
8354             var mw = this.minWidth, mh = this.minHeight;
8355             var mxw = this.maxWidth, mxh = this.maxHeight;
8356             var wi = this.widthIncrement;
8357             var hi = this.heightIncrement;
8358
8359             var eventXY = e.getXY();
8360             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8361             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8362
8363             var pos = this.activeHandle.position;
8364
8365             switch(pos){
8366                 case "east":
8367                     w += diffX;
8368                     w = Math.min(Math.max(mw, w), mxw);
8369                     break;
8370              
8371                 case "south":
8372                     h += diffY;
8373                     h = Math.min(Math.max(mh, h), mxh);
8374                     break;
8375                 case "southeast":
8376                     w += diffX;
8377                     h += diffY;
8378                     w = Math.min(Math.max(mw, w), mxw);
8379                     h = Math.min(Math.max(mh, h), mxh);
8380                     break;
8381                 case "north":
8382                     diffY = this.constrain(h, diffY, mh, mxh);
8383                     y += diffY;
8384                     h -= diffY;
8385                     break;
8386                 case "hdrag":
8387                     
8388                     if (wi) {
8389                         var adiffX = Math.abs(diffX);
8390                         var sub = (adiffX % wi); // how much 
8391                         if (sub > (wi/2)) { // far enough to snap
8392                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8393                         } else {
8394                             // remove difference.. 
8395                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8396                         }
8397                     }
8398                     x += diffX;
8399                     x = Math.max(this.minX, x);
8400                     break;
8401                 case "west":
8402                     diffX = this.constrain(w, diffX, mw, mxw);
8403                     x += diffX;
8404                     w -= diffX;
8405                     break;
8406                 case "northeast":
8407                     w += diffX;
8408                     w = Math.min(Math.max(mw, w), mxw);
8409                     diffY = this.constrain(h, diffY, mh, mxh);
8410                     y += diffY;
8411                     h -= diffY;
8412                     break;
8413                 case "northwest":
8414                     diffX = this.constrain(w, diffX, mw, mxw);
8415                     diffY = this.constrain(h, diffY, mh, mxh);
8416                     y += diffY;
8417                     h -= diffY;
8418                     x += diffX;
8419                     w -= diffX;
8420                     break;
8421                case "southwest":
8422                     diffX = this.constrain(w, diffX, mw, mxw);
8423                     h += diffY;
8424                     h = Math.min(Math.max(mh, h), mxh);
8425                     x += diffX;
8426                     w -= diffX;
8427                     break;
8428             }
8429
8430             var sw = this.snap(w, wi, mw);
8431             var sh = this.snap(h, hi, mh);
8432             if(sw != w || sh != h){
8433                 switch(pos){
8434                     case "northeast":
8435                         y -= sh - h;
8436                     break;
8437                     case "north":
8438                         y -= sh - h;
8439                         break;
8440                     case "southwest":
8441                         x -= sw - w;
8442                     break;
8443                     case "west":
8444                         x -= sw - w;
8445                         break;
8446                     case "northwest":
8447                         x -= sw - w;
8448                         y -= sh - h;
8449                     break;
8450                 }
8451                 w = sw;
8452                 h = sh;
8453             }
8454
8455             if(this.preserveRatio){
8456                 switch(pos){
8457                     case "southeast":
8458                     case "east":
8459                         h = oh * (w/ow);
8460                         h = Math.min(Math.max(mh, h), mxh);
8461                         w = ow * (h/oh);
8462                        break;
8463                     case "south":
8464                         w = ow * (h/oh);
8465                         w = Math.min(Math.max(mw, w), mxw);
8466                         h = oh * (w/ow);
8467                         break;
8468                     case "northeast":
8469                         w = ow * (h/oh);
8470                         w = Math.min(Math.max(mw, w), mxw);
8471                         h = oh * (w/ow);
8472                     break;
8473                     case "north":
8474                         var tw = w;
8475                         w = ow * (h/oh);
8476                         w = Math.min(Math.max(mw, w), mxw);
8477                         h = oh * (w/ow);
8478                         x += (tw - w) / 2;
8479                         break;
8480                     case "southwest":
8481                         h = oh * (w/ow);
8482                         h = Math.min(Math.max(mh, h), mxh);
8483                         var tw = w;
8484                         w = ow * (h/oh);
8485                         x += tw - w;
8486                         break;
8487                     case "west":
8488                         var th = h;
8489                         h = oh * (w/ow);
8490                         h = Math.min(Math.max(mh, h), mxh);
8491                         y += (th - h) / 2;
8492                         var tw = w;
8493                         w = ow * (h/oh);
8494                         x += tw - w;
8495                        break;
8496                     case "northwest":
8497                         var tw = w;
8498                         var th = h;
8499                         h = oh * (w/ow);
8500                         h = Math.min(Math.max(mh, h), mxh);
8501                         w = ow * (h/oh);
8502                         y += th - h;
8503                         x += tw - w;
8504                        break;
8505
8506                 }
8507             }
8508             if (pos == 'hdrag') {
8509                 w = ow;
8510             }
8511             this.proxy.setBounds(x, y, w, h);
8512             if(this.dynamic){
8513                 this.resizeElement();
8514             }
8515             }catch(e){}
8516         }
8517         this.fireEvent("resizing", this, x, y, w, h, e);
8518     },
8519
8520     // private
8521     handleOver : function(){
8522         if(this.enabled){
8523             this.el.addClass("x-resizable-over");
8524         }
8525     },
8526
8527     // private
8528     handleOut : function(){
8529         if(!this.resizing){
8530             this.el.removeClass("x-resizable-over");
8531         }
8532     },
8533
8534     /**
8535      * Returns the element this component is bound to.
8536      * @return {Roo.Element}
8537      */
8538     getEl : function(){
8539         return this.el;
8540     },
8541
8542     /**
8543      * Returns the resizeChild element (or null).
8544      * @return {Roo.Element}
8545      */
8546     getResizeChild : function(){
8547         return this.resizeChild;
8548     },
8549     groupHandler : function()
8550     {
8551         
8552     },
8553     /**
8554      * Destroys this resizable. If the element was wrapped and
8555      * removeEl is not true then the element remains.
8556      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8557      */
8558     destroy : function(removeEl){
8559         this.proxy.remove();
8560         if(this.overlay){
8561             this.overlay.removeAllListeners();
8562             this.overlay.remove();
8563         }
8564         var ps = Roo.Resizable.positions;
8565         for(var k in ps){
8566             if(typeof ps[k] != "function" && this[ps[k]]){
8567                 var h = this[ps[k]];
8568                 h.el.removeAllListeners();
8569                 h.el.remove();
8570             }
8571         }
8572         if(removeEl){
8573             this.el.update("");
8574             this.el.remove();
8575         }
8576     }
8577 });
8578
8579 // private
8580 // hash to map config positions to true positions
8581 Roo.Resizable.positions = {
8582     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8583     hd: "hdrag"
8584 };
8585
8586 // private
8587 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8588     if(!this.tpl){
8589         // only initialize the template if resizable is used
8590         var tpl = Roo.DomHelper.createTemplate(
8591             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8592         );
8593         tpl.compile();
8594         Roo.Resizable.Handle.prototype.tpl = tpl;
8595     }
8596     this.position = pos;
8597     this.rz = rz;
8598     // show north drag fro topdra
8599     var handlepos = pos == 'hdrag' ? 'north' : pos;
8600     
8601     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8602     if (pos == 'hdrag') {
8603         this.el.setStyle('cursor', 'pointer');
8604     }
8605     this.el.unselectable();
8606     if(transparent){
8607         this.el.setOpacity(0);
8608     }
8609     this.el.on("mousedown", this.onMouseDown, this);
8610     if(!disableTrackOver){
8611         this.el.on("mouseover", this.onMouseOver, this);
8612         this.el.on("mouseout", this.onMouseOut, this);
8613     }
8614 };
8615
8616 // private
8617 Roo.Resizable.Handle.prototype = {
8618     afterResize : function(rz){
8619         Roo.log('after?');
8620         // do nothing
8621     },
8622     // private
8623     onMouseDown : function(e){
8624         this.rz.onMouseDown(this, e);
8625     },
8626     // private
8627     onMouseOver : function(e){
8628         this.rz.handleOver(this, e);
8629     },
8630     // private
8631     onMouseOut : function(e){
8632         this.rz.handleOut(this, e);
8633     }
8634 };/*
8635  * Based on:
8636  * Ext JS Library 1.1.1
8637  * Copyright(c) 2006-2007, Ext JS, LLC.
8638  *
8639  * Originally Released Under LGPL - original licence link has changed is not relivant.
8640  *
8641  * Fork - LGPL
8642  * <script type="text/javascript">
8643  */
8644
8645 /**
8646  * @class Roo.Editor
8647  * @extends Roo.Component
8648  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8649  * @constructor
8650  * Create a new Editor
8651  * @param {Roo.form.Field} field The Field object (or descendant)
8652  * @param {Object} config The config object
8653  */
8654 Roo.Editor = function(field, config){
8655     Roo.Editor.superclass.constructor.call(this, config);
8656     this.field = field;
8657     this.addEvents({
8658         /**
8659              * @event beforestartedit
8660              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8661              * false from the handler of this event.
8662              * @param {Editor} this
8663              * @param {Roo.Element} boundEl The underlying element bound to this editor
8664              * @param {Mixed} value The field value being set
8665              */
8666         "beforestartedit" : true,
8667         /**
8668              * @event startedit
8669              * Fires when this editor is displayed
8670              * @param {Roo.Element} boundEl The underlying element bound to this editor
8671              * @param {Mixed} value The starting field value
8672              */
8673         "startedit" : true,
8674         /**
8675              * @event beforecomplete
8676              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8677              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8678              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8679              * event will not fire since no edit actually occurred.
8680              * @param {Editor} this
8681              * @param {Mixed} value The current field value
8682              * @param {Mixed} startValue The original field value
8683              */
8684         "beforecomplete" : true,
8685         /**
8686              * @event complete
8687              * Fires after editing is complete and any changed value has been written to the underlying field.
8688              * @param {Editor} this
8689              * @param {Mixed} value The current field value
8690              * @param {Mixed} startValue The original field value
8691              */
8692         "complete" : true,
8693         /**
8694          * @event specialkey
8695          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8696          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8697          * @param {Roo.form.Field} this
8698          * @param {Roo.EventObject} e The event object
8699          */
8700         "specialkey" : true
8701     });
8702 };
8703
8704 Roo.extend(Roo.Editor, Roo.Component, {
8705     /**
8706      * @cfg {Boolean/String} autosize
8707      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8708      * or "height" to adopt the height only (defaults to false)
8709      */
8710     /**
8711      * @cfg {Boolean} revertInvalid
8712      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8713      * validation fails (defaults to true)
8714      */
8715     /**
8716      * @cfg {Boolean} ignoreNoChange
8717      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8718      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8719      * will never be ignored.
8720      */
8721     /**
8722      * @cfg {Boolean} hideEl
8723      * False to keep the bound element visible while the editor is displayed (defaults to true)
8724      */
8725     /**
8726      * @cfg {Mixed} value
8727      * The data value of the underlying field (defaults to "")
8728      */
8729     value : "",
8730     /**
8731      * @cfg {String} alignment
8732      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8733      */
8734     alignment: "c-c?",
8735     /**
8736      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8737      * for bottom-right shadow (defaults to "frame")
8738      */
8739     shadow : "frame",
8740     /**
8741      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8742      */
8743     constrain : false,
8744     /**
8745      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8746      */
8747     completeOnEnter : false,
8748     /**
8749      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8750      */
8751     cancelOnEsc : false,
8752     /**
8753      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8754      */
8755     updateEl : false,
8756
8757     // private
8758     onRender : function(ct, position){
8759         this.el = new Roo.Layer({
8760             shadow: this.shadow,
8761             cls: "x-editor",
8762             parentEl : ct,
8763             shim : this.shim,
8764             shadowOffset:4,
8765             id: this.id,
8766             constrain: this.constrain
8767         });
8768         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8769         if(this.field.msgTarget != 'title'){
8770             this.field.msgTarget = 'qtip';
8771         }
8772         this.field.render(this.el);
8773         if(Roo.isGecko){
8774             this.field.el.dom.setAttribute('autocomplete', 'off');
8775         }
8776         this.field.on("specialkey", this.onSpecialKey, this);
8777         if(this.swallowKeys){
8778             this.field.el.swallowEvent(['keydown','keypress']);
8779         }
8780         this.field.show();
8781         this.field.on("blur", this.onBlur, this);
8782         if(this.field.grow){
8783             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8784         }
8785     },
8786
8787     onSpecialKey : function(field, e)
8788     {
8789         //Roo.log('editor onSpecialKey');
8790         if(this.completeOnEnter && e.getKey() == e.ENTER){
8791             e.stopEvent();
8792             this.completeEdit();
8793             return;
8794         }
8795         // do not fire special key otherwise it might hide close the editor...
8796         if(e.getKey() == e.ENTER){    
8797             return;
8798         }
8799         if(this.cancelOnEsc && e.getKey() == e.ESC){
8800             this.cancelEdit();
8801             return;
8802         } 
8803         this.fireEvent('specialkey', field, e);
8804     
8805     },
8806
8807     /**
8808      * Starts the editing process and shows the editor.
8809      * @param {String/HTMLElement/Element} el The element to edit
8810      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8811       * to the innerHTML of el.
8812      */
8813     startEdit : function(el, value){
8814         if(this.editing){
8815             this.completeEdit();
8816         }
8817         this.boundEl = Roo.get(el);
8818         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8819         if(!this.rendered){
8820             this.render(this.parentEl || document.body);
8821         }
8822         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8823             return;
8824         }
8825         this.startValue = v;
8826         this.field.setValue(v);
8827         if(this.autoSize){
8828             var sz = this.boundEl.getSize();
8829             switch(this.autoSize){
8830                 case "width":
8831                 this.setSize(sz.width,  "");
8832                 break;
8833                 case "height":
8834                 this.setSize("",  sz.height);
8835                 break;
8836                 default:
8837                 this.setSize(sz.width,  sz.height);
8838             }
8839         }
8840         this.el.alignTo(this.boundEl, this.alignment);
8841         this.editing = true;
8842         if(Roo.QuickTips){
8843             Roo.QuickTips.disable();
8844         }
8845         this.show();
8846     },
8847
8848     /**
8849      * Sets the height and width of this editor.
8850      * @param {Number} width The new width
8851      * @param {Number} height The new height
8852      */
8853     setSize : function(w, h){
8854         this.field.setSize(w, h);
8855         if(this.el){
8856             this.el.sync();
8857         }
8858     },
8859
8860     /**
8861      * Realigns the editor to the bound field based on the current alignment config value.
8862      */
8863     realign : function(){
8864         this.el.alignTo(this.boundEl, this.alignment);
8865     },
8866
8867     /**
8868      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8869      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8870      */
8871     completeEdit : function(remainVisible){
8872         if(!this.editing){
8873             return;
8874         }
8875         var v = this.getValue();
8876         if(this.revertInvalid !== false && !this.field.isValid()){
8877             v = this.startValue;
8878             this.cancelEdit(true);
8879         }
8880         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8881             this.editing = false;
8882             this.hide();
8883             return;
8884         }
8885         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8886             this.editing = false;
8887             if(this.updateEl && this.boundEl){
8888                 this.boundEl.update(v);
8889             }
8890             if(remainVisible !== true){
8891                 this.hide();
8892             }
8893             this.fireEvent("complete", this, v, this.startValue);
8894         }
8895     },
8896
8897     // private
8898     onShow : function(){
8899         this.el.show();
8900         if(this.hideEl !== false){
8901             this.boundEl.hide();
8902         }
8903         this.field.show();
8904         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8905             this.fixIEFocus = true;
8906             this.deferredFocus.defer(50, this);
8907         }else{
8908             this.field.focus();
8909         }
8910         this.fireEvent("startedit", this.boundEl, this.startValue);
8911     },
8912
8913     deferredFocus : function(){
8914         if(this.editing){
8915             this.field.focus();
8916         }
8917     },
8918
8919     /**
8920      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8921      * reverted to the original starting value.
8922      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8923      * cancel (defaults to false)
8924      */
8925     cancelEdit : function(remainVisible){
8926         if(this.editing){
8927             this.setValue(this.startValue);
8928             if(remainVisible !== true){
8929                 this.hide();
8930             }
8931         }
8932     },
8933
8934     // private
8935     onBlur : function(){
8936         if(this.allowBlur !== true && this.editing){
8937             this.completeEdit();
8938         }
8939     },
8940
8941     // private
8942     onHide : function(){
8943         if(this.editing){
8944             this.completeEdit();
8945             return;
8946         }
8947         this.field.blur();
8948         if(this.field.collapse){
8949             this.field.collapse();
8950         }
8951         this.el.hide();
8952         if(this.hideEl !== false){
8953             this.boundEl.show();
8954         }
8955         if(Roo.QuickTips){
8956             Roo.QuickTips.enable();
8957         }
8958     },
8959
8960     /**
8961      * Sets the data value of the editor
8962      * @param {Mixed} value Any valid value supported by the underlying field
8963      */
8964     setValue : function(v){
8965         this.field.setValue(v);
8966     },
8967
8968     /**
8969      * Gets the data value of the editor
8970      * @return {Mixed} The data value
8971      */
8972     getValue : function(){
8973         return this.field.getValue();
8974     }
8975 });/*
8976  * Based on:
8977  * Ext JS Library 1.1.1
8978  * Copyright(c) 2006-2007, Ext JS, LLC.
8979  *
8980  * Originally Released Under LGPL - original licence link has changed is not relivant.
8981  *
8982  * Fork - LGPL
8983  * <script type="text/javascript">
8984  */
8985  
8986 /**
8987  * @class Roo.BasicDialog
8988  * @extends Roo.util.Observable
8989  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8990  * <pre><code>
8991 var dlg = new Roo.BasicDialog("my-dlg", {
8992     height: 200,
8993     width: 300,
8994     minHeight: 100,
8995     minWidth: 150,
8996     modal: true,
8997     proxyDrag: true,
8998     shadow: true
8999 });
9000 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9001 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9002 dlg.addButton('Cancel', dlg.hide, dlg);
9003 dlg.show();
9004 </code></pre>
9005   <b>A Dialog should always be a direct child of the body element.</b>
9006  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9007  * @cfg {String} title Default text to display in the title bar (defaults to null)
9008  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9009  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9010  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9011  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9012  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9013  * (defaults to null with no animation)
9014  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9015  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9016  * property for valid values (defaults to 'all')
9017  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9018  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9019  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9020  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9021  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9022  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9023  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9024  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9025  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9026  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9027  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9028  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9029  * draggable = true (defaults to false)
9030  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9031  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9032  * shadow (defaults to false)
9033  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9034  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9035  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9036  * @cfg {Array} buttons Array of buttons
9037  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9038  * @constructor
9039  * Create a new BasicDialog.
9040  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9041  * @param {Object} config Configuration options
9042  */
9043 Roo.BasicDialog = function(el, config){
9044     this.el = Roo.get(el);
9045     var dh = Roo.DomHelper;
9046     if(!this.el && config && config.autoCreate){
9047         if(typeof config.autoCreate == "object"){
9048             if(!config.autoCreate.id){
9049                 config.autoCreate.id = el;
9050             }
9051             this.el = dh.append(document.body,
9052                         config.autoCreate, true);
9053         }else{
9054             this.el = dh.append(document.body,
9055                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9056         }
9057     }
9058     el = this.el;
9059     el.setDisplayed(true);
9060     el.hide = this.hideAction;
9061     this.id = el.id;
9062     el.addClass("x-dlg");
9063
9064     Roo.apply(this, config);
9065
9066     this.proxy = el.createProxy("x-dlg-proxy");
9067     this.proxy.hide = this.hideAction;
9068     this.proxy.setOpacity(.5);
9069     this.proxy.hide();
9070
9071     if(config.width){
9072         el.setWidth(config.width);
9073     }
9074     if(config.height){
9075         el.setHeight(config.height);
9076     }
9077     this.size = el.getSize();
9078     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9079         this.xy = [config.x,config.y];
9080     }else{
9081         this.xy = el.getCenterXY(true);
9082     }
9083     /** The header element @type Roo.Element */
9084     this.header = el.child("> .x-dlg-hd");
9085     /** The body element @type Roo.Element */
9086     this.body = el.child("> .x-dlg-bd");
9087     /** The footer element @type Roo.Element */
9088     this.footer = el.child("> .x-dlg-ft");
9089
9090     if(!this.header){
9091         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9092     }
9093     if(!this.body){
9094         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9095     }
9096
9097     this.header.unselectable();
9098     if(this.title){
9099         this.header.update(this.title);
9100     }
9101     // this element allows the dialog to be focused for keyboard event
9102     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9103     this.focusEl.swallowEvent("click", true);
9104
9105     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9106
9107     // wrap the body and footer for special rendering
9108     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9109     if(this.footer){
9110         this.bwrap.dom.appendChild(this.footer.dom);
9111     }
9112
9113     this.bg = this.el.createChild({
9114         tag: "div", cls:"x-dlg-bg",
9115         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9116     });
9117     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9118
9119
9120     if(this.autoScroll !== false && !this.autoTabs){
9121         this.body.setStyle("overflow", "auto");
9122     }
9123
9124     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9125
9126     if(this.closable !== false){
9127         this.el.addClass("x-dlg-closable");
9128         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9129         this.close.on("click", this.closeClick, this);
9130         this.close.addClassOnOver("x-dlg-close-over");
9131     }
9132     if(this.collapsible !== false){
9133         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9134         this.collapseBtn.on("click", this.collapseClick, this);
9135         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9136         this.header.on("dblclick", this.collapseClick, this);
9137     }
9138     if(this.resizable !== false){
9139         this.el.addClass("x-dlg-resizable");
9140         this.resizer = new Roo.Resizable(el, {
9141             minWidth: this.minWidth || 80,
9142             minHeight:this.minHeight || 80,
9143             handles: this.resizeHandles || "all",
9144             pinned: true
9145         });
9146         this.resizer.on("beforeresize", this.beforeResize, this);
9147         this.resizer.on("resize", this.onResize, this);
9148     }
9149     if(this.draggable !== false){
9150         el.addClass("x-dlg-draggable");
9151         if (!this.proxyDrag) {
9152             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9153         }
9154         else {
9155             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9156         }
9157         dd.setHandleElId(this.header.id);
9158         dd.endDrag = this.endMove.createDelegate(this);
9159         dd.startDrag = this.startMove.createDelegate(this);
9160         dd.onDrag = this.onDrag.createDelegate(this);
9161         dd.scroll = false;
9162         this.dd = dd;
9163     }
9164     if(this.modal){
9165         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9166         this.mask.enableDisplayMode("block");
9167         this.mask.hide();
9168         this.el.addClass("x-dlg-modal");
9169     }
9170     if(this.shadow){
9171         this.shadow = new Roo.Shadow({
9172             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9173             offset : this.shadowOffset
9174         });
9175     }else{
9176         this.shadowOffset = 0;
9177     }
9178     if(Roo.useShims && this.shim !== false){
9179         this.shim = this.el.createShim();
9180         this.shim.hide = this.hideAction;
9181         this.shim.hide();
9182     }else{
9183         this.shim = false;
9184     }
9185     if(this.autoTabs){
9186         this.initTabs();
9187     }
9188     if (this.buttons) { 
9189         var bts= this.buttons;
9190         this.buttons = [];
9191         Roo.each(bts, function(b) {
9192             this.addButton(b);
9193         }, this);
9194     }
9195     
9196     
9197     this.addEvents({
9198         /**
9199          * @event keydown
9200          * Fires when a key is pressed
9201          * @param {Roo.BasicDialog} this
9202          * @param {Roo.EventObject} e
9203          */
9204         "keydown" : true,
9205         /**
9206          * @event move
9207          * Fires when this dialog is moved by the user.
9208          * @param {Roo.BasicDialog} this
9209          * @param {Number} x The new page X
9210          * @param {Number} y The new page Y
9211          */
9212         "move" : true,
9213         /**
9214          * @event resize
9215          * Fires when this dialog is resized by the user.
9216          * @param {Roo.BasicDialog} this
9217          * @param {Number} width The new width
9218          * @param {Number} height The new height
9219          */
9220         "resize" : true,
9221         /**
9222          * @event beforehide
9223          * Fires before this dialog is hidden.
9224          * @param {Roo.BasicDialog} this
9225          */
9226         "beforehide" : true,
9227         /**
9228          * @event hide
9229          * Fires when this dialog is hidden.
9230          * @param {Roo.BasicDialog} this
9231          */
9232         "hide" : true,
9233         /**
9234          * @event beforeshow
9235          * Fires before this dialog is shown.
9236          * @param {Roo.BasicDialog} this
9237          */
9238         "beforeshow" : true,
9239         /**
9240          * @event show
9241          * Fires when this dialog is shown.
9242          * @param {Roo.BasicDialog} this
9243          */
9244         "show" : true
9245     });
9246     el.on("keydown", this.onKeyDown, this);
9247     el.on("mousedown", this.toFront, this);
9248     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9249     this.el.hide();
9250     Roo.DialogManager.register(this);
9251     Roo.BasicDialog.superclass.constructor.call(this);
9252 };
9253
9254 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9255     shadowOffset: Roo.isIE ? 6 : 5,
9256     minHeight: 80,
9257     minWidth: 200,
9258     minButtonWidth: 75,
9259     defaultButton: null,
9260     buttonAlign: "right",
9261     tabTag: 'div',
9262     firstShow: true,
9263
9264     /**
9265      * Sets the dialog title text
9266      * @param {String} text The title text to display
9267      * @return {Roo.BasicDialog} this
9268      */
9269     setTitle : function(text){
9270         this.header.update(text);
9271         return this;
9272     },
9273
9274     // private
9275     closeClick : function(){
9276         this.hide();
9277     },
9278
9279     // private
9280     collapseClick : function(){
9281         this[this.collapsed ? "expand" : "collapse"]();
9282     },
9283
9284     /**
9285      * Collapses the dialog to its minimized state (only the title bar is visible).
9286      * Equivalent to the user clicking the collapse dialog button.
9287      */
9288     collapse : function(){
9289         if(!this.collapsed){
9290             this.collapsed = true;
9291             this.el.addClass("x-dlg-collapsed");
9292             this.restoreHeight = this.el.getHeight();
9293             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9294         }
9295     },
9296
9297     /**
9298      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9299      * clicking the expand dialog button.
9300      */
9301     expand : function(){
9302         if(this.collapsed){
9303             this.collapsed = false;
9304             this.el.removeClass("x-dlg-collapsed");
9305             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9306         }
9307     },
9308
9309     /**
9310      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9311      * @return {Roo.TabPanel} The tabs component
9312      */
9313     initTabs : function(){
9314         var tabs = this.getTabs();
9315         while(tabs.getTab(0)){
9316             tabs.removeTab(0);
9317         }
9318         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9319             var dom = el.dom;
9320             tabs.addTab(Roo.id(dom), dom.title);
9321             dom.title = "";
9322         });
9323         tabs.activate(0);
9324         return tabs;
9325     },
9326
9327     // private
9328     beforeResize : function(){
9329         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9330     },
9331
9332     // private
9333     onResize : function(){
9334         this.refreshSize();
9335         this.syncBodyHeight();
9336         this.adjustAssets();
9337         this.focus();
9338         this.fireEvent("resize", this, this.size.width, this.size.height);
9339     },
9340
9341     // private
9342     onKeyDown : function(e){
9343         if(this.isVisible()){
9344             this.fireEvent("keydown", this, e);
9345         }
9346     },
9347
9348     /**
9349      * Resizes the dialog.
9350      * @param {Number} width
9351      * @param {Number} height
9352      * @return {Roo.BasicDialog} this
9353      */
9354     resizeTo : function(width, height){
9355         this.el.setSize(width, height);
9356         this.size = {width: width, height: height};
9357         this.syncBodyHeight();
9358         if(this.fixedcenter){
9359             this.center();
9360         }
9361         if(this.isVisible()){
9362             this.constrainXY();
9363             this.adjustAssets();
9364         }
9365         this.fireEvent("resize", this, width, height);
9366         return this;
9367     },
9368
9369
9370     /**
9371      * Resizes the dialog to fit the specified content size.
9372      * @param {Number} width
9373      * @param {Number} height
9374      * @return {Roo.BasicDialog} this
9375      */
9376     setContentSize : function(w, h){
9377         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9378         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9379         //if(!this.el.isBorderBox()){
9380             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9381             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9382         //}
9383         if(this.tabs){
9384             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9385             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9386         }
9387         this.resizeTo(w, h);
9388         return this;
9389     },
9390
9391     /**
9392      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9393      * executed in response to a particular key being pressed while the dialog is active.
9394      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9395      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9396      * @param {Function} fn The function to call
9397      * @param {Object} scope (optional) The scope of the function
9398      * @return {Roo.BasicDialog} this
9399      */
9400     addKeyListener : function(key, fn, scope){
9401         var keyCode, shift, ctrl, alt;
9402         if(typeof key == "object" && !(key instanceof Array)){
9403             keyCode = key["key"];
9404             shift = key["shift"];
9405             ctrl = key["ctrl"];
9406             alt = key["alt"];
9407         }else{
9408             keyCode = key;
9409         }
9410         var handler = function(dlg, e){
9411             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9412                 var k = e.getKey();
9413                 if(keyCode instanceof Array){
9414                     for(var i = 0, len = keyCode.length; i < len; i++){
9415                         if(keyCode[i] == k){
9416                           fn.call(scope || window, dlg, k, e);
9417                           return;
9418                         }
9419                     }
9420                 }else{
9421                     if(k == keyCode){
9422                         fn.call(scope || window, dlg, k, e);
9423                     }
9424                 }
9425             }
9426         };
9427         this.on("keydown", handler);
9428         return this;
9429     },
9430
9431     /**
9432      * Returns the TabPanel component (creates it if it doesn't exist).
9433      * Note: If you wish to simply check for the existence of tabs without creating them,
9434      * check for a null 'tabs' property.
9435      * @return {Roo.TabPanel} The tabs component
9436      */
9437     getTabs : function(){
9438         if(!this.tabs){
9439             this.el.addClass("x-dlg-auto-tabs");
9440             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9441             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9442         }
9443         return this.tabs;
9444     },
9445
9446     /**
9447      * Adds a button to the footer section of the dialog.
9448      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9449      * object or a valid Roo.DomHelper element config
9450      * @param {Function} handler The function called when the button is clicked
9451      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9452      * @return {Roo.Button} The new button
9453      */
9454     addButton : function(config, handler, scope){
9455         var dh = Roo.DomHelper;
9456         if(!this.footer){
9457             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9458         }
9459         if(!this.btnContainer){
9460             var tb = this.footer.createChild({
9461
9462                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9463                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9464             }, null, true);
9465             this.btnContainer = tb.firstChild.firstChild.firstChild;
9466         }
9467         var bconfig = {
9468             handler: handler,
9469             scope: scope,
9470             minWidth: this.minButtonWidth,
9471             hideParent:true
9472         };
9473         if(typeof config == "string"){
9474             bconfig.text = config;
9475         }else{
9476             if(config.tag){
9477                 bconfig.dhconfig = config;
9478             }else{
9479                 Roo.apply(bconfig, config);
9480             }
9481         }
9482         var fc = false;
9483         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9484             bconfig.position = Math.max(0, bconfig.position);
9485             fc = this.btnContainer.childNodes[bconfig.position];
9486         }
9487          
9488         var btn = new Roo.Button(
9489             fc ? 
9490                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9491                 : this.btnContainer.appendChild(document.createElement("td")),
9492             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9493             bconfig
9494         );
9495         this.syncBodyHeight();
9496         if(!this.buttons){
9497             /**
9498              * Array of all the buttons that have been added to this dialog via addButton
9499              * @type Array
9500              */
9501             this.buttons = [];
9502         }
9503         this.buttons.push(btn);
9504         return btn;
9505     },
9506
9507     /**
9508      * Sets the default button to be focused when the dialog is displayed.
9509      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9510      * @return {Roo.BasicDialog} this
9511      */
9512     setDefaultButton : function(btn){
9513         this.defaultButton = btn;
9514         return this;
9515     },
9516
9517     // private
9518     getHeaderFooterHeight : function(safe){
9519         var height = 0;
9520         if(this.header){
9521            height += this.header.getHeight();
9522         }
9523         if(this.footer){
9524            var fm = this.footer.getMargins();
9525             height += (this.footer.getHeight()+fm.top+fm.bottom);
9526         }
9527         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9528         height += this.centerBg.getPadding("tb");
9529         return height;
9530     },
9531
9532     // private
9533     syncBodyHeight : function()
9534     {
9535         var bd = this.body, // the text
9536             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9537             bw = this.bwrap;
9538         var height = this.size.height - this.getHeaderFooterHeight(false);
9539         bd.setHeight(height-bd.getMargins("tb"));
9540         var hh = this.header.getHeight();
9541         var h = this.size.height-hh;
9542         cb.setHeight(h);
9543         
9544         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9545         bw.setHeight(h-cb.getPadding("tb"));
9546         
9547         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9548         bd.setWidth(bw.getWidth(true));
9549         if(this.tabs){
9550             this.tabs.syncHeight();
9551             if(Roo.isIE){
9552                 this.tabs.el.repaint();
9553             }
9554         }
9555     },
9556
9557     /**
9558      * Restores the previous state of the dialog if Roo.state is configured.
9559      * @return {Roo.BasicDialog} this
9560      */
9561     restoreState : function(){
9562         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9563         if(box && box.width){
9564             this.xy = [box.x, box.y];
9565             this.resizeTo(box.width, box.height);
9566         }
9567         return this;
9568     },
9569
9570     // private
9571     beforeShow : function(){
9572         this.expand();
9573         if(this.fixedcenter){
9574             this.xy = this.el.getCenterXY(true);
9575         }
9576         if(this.modal){
9577             Roo.get(document.body).addClass("x-body-masked");
9578             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9579             this.mask.show();
9580         }
9581         this.constrainXY();
9582     },
9583
9584     // private
9585     animShow : function(){
9586         var b = Roo.get(this.animateTarget).getBox();
9587         this.proxy.setSize(b.width, b.height);
9588         this.proxy.setLocation(b.x, b.y);
9589         this.proxy.show();
9590         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9591                     true, .35, this.showEl.createDelegate(this));
9592     },
9593
9594     /**
9595      * Shows the dialog.
9596      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9597      * @return {Roo.BasicDialog} this
9598      */
9599     show : function(animateTarget){
9600         if (this.fireEvent("beforeshow", this) === false){
9601             return;
9602         }
9603         if(this.syncHeightBeforeShow){
9604             this.syncBodyHeight();
9605         }else if(this.firstShow){
9606             this.firstShow = false;
9607             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9608         }
9609         this.animateTarget = animateTarget || this.animateTarget;
9610         if(!this.el.isVisible()){
9611             this.beforeShow();
9612             if(this.animateTarget && Roo.get(this.animateTarget)){
9613                 this.animShow();
9614             }else{
9615                 this.showEl();
9616             }
9617         }
9618         return this;
9619     },
9620
9621     // private
9622     showEl : function(){
9623         this.proxy.hide();
9624         this.el.setXY(this.xy);
9625         this.el.show();
9626         this.adjustAssets(true);
9627         this.toFront();
9628         this.focus();
9629         // IE peekaboo bug - fix found by Dave Fenwick
9630         if(Roo.isIE){
9631             this.el.repaint();
9632         }
9633         this.fireEvent("show", this);
9634     },
9635
9636     /**
9637      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9638      * dialog itself will receive focus.
9639      */
9640     focus : function(){
9641         if(this.defaultButton){
9642             this.defaultButton.focus();
9643         }else{
9644             this.focusEl.focus();
9645         }
9646     },
9647
9648     // private
9649     constrainXY : function(){
9650         if(this.constraintoviewport !== false){
9651             if(!this.viewSize){
9652                 if(this.container){
9653                     var s = this.container.getSize();
9654                     this.viewSize = [s.width, s.height];
9655                 }else{
9656                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9657                 }
9658             }
9659             var s = Roo.get(this.container||document).getScroll();
9660
9661             var x = this.xy[0], y = this.xy[1];
9662             var w = this.size.width, h = this.size.height;
9663             var vw = this.viewSize[0], vh = this.viewSize[1];
9664             // only move it if it needs it
9665             var moved = false;
9666             // first validate right/bottom
9667             if(x + w > vw+s.left){
9668                 x = vw - w;
9669                 moved = true;
9670             }
9671             if(y + h > vh+s.top){
9672                 y = vh - h;
9673                 moved = true;
9674             }
9675             // then make sure top/left isn't negative
9676             if(x < s.left){
9677                 x = s.left;
9678                 moved = true;
9679             }
9680             if(y < s.top){
9681                 y = s.top;
9682                 moved = true;
9683             }
9684             if(moved){
9685                 // cache xy
9686                 this.xy = [x, y];
9687                 if(this.isVisible()){
9688                     this.el.setLocation(x, y);
9689                     this.adjustAssets();
9690                 }
9691             }
9692         }
9693     },
9694
9695     // private
9696     onDrag : function(){
9697         if(!this.proxyDrag){
9698             this.xy = this.el.getXY();
9699             this.adjustAssets();
9700         }
9701     },
9702
9703     // private
9704     adjustAssets : function(doShow){
9705         var x = this.xy[0], y = this.xy[1];
9706         var w = this.size.width, h = this.size.height;
9707         if(doShow === true){
9708             if(this.shadow){
9709                 this.shadow.show(this.el);
9710             }
9711             if(this.shim){
9712                 this.shim.show();
9713             }
9714         }
9715         if(this.shadow && this.shadow.isVisible()){
9716             this.shadow.show(this.el);
9717         }
9718         if(this.shim && this.shim.isVisible()){
9719             this.shim.setBounds(x, y, w, h);
9720         }
9721     },
9722
9723     // private
9724     adjustViewport : function(w, h){
9725         if(!w || !h){
9726             w = Roo.lib.Dom.getViewWidth();
9727             h = Roo.lib.Dom.getViewHeight();
9728         }
9729         // cache the size
9730         this.viewSize = [w, h];
9731         if(this.modal && this.mask.isVisible()){
9732             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9733             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9734         }
9735         if(this.isVisible()){
9736             this.constrainXY();
9737         }
9738     },
9739
9740     /**
9741      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9742      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9743      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9744      */
9745     destroy : function(removeEl){
9746         if(this.isVisible()){
9747             this.animateTarget = null;
9748             this.hide();
9749         }
9750         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9751         if(this.tabs){
9752             this.tabs.destroy(removeEl);
9753         }
9754         Roo.destroy(
9755              this.shim,
9756              this.proxy,
9757              this.resizer,
9758              this.close,
9759              this.mask
9760         );
9761         if(this.dd){
9762             this.dd.unreg();
9763         }
9764         if(this.buttons){
9765            for(var i = 0, len = this.buttons.length; i < len; i++){
9766                this.buttons[i].destroy();
9767            }
9768         }
9769         this.el.removeAllListeners();
9770         if(removeEl === true){
9771             this.el.update("");
9772             this.el.remove();
9773         }
9774         Roo.DialogManager.unregister(this);
9775     },
9776
9777     // private
9778     startMove : function(){
9779         if(this.proxyDrag){
9780             this.proxy.show();
9781         }
9782         if(this.constraintoviewport !== false){
9783             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9784         }
9785     },
9786
9787     // private
9788     endMove : function(){
9789         if(!this.proxyDrag){
9790             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9791         }else{
9792             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9793             this.proxy.hide();
9794         }
9795         this.refreshSize();
9796         this.adjustAssets();
9797         this.focus();
9798         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9799     },
9800
9801     /**
9802      * Brings this dialog to the front of any other visible dialogs
9803      * @return {Roo.BasicDialog} this
9804      */
9805     toFront : function(){
9806         Roo.DialogManager.bringToFront(this);
9807         return this;
9808     },
9809
9810     /**
9811      * Sends this dialog to the back (under) of any other visible dialogs
9812      * @return {Roo.BasicDialog} this
9813      */
9814     toBack : function(){
9815         Roo.DialogManager.sendToBack(this);
9816         return this;
9817     },
9818
9819     /**
9820      * Centers this dialog in the viewport
9821      * @return {Roo.BasicDialog} this
9822      */
9823     center : function(){
9824         var xy = this.el.getCenterXY(true);
9825         this.moveTo(xy[0], xy[1]);
9826         return this;
9827     },
9828
9829     /**
9830      * Moves the dialog's top-left corner to the specified point
9831      * @param {Number} x
9832      * @param {Number} y
9833      * @return {Roo.BasicDialog} this
9834      */
9835     moveTo : function(x, y){
9836         this.xy = [x,y];
9837         if(this.isVisible()){
9838             this.el.setXY(this.xy);
9839             this.adjustAssets();
9840         }
9841         return this;
9842     },
9843
9844     /**
9845      * Aligns the dialog to the specified element
9846      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9847      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9848      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9849      * @return {Roo.BasicDialog} this
9850      */
9851     alignTo : function(element, position, offsets){
9852         this.xy = this.el.getAlignToXY(element, position, offsets);
9853         if(this.isVisible()){
9854             this.el.setXY(this.xy);
9855             this.adjustAssets();
9856         }
9857         return this;
9858     },
9859
9860     /**
9861      * Anchors an element to another element and realigns it when the window is resized.
9862      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9863      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9864      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9865      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9866      * is a number, it is used as the buffer delay (defaults to 50ms).
9867      * @return {Roo.BasicDialog} this
9868      */
9869     anchorTo : function(el, alignment, offsets, monitorScroll){
9870         var action = function(){
9871             this.alignTo(el, alignment, offsets);
9872         };
9873         Roo.EventManager.onWindowResize(action, this);
9874         var tm = typeof monitorScroll;
9875         if(tm != 'undefined'){
9876             Roo.EventManager.on(window, 'scroll', action, this,
9877                 {buffer: tm == 'number' ? monitorScroll : 50});
9878         }
9879         action.call(this);
9880         return this;
9881     },
9882
9883     /**
9884      * Returns true if the dialog is visible
9885      * @return {Boolean}
9886      */
9887     isVisible : function(){
9888         return this.el.isVisible();
9889     },
9890
9891     // private
9892     animHide : function(callback){
9893         var b = Roo.get(this.animateTarget).getBox();
9894         this.proxy.show();
9895         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9896         this.el.hide();
9897         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9898                     this.hideEl.createDelegate(this, [callback]));
9899     },
9900
9901     /**
9902      * Hides the dialog.
9903      * @param {Function} callback (optional) Function to call when the dialog is hidden
9904      * @return {Roo.BasicDialog} this
9905      */
9906     hide : function(callback){
9907         if (this.fireEvent("beforehide", this) === false){
9908             return;
9909         }
9910         if(this.shadow){
9911             this.shadow.hide();
9912         }
9913         if(this.shim) {
9914           this.shim.hide();
9915         }
9916         // sometimes animateTarget seems to get set.. causing problems...
9917         // this just double checks..
9918         if(this.animateTarget && Roo.get(this.animateTarget)) {
9919            this.animHide(callback);
9920         }else{
9921             this.el.hide();
9922             this.hideEl(callback);
9923         }
9924         return this;
9925     },
9926
9927     // private
9928     hideEl : function(callback){
9929         this.proxy.hide();
9930         if(this.modal){
9931             this.mask.hide();
9932             Roo.get(document.body).removeClass("x-body-masked");
9933         }
9934         this.fireEvent("hide", this);
9935         if(typeof callback == "function"){
9936             callback();
9937         }
9938     },
9939
9940     // private
9941     hideAction : function(){
9942         this.setLeft("-10000px");
9943         this.setTop("-10000px");
9944         this.setStyle("visibility", "hidden");
9945     },
9946
9947     // private
9948     refreshSize : function(){
9949         this.size = this.el.getSize();
9950         this.xy = this.el.getXY();
9951         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9952     },
9953
9954     // private
9955     // z-index is managed by the DialogManager and may be overwritten at any time
9956     setZIndex : function(index){
9957         if(this.modal){
9958             this.mask.setStyle("z-index", index);
9959         }
9960         if(this.shim){
9961             this.shim.setStyle("z-index", ++index);
9962         }
9963         if(this.shadow){
9964             this.shadow.setZIndex(++index);
9965         }
9966         this.el.setStyle("z-index", ++index);
9967         if(this.proxy){
9968             this.proxy.setStyle("z-index", ++index);
9969         }
9970         if(this.resizer){
9971             this.resizer.proxy.setStyle("z-index", ++index);
9972         }
9973
9974         this.lastZIndex = index;
9975     },
9976
9977     /**
9978      * Returns the element for this dialog
9979      * @return {Roo.Element} The underlying dialog Element
9980      */
9981     getEl : function(){
9982         return this.el;
9983     }
9984 });
9985
9986 /**
9987  * @class Roo.DialogManager
9988  * Provides global access to BasicDialogs that have been created and
9989  * support for z-indexing (layering) multiple open dialogs.
9990  */
9991 Roo.DialogManager = function(){
9992     var list = {};
9993     var accessList = [];
9994     var front = null;
9995
9996     // private
9997     var sortDialogs = function(d1, d2){
9998         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
9999     };
10000
10001     // private
10002     var orderDialogs = function(){
10003         accessList.sort(sortDialogs);
10004         var seed = Roo.DialogManager.zseed;
10005         for(var i = 0, len = accessList.length; i < len; i++){
10006             var dlg = accessList[i];
10007             if(dlg){
10008                 dlg.setZIndex(seed + (i*10));
10009             }
10010         }
10011     };
10012
10013     return {
10014         /**
10015          * The starting z-index for BasicDialogs (defaults to 9000)
10016          * @type Number The z-index value
10017          */
10018         zseed : 9000,
10019
10020         // private
10021         register : function(dlg){
10022             list[dlg.id] = dlg;
10023             accessList.push(dlg);
10024         },
10025
10026         // private
10027         unregister : function(dlg){
10028             delete list[dlg.id];
10029             var i=0;
10030             var len=0;
10031             if(!accessList.indexOf){
10032                 for(  i = 0, len = accessList.length; i < len; i++){
10033                     if(accessList[i] == dlg){
10034                         accessList.splice(i, 1);
10035                         return;
10036                     }
10037                 }
10038             }else{
10039                  i = accessList.indexOf(dlg);
10040                 if(i != -1){
10041                     accessList.splice(i, 1);
10042                 }
10043             }
10044         },
10045
10046         /**
10047          * Gets a registered dialog by id
10048          * @param {String/Object} id The id of the dialog or a dialog
10049          * @return {Roo.BasicDialog} this
10050          */
10051         get : function(id){
10052             return typeof id == "object" ? id : list[id];
10053         },
10054
10055         /**
10056          * Brings the specified dialog to the front
10057          * @param {String/Object} dlg The id of the dialog or a dialog
10058          * @return {Roo.BasicDialog} this
10059          */
10060         bringToFront : function(dlg){
10061             dlg = this.get(dlg);
10062             if(dlg != front){
10063                 front = dlg;
10064                 dlg._lastAccess = new Date().getTime();
10065                 orderDialogs();
10066             }
10067             return dlg;
10068         },
10069
10070         /**
10071          * Sends the specified dialog to the back
10072          * @param {String/Object} dlg The id of the dialog or a dialog
10073          * @return {Roo.BasicDialog} this
10074          */
10075         sendToBack : function(dlg){
10076             dlg = this.get(dlg);
10077             dlg._lastAccess = -(new Date().getTime());
10078             orderDialogs();
10079             return dlg;
10080         },
10081
10082         /**
10083          * Hides all dialogs
10084          */
10085         hideAll : function(){
10086             for(var id in list){
10087                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10088                     list[id].hide();
10089                 }
10090             }
10091         }
10092     };
10093 }();
10094
10095 /**
10096  * @class Roo.LayoutDialog
10097  * @extends Roo.BasicDialog
10098  * @children Roo.ContentPanel
10099  * Dialog which provides adjustments for working with a layout in a Dialog.
10100  * Add your necessary layout config options to the dialog's config.<br>
10101  * Example usage (including a nested layout):
10102  * <pre><code>
10103 if(!dialog){
10104     dialog = new Roo.LayoutDialog("download-dlg", {
10105         modal: true,
10106         width:600,
10107         height:450,
10108         shadow:true,
10109         minWidth:500,
10110         minHeight:350,
10111         autoTabs:true,
10112         proxyDrag:true,
10113         // layout config merges with the dialog config
10114         center:{
10115             tabPosition: "top",
10116             alwaysShowTabs: true
10117         }
10118     });
10119     dialog.addKeyListener(27, dialog.hide, dialog);
10120     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10121     dialog.addButton("Build It!", this.getDownload, this);
10122
10123     // we can even add nested layouts
10124     var innerLayout = new Roo.BorderLayout("dl-inner", {
10125         east: {
10126             initialSize: 200,
10127             autoScroll:true,
10128             split:true
10129         },
10130         center: {
10131             autoScroll:true
10132         }
10133     });
10134     innerLayout.beginUpdate();
10135     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10136     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10137     innerLayout.endUpdate(true);
10138
10139     var layout = dialog.getLayout();
10140     layout.beginUpdate();
10141     layout.add("center", new Roo.ContentPanel("standard-panel",
10142                         {title: "Download the Source", fitToFrame:true}));
10143     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10144                {title: "Build your own roo.js"}));
10145     layout.getRegion("center").showPanel(sp);
10146     layout.endUpdate();
10147 }
10148 </code></pre>
10149     * @constructor
10150     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10151     * @param {Object} config configuration options
10152   */
10153 Roo.LayoutDialog = function(el, cfg){
10154     
10155     var config=  cfg;
10156     if (typeof(cfg) == 'undefined') {
10157         config = Roo.apply({}, el);
10158         // not sure why we use documentElement here.. - it should always be body.
10159         // IE7 borks horribly if we use documentElement.
10160         // webkit also does not like documentElement - it creates a body element...
10161         el = Roo.get( document.body || document.documentElement ).createChild();
10162         //config.autoCreate = true;
10163     }
10164     
10165     
10166     config.autoTabs = false;
10167     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10168     this.body.setStyle({overflow:"hidden", position:"relative"});
10169     this.layout = new Roo.BorderLayout(this.body.dom, config);
10170     this.layout.monitorWindowResize = false;
10171     this.el.addClass("x-dlg-auto-layout");
10172     // fix case when center region overwrites center function
10173     this.center = Roo.BasicDialog.prototype.center;
10174     this.on("show", this.layout.layout, this.layout, true);
10175     if (config.items) {
10176         var xitems = config.items;
10177         delete config.items;
10178         Roo.each(xitems, this.addxtype, this);
10179     }
10180     
10181     
10182 };
10183 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10184     
10185     
10186     /**
10187      * @cfg {Roo.LayoutRegion} east  
10188      */
10189     /**
10190      * @cfg {Roo.LayoutRegion} west
10191      */
10192     /**
10193      * @cfg {Roo.LayoutRegion} south
10194      */
10195     /**
10196      * @cfg {Roo.LayoutRegion} north
10197      */
10198     /**
10199      * @cfg {Roo.LayoutRegion} center
10200      */
10201     /**
10202      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10203      */
10204     
10205     
10206     /**
10207      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10208      * @deprecated
10209      */
10210     endUpdate : function(){
10211         this.layout.endUpdate();
10212     },
10213
10214     /**
10215      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10216      *  @deprecated
10217      */
10218     beginUpdate : function(){
10219         this.layout.beginUpdate();
10220     },
10221
10222     /**
10223      * Get the BorderLayout for this dialog
10224      * @return {Roo.BorderLayout}
10225      */
10226     getLayout : function(){
10227         return this.layout;
10228     },
10229
10230     showEl : function(){
10231         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10232         if(Roo.isIE7){
10233             this.layout.layout();
10234         }
10235     },
10236
10237     // private
10238     // Use the syncHeightBeforeShow config option to control this automatically
10239     syncBodyHeight : function(){
10240         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10241         if(this.layout){this.layout.layout();}
10242     },
10243     
10244       /**
10245      * Add an xtype element (actually adds to the layout.)
10246      * @return {Object} xdata xtype object data.
10247      */
10248     
10249     addxtype : function(c) {
10250         return this.layout.addxtype(c);
10251     }
10252 });/*
10253  * Based on:
10254  * Ext JS Library 1.1.1
10255  * Copyright(c) 2006-2007, Ext JS, LLC.
10256  *
10257  * Originally Released Under LGPL - original licence link has changed is not relivant.
10258  *
10259  * Fork - LGPL
10260  * <script type="text/javascript">
10261  */
10262  
10263 /**
10264  * @class Roo.MessageBox
10265  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10266  * Example usage:
10267  *<pre><code>
10268 // Basic alert:
10269 Roo.Msg.alert('Status', 'Changes saved successfully.');
10270
10271 // Prompt for user data:
10272 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10273     if (btn == 'ok'){
10274         // process text value...
10275     }
10276 });
10277
10278 // Show a dialog using config options:
10279 Roo.Msg.show({
10280    title:'Save Changes?',
10281    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10282    buttons: Roo.Msg.YESNOCANCEL,
10283    fn: processResult,
10284    animEl: 'elId'
10285 });
10286 </code></pre>
10287  * @singleton
10288  */
10289 Roo.MessageBox = function(){
10290     var dlg, opt, mask, waitTimer;
10291     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10292     var buttons, activeTextEl, bwidth;
10293
10294     // private
10295     var handleButton = function(button){
10296         dlg.hide();
10297         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10298     };
10299
10300     // private
10301     var handleHide = function(){
10302         if(opt && opt.cls){
10303             dlg.el.removeClass(opt.cls);
10304         }
10305         if(waitTimer){
10306             Roo.TaskMgr.stop(waitTimer);
10307             waitTimer = null;
10308         }
10309     };
10310
10311     // private
10312     var updateButtons = function(b){
10313         var width = 0;
10314         if(!b){
10315             buttons["ok"].hide();
10316             buttons["cancel"].hide();
10317             buttons["yes"].hide();
10318             buttons["no"].hide();
10319             dlg.footer.dom.style.display = 'none';
10320             return width;
10321         }
10322         dlg.footer.dom.style.display = '';
10323         for(var k in buttons){
10324             if(typeof buttons[k] != "function"){
10325                 if(b[k]){
10326                     buttons[k].show();
10327                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10328                     width += buttons[k].el.getWidth()+15;
10329                 }else{
10330                     buttons[k].hide();
10331                 }
10332             }
10333         }
10334         return width;
10335     };
10336
10337     // private
10338     var handleEsc = function(d, k, e){
10339         if(opt && opt.closable !== false){
10340             dlg.hide();
10341         }
10342         if(e){
10343             e.stopEvent();
10344         }
10345     };
10346
10347     return {
10348         /**
10349          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10350          * @return {Roo.BasicDialog} The BasicDialog element
10351          */
10352         getDialog : function(){
10353            if(!dlg){
10354                 dlg = new Roo.BasicDialog("x-msg-box", {
10355                     autoCreate : true,
10356                     shadow: true,
10357                     draggable: true,
10358                     resizable:false,
10359                     constraintoviewport:false,
10360                     fixedcenter:true,
10361                     collapsible : false,
10362                     shim:true,
10363                     modal: true,
10364                     width:400, height:100,
10365                     buttonAlign:"center",
10366                     closeClick : function(){
10367                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10368                             handleButton("no");
10369                         }else{
10370                             handleButton("cancel");
10371                         }
10372                     }
10373                 });
10374                 dlg.on("hide", handleHide);
10375                 mask = dlg.mask;
10376                 dlg.addKeyListener(27, handleEsc);
10377                 buttons = {};
10378                 var bt = this.buttonText;
10379                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10380                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10381                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10382                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10383                 bodyEl = dlg.body.createChild({
10384
10385                     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>'
10386                 });
10387                 msgEl = bodyEl.dom.firstChild;
10388                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10389                 textboxEl.enableDisplayMode();
10390                 textboxEl.addKeyListener([10,13], function(){
10391                     if(dlg.isVisible() && opt && opt.buttons){
10392                         if(opt.buttons.ok){
10393                             handleButton("ok");
10394                         }else if(opt.buttons.yes){
10395                             handleButton("yes");
10396                         }
10397                     }
10398                 });
10399                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10400                 textareaEl.enableDisplayMode();
10401                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10402                 progressEl.enableDisplayMode();
10403                 var pf = progressEl.dom.firstChild;
10404                 if (pf) {
10405                     pp = Roo.get(pf.firstChild);
10406                     pp.setHeight(pf.offsetHeight);
10407                 }
10408                 
10409             }
10410             return dlg;
10411         },
10412
10413         /**
10414          * Updates the message box body text
10415          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10416          * the XHTML-compliant non-breaking space character '&amp;#160;')
10417          * @return {Roo.MessageBox} This message box
10418          */
10419         updateText : function(text){
10420             if(!dlg.isVisible() && !opt.width){
10421                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10422             }
10423             msgEl.innerHTML = text || '&#160;';
10424       
10425             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10426             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10427             var w = Math.max(
10428                     Math.min(opt.width || cw , this.maxWidth), 
10429                     Math.max(opt.minWidth || this.minWidth, bwidth)
10430             );
10431             if(opt.prompt){
10432                 activeTextEl.setWidth(w);
10433             }
10434             if(dlg.isVisible()){
10435                 dlg.fixedcenter = false;
10436             }
10437             // to big, make it scroll. = But as usual stupid IE does not support
10438             // !important..
10439             
10440             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10441                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10442                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10443             } else {
10444                 bodyEl.dom.style.height = '';
10445                 bodyEl.dom.style.overflowY = '';
10446             }
10447             if (cw > w) {
10448                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10449             } else {
10450                 bodyEl.dom.style.overflowX = '';
10451             }
10452             
10453             dlg.setContentSize(w, bodyEl.getHeight());
10454             if(dlg.isVisible()){
10455                 dlg.fixedcenter = true;
10456             }
10457             return this;
10458         },
10459
10460         /**
10461          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10462          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10463          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10464          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10465          * @return {Roo.MessageBox} This message box
10466          */
10467         updateProgress : function(value, text){
10468             if(text){
10469                 this.updateText(text);
10470             }
10471             if (pp) { // weird bug on my firefox - for some reason this is not defined
10472                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10473             }
10474             return this;
10475         },        
10476
10477         /**
10478          * Returns true if the message box is currently displayed
10479          * @return {Boolean} True if the message box is visible, else false
10480          */
10481         isVisible : function(){
10482             return dlg && dlg.isVisible();  
10483         },
10484
10485         /**
10486          * Hides the message box if it is displayed
10487          */
10488         hide : function(){
10489             if(this.isVisible()){
10490                 dlg.hide();
10491             }  
10492         },
10493
10494         /**
10495          * Displays a new message box, or reinitializes an existing message box, based on the config options
10496          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10497          * The following config object properties are supported:
10498          * <pre>
10499 Property    Type             Description
10500 ----------  ---------------  ------------------------------------------------------------------------------------
10501 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10502                                    closes (defaults to undefined)
10503 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10504                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10505 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10506                                    progress and wait dialogs will ignore this property and always hide the
10507                                    close button as they can only be closed programmatically.
10508 cls               String           A custom CSS class to apply to the message box element
10509 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10510                                    displayed (defaults to 75)
10511 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10512                                    function will be btn (the name of the button that was clicked, if applicable,
10513                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10514                                    Progress and wait dialogs will ignore this option since they do not respond to
10515                                    user actions and can only be closed programmatically, so any required function
10516                                    should be called by the same code after it closes the dialog.
10517 icon              String           A CSS class that provides a background image to be used as an icon for
10518                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10519 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10520 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10521 modal             Boolean          False to allow user interaction with the page while the message box is
10522                                    displayed (defaults to true)
10523 msg               String           A string that will replace the existing message box body text (defaults
10524                                    to the XHTML-compliant non-breaking space character '&#160;')
10525 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10526 progress          Boolean          True to display a progress bar (defaults to false)
10527 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10528 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10529 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10530 title             String           The title text
10531 value             String           The string value to set into the active textbox element if displayed
10532 wait              Boolean          True to display a progress bar (defaults to false)
10533 width             Number           The width of the dialog in pixels
10534 </pre>
10535          *
10536          * Example usage:
10537          * <pre><code>
10538 Roo.Msg.show({
10539    title: 'Address',
10540    msg: 'Please enter your address:',
10541    width: 300,
10542    buttons: Roo.MessageBox.OKCANCEL,
10543    multiline: true,
10544    fn: saveAddress,
10545    animEl: 'addAddressBtn'
10546 });
10547 </code></pre>
10548          * @param {Object} config Configuration options
10549          * @return {Roo.MessageBox} This message box
10550          */
10551         show : function(options)
10552         {
10553             
10554             // this causes nightmares if you show one dialog after another
10555             // especially on callbacks..
10556              
10557             if(this.isVisible()){
10558                 
10559                 this.hide();
10560                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10561                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10562                 Roo.log("New Dialog Message:" +  options.msg )
10563                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10564                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10565                 
10566             }
10567             var d = this.getDialog();
10568             opt = options;
10569             d.setTitle(opt.title || "&#160;");
10570             d.close.setDisplayed(opt.closable !== false);
10571             activeTextEl = textboxEl;
10572             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10573             if(opt.prompt){
10574                 if(opt.multiline){
10575                     textboxEl.hide();
10576                     textareaEl.show();
10577                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10578                         opt.multiline : this.defaultTextHeight);
10579                     activeTextEl = textareaEl;
10580                 }else{
10581                     textboxEl.show();
10582                     textareaEl.hide();
10583                 }
10584             }else{
10585                 textboxEl.hide();
10586                 textareaEl.hide();
10587             }
10588             progressEl.setDisplayed(opt.progress === true);
10589             this.updateProgress(0);
10590             activeTextEl.dom.value = opt.value || "";
10591             if(opt.prompt){
10592                 dlg.setDefaultButton(activeTextEl);
10593             }else{
10594                 var bs = opt.buttons;
10595                 var db = null;
10596                 if(bs && bs.ok){
10597                     db = buttons["ok"];
10598                 }else if(bs && bs.yes){
10599                     db = buttons["yes"];
10600                 }
10601                 dlg.setDefaultButton(db);
10602             }
10603             bwidth = updateButtons(opt.buttons);
10604             this.updateText(opt.msg);
10605             if(opt.cls){
10606                 d.el.addClass(opt.cls);
10607             }
10608             d.proxyDrag = opt.proxyDrag === true;
10609             d.modal = opt.modal !== false;
10610             d.mask = opt.modal !== false ? mask : false;
10611             if(!d.isVisible()){
10612                 // force it to the end of the z-index stack so it gets a cursor in FF
10613                 document.body.appendChild(dlg.el.dom);
10614                 d.animateTarget = null;
10615                 d.show(options.animEl);
10616             }
10617             return this;
10618         },
10619
10620         /**
10621          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10622          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10623          * and closing the message box when the process is complete.
10624          * @param {String} title The title bar text
10625          * @param {String} msg The message box body text
10626          * @return {Roo.MessageBox} This message box
10627          */
10628         progress : function(title, msg){
10629             this.show({
10630                 title : title,
10631                 msg : msg,
10632                 buttons: false,
10633                 progress:true,
10634                 closable:false,
10635                 minWidth: this.minProgressWidth,
10636                 modal : true
10637             });
10638             return this;
10639         },
10640
10641         /**
10642          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10643          * If a callback function is passed it will be called after the user clicks the button, and the
10644          * id of the button that was clicked will be passed as the only parameter to the callback
10645          * (could also be the top-right close button).
10646          * @param {String} title The title bar text
10647          * @param {String} msg The message box body text
10648          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10649          * @param {Object} scope (optional) The scope of the callback function
10650          * @return {Roo.MessageBox} This message box
10651          */
10652         alert : function(title, msg, fn, scope){
10653             this.show({
10654                 title : title,
10655                 msg : msg,
10656                 buttons: this.OK,
10657                 fn: fn,
10658                 scope : scope,
10659                 modal : true
10660             });
10661             return this;
10662         },
10663
10664         /**
10665          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10666          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10667          * You are responsible for closing the message box when the process is complete.
10668          * @param {String} msg The message box body text
10669          * @param {String} title (optional) The title bar text
10670          * @return {Roo.MessageBox} This message box
10671          */
10672         wait : function(msg, title){
10673             this.show({
10674                 title : title,
10675                 msg : msg,
10676                 buttons: false,
10677                 closable:false,
10678                 progress:true,
10679                 modal:true,
10680                 width:300,
10681                 wait:true
10682             });
10683             waitTimer = Roo.TaskMgr.start({
10684                 run: function(i){
10685                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10686                 },
10687                 interval: 1000
10688             });
10689             return this;
10690         },
10691
10692         /**
10693          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10694          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10695          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10696          * @param {String} title The title bar text
10697          * @param {String} msg The message box body text
10698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10699          * @param {Object} scope (optional) The scope of the callback function
10700          * @return {Roo.MessageBox} This message box
10701          */
10702         confirm : function(title, msg, fn, scope){
10703             this.show({
10704                 title : title,
10705                 msg : msg,
10706                 buttons: this.YESNO,
10707                 fn: fn,
10708                 scope : scope,
10709                 modal : true
10710             });
10711             return this;
10712         },
10713
10714         /**
10715          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10716          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10717          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10718          * (could also be the top-right close button) and the text that was entered will be passed as the two
10719          * parameters to the callback.
10720          * @param {String} title The title bar text
10721          * @param {String} msg The message box body text
10722          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10723          * @param {Object} scope (optional) The scope of the callback function
10724          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10725          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10726          * @return {Roo.MessageBox} This message box
10727          */
10728         prompt : function(title, msg, fn, scope, multiline){
10729             this.show({
10730                 title : title,
10731                 msg : msg,
10732                 buttons: this.OKCANCEL,
10733                 fn: fn,
10734                 minWidth:250,
10735                 scope : scope,
10736                 prompt:true,
10737                 multiline: multiline,
10738                 modal : true
10739             });
10740             return this;
10741         },
10742
10743         /**
10744          * Button config that displays a single OK button
10745          * @type Object
10746          */
10747         OK : {ok:true},
10748         /**
10749          * Button config that displays Yes and No buttons
10750          * @type Object
10751          */
10752         YESNO : {yes:true, no:true},
10753         /**
10754          * Button config that displays OK and Cancel buttons
10755          * @type Object
10756          */
10757         OKCANCEL : {ok:true, cancel:true},
10758         /**
10759          * Button config that displays Yes, No and Cancel buttons
10760          * @type Object
10761          */
10762         YESNOCANCEL : {yes:true, no:true, cancel:true},
10763
10764         /**
10765          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10766          * @type Number
10767          */
10768         defaultTextHeight : 75,
10769         /**
10770          * The maximum width in pixels of the message box (defaults to 600)
10771          * @type Number
10772          */
10773         maxWidth : 600,
10774         /**
10775          * The minimum width in pixels of the message box (defaults to 100)
10776          * @type Number
10777          */
10778         minWidth : 100,
10779         /**
10780          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10781          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10782          * @type Number
10783          */
10784         minProgressWidth : 250,
10785         /**
10786          * An object containing the default button text strings that can be overriden for localized language support.
10787          * Supported properties are: ok, cancel, yes and no.
10788          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10789          * @type Object
10790          */
10791         buttonText : {
10792             ok : "OK",
10793             cancel : "Cancel",
10794             yes : "Yes",
10795             no : "No"
10796         }
10797     };
10798 }();
10799
10800 /**
10801  * Shorthand for {@link Roo.MessageBox}
10802  */
10803 Roo.Msg = Roo.MessageBox;/*
10804  * Based on:
10805  * Ext JS Library 1.1.1
10806  * Copyright(c) 2006-2007, Ext JS, LLC.
10807  *
10808  * Originally Released Under LGPL - original licence link has changed is not relivant.
10809  *
10810  * Fork - LGPL
10811  * <script type="text/javascript">
10812  */
10813 /**
10814  * @class Roo.QuickTips
10815  * Provides attractive and customizable tooltips for any element.
10816  * @singleton
10817  */
10818 Roo.QuickTips = function(){
10819     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10820     var ce, bd, xy, dd;
10821     var visible = false, disabled = true, inited = false;
10822     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10823     
10824     var onOver = function(e){
10825         if(disabled){
10826             return;
10827         }
10828         var t = e.getTarget();
10829         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10830             return;
10831         }
10832         if(ce && t == ce.el){
10833             clearTimeout(hideProc);
10834             return;
10835         }
10836         if(t && tagEls[t.id]){
10837             tagEls[t.id].el = t;
10838             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10839             return;
10840         }
10841         var ttp, et = Roo.fly(t);
10842         var ns = cfg.namespace;
10843         if(tm.interceptTitles && t.title){
10844             ttp = t.title;
10845             t.qtip = ttp;
10846             t.removeAttribute("title");
10847             e.preventDefault();
10848         }else{
10849             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10850         }
10851         if(ttp){
10852             showProc = show.defer(tm.showDelay, tm, [{
10853                 el: t, 
10854                 text: ttp.replace(/\\n/g,'<br/>'),
10855                 width: et.getAttributeNS(ns, cfg.width),
10856                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10857                 title: et.getAttributeNS(ns, cfg.title),
10858                     cls: et.getAttributeNS(ns, cfg.cls)
10859             }]);
10860         }
10861     };
10862     
10863     var onOut = function(e){
10864         clearTimeout(showProc);
10865         var t = e.getTarget();
10866         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10867             hideProc = setTimeout(hide, tm.hideDelay);
10868         }
10869     };
10870     
10871     var onMove = function(e){
10872         if(disabled){
10873             return;
10874         }
10875         xy = e.getXY();
10876         xy[1] += 18;
10877         if(tm.trackMouse && ce){
10878             el.setXY(xy);
10879         }
10880     };
10881     
10882     var onDown = function(e){
10883         clearTimeout(showProc);
10884         clearTimeout(hideProc);
10885         if(!e.within(el)){
10886             if(tm.hideOnClick){
10887                 hide();
10888                 tm.disable();
10889                 tm.enable.defer(100, tm);
10890             }
10891         }
10892     };
10893     
10894     var getPad = function(){
10895         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10896     };
10897
10898     var show = function(o){
10899         if(disabled){
10900             return;
10901         }
10902         clearTimeout(dismissProc);
10903         ce = o;
10904         if(removeCls){ // in case manually hidden
10905             el.removeClass(removeCls);
10906             removeCls = null;
10907         }
10908         if(ce.cls){
10909             el.addClass(ce.cls);
10910             removeCls = ce.cls;
10911         }
10912         if(ce.title){
10913             tipTitle.update(ce.title);
10914             tipTitle.show();
10915         }else{
10916             tipTitle.update('');
10917             tipTitle.hide();
10918         }
10919         el.dom.style.width  = tm.maxWidth+'px';
10920         //tipBody.dom.style.width = '';
10921         tipBodyText.update(o.text);
10922         var p = getPad(), w = ce.width;
10923         if(!w){
10924             var td = tipBodyText.dom;
10925             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10926             if(aw > tm.maxWidth){
10927                 w = tm.maxWidth;
10928             }else if(aw < tm.minWidth){
10929                 w = tm.minWidth;
10930             }else{
10931                 w = aw;
10932             }
10933         }
10934         //tipBody.setWidth(w);
10935         el.setWidth(parseInt(w, 10) + p);
10936         if(ce.autoHide === false){
10937             close.setDisplayed(true);
10938             if(dd){
10939                 dd.unlock();
10940             }
10941         }else{
10942             close.setDisplayed(false);
10943             if(dd){
10944                 dd.lock();
10945             }
10946         }
10947         if(xy){
10948             el.avoidY = xy[1]-18;
10949             el.setXY(xy);
10950         }
10951         if(tm.animate){
10952             el.setOpacity(.1);
10953             el.setStyle("visibility", "visible");
10954             el.fadeIn({callback: afterShow});
10955         }else{
10956             afterShow();
10957         }
10958     };
10959     
10960     var afterShow = function(){
10961         if(ce){
10962             el.show();
10963             esc.enable();
10964             if(tm.autoDismiss && ce.autoHide !== false){
10965                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10966             }
10967         }
10968     };
10969     
10970     var hide = function(noanim){
10971         clearTimeout(dismissProc);
10972         clearTimeout(hideProc);
10973         ce = null;
10974         if(el.isVisible()){
10975             esc.disable();
10976             if(noanim !== true && tm.animate){
10977                 el.fadeOut({callback: afterHide});
10978             }else{
10979                 afterHide();
10980             } 
10981         }
10982     };
10983     
10984     var afterHide = function(){
10985         el.hide();
10986         if(removeCls){
10987             el.removeClass(removeCls);
10988             removeCls = null;
10989         }
10990     };
10991     
10992     return {
10993         /**
10994         * @cfg {Number} minWidth
10995         * The minimum width of the quick tip (defaults to 40)
10996         */
10997        minWidth : 40,
10998         /**
10999         * @cfg {Number} maxWidth
11000         * The maximum width of the quick tip (defaults to 300)
11001         */
11002        maxWidth : 300,
11003         /**
11004         * @cfg {Boolean} interceptTitles
11005         * True to automatically use the element's DOM title value if available (defaults to false)
11006         */
11007        interceptTitles : false,
11008         /**
11009         * @cfg {Boolean} trackMouse
11010         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11011         */
11012        trackMouse : false,
11013         /**
11014         * @cfg {Boolean} hideOnClick
11015         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11016         */
11017        hideOnClick : true,
11018         /**
11019         * @cfg {Number} showDelay
11020         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11021         */
11022        showDelay : 500,
11023         /**
11024         * @cfg {Number} hideDelay
11025         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11026         */
11027        hideDelay : 200,
11028         /**
11029         * @cfg {Boolean} autoHide
11030         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11031         * Used in conjunction with hideDelay.
11032         */
11033        autoHide : true,
11034         /**
11035         * @cfg {Boolean}
11036         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11037         * (defaults to true).  Used in conjunction with autoDismissDelay.
11038         */
11039        autoDismiss : true,
11040         /**
11041         * @cfg {Number}
11042         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11043         */
11044        autoDismissDelay : 5000,
11045        /**
11046         * @cfg {Boolean} animate
11047         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11048         */
11049        animate : false,
11050
11051        /**
11052         * @cfg {String} title
11053         * Title text to display (defaults to '').  This can be any valid HTML markup.
11054         */
11055         title: '',
11056        /**
11057         * @cfg {String} text
11058         * Body text to display (defaults to '').  This can be any valid HTML markup.
11059         */
11060         text : '',
11061        /**
11062         * @cfg {String} cls
11063         * A CSS class to apply to the base quick tip element (defaults to '').
11064         */
11065         cls : '',
11066        /**
11067         * @cfg {Number} width
11068         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11069         * minWidth or maxWidth.
11070         */
11071         width : null,
11072
11073     /**
11074      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11075      * or display QuickTips in a page.
11076      */
11077        init : function(){
11078           tm = Roo.QuickTips;
11079           cfg = tm.tagConfig;
11080           if(!inited){
11081               if(!Roo.isReady){ // allow calling of init() before onReady
11082                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11083                   return;
11084               }
11085               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11086               el.fxDefaults = {stopFx: true};
11087               // maximum custom styling
11088               //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>');
11089               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>');              
11090               tipTitle = el.child('h3');
11091               tipTitle.enableDisplayMode("block");
11092               tipBody = el.child('div.x-tip-bd');
11093               tipBodyText = el.child('div.x-tip-bd-inner');
11094               //bdLeft = el.child('div.x-tip-bd-left');
11095               //bdRight = el.child('div.x-tip-bd-right');
11096               close = el.child('div.x-tip-close');
11097               close.enableDisplayMode("block");
11098               close.on("click", hide);
11099               var d = Roo.get(document);
11100               d.on("mousedown", onDown);
11101               d.on("mouseover", onOver);
11102               d.on("mouseout", onOut);
11103               d.on("mousemove", onMove);
11104               esc = d.addKeyListener(27, hide);
11105               esc.disable();
11106               if(Roo.dd.DD){
11107                   dd = el.initDD("default", null, {
11108                       onDrag : function(){
11109                           el.sync();  
11110                       }
11111                   });
11112                   dd.setHandleElId(tipTitle.id);
11113                   dd.lock();
11114               }
11115               inited = true;
11116           }
11117           this.enable(); 
11118        },
11119
11120     /**
11121      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11122      * are supported:
11123      * <pre>
11124 Property    Type                   Description
11125 ----------  ---------------------  ------------------------------------------------------------------------
11126 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11127      * </ul>
11128      * @param {Object} config The config object
11129      */
11130        register : function(config){
11131            var cs = config instanceof Array ? config : arguments;
11132            for(var i = 0, len = cs.length; i < len; i++) {
11133                var c = cs[i];
11134                var target = c.target;
11135                if(target){
11136                    if(target instanceof Array){
11137                        for(var j = 0, jlen = target.length; j < jlen; j++){
11138                            tagEls[target[j]] = c;
11139                        }
11140                    }else{
11141                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11142                    }
11143                }
11144            }
11145        },
11146
11147     /**
11148      * Removes this quick tip from its element and destroys it.
11149      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11150      */
11151        unregister : function(el){
11152            delete tagEls[Roo.id(el)];
11153        },
11154
11155     /**
11156      * Enable this quick tip.
11157      */
11158        enable : function(){
11159            if(inited && disabled){
11160                locks.pop();
11161                if(locks.length < 1){
11162                    disabled = false;
11163                }
11164            }
11165        },
11166
11167     /**
11168      * Disable this quick tip.
11169      */
11170        disable : function(){
11171           disabled = true;
11172           clearTimeout(showProc);
11173           clearTimeout(hideProc);
11174           clearTimeout(dismissProc);
11175           if(ce){
11176               hide(true);
11177           }
11178           locks.push(1);
11179        },
11180
11181     /**
11182      * Returns true if the quick tip is enabled, else false.
11183      */
11184        isEnabled : function(){
11185             return !disabled;
11186        },
11187
11188         // private
11189        tagConfig : {
11190            namespace : "roo", // was ext?? this may break..
11191            alt_namespace : "ext",
11192            attribute : "qtip",
11193            width : "width",
11194            target : "target",
11195            title : "qtitle",
11196            hide : "hide",
11197            cls : "qclass"
11198        }
11199    };
11200 }();
11201
11202 // backwards compat
11203 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11204  * Based on:
11205  * Ext JS Library 1.1.1
11206  * Copyright(c) 2006-2007, Ext JS, LLC.
11207  *
11208  * Originally Released Under LGPL - original licence link has changed is not relivant.
11209  *
11210  * Fork - LGPL
11211  * <script type="text/javascript">
11212  */
11213  
11214
11215 /**
11216  * @class Roo.tree.TreePanel
11217  * @extends Roo.data.Tree
11218  * @cfg {Roo.tree.TreeNode} root The root node
11219  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11220  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11221  * @cfg {Boolean} enableDD true to enable drag and drop
11222  * @cfg {Boolean} enableDrag true to enable just drag
11223  * @cfg {Boolean} enableDrop true to enable just drop
11224  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11225  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11226  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11227  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11228  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11229  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11230  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11231  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11232  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11233  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11234  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11235  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11236  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11237  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11238  * @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>
11239  * @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>
11240  * 
11241  * @constructor
11242  * @param {String/HTMLElement/Element} el The container element
11243  * @param {Object} config
11244  */
11245 Roo.tree.TreePanel = function(el, config){
11246     var root = false;
11247     var loader = false;
11248     if (config.root) {
11249         root = config.root;
11250         delete config.root;
11251     }
11252     if (config.loader) {
11253         loader = config.loader;
11254         delete config.loader;
11255     }
11256     
11257     Roo.apply(this, config);
11258     Roo.tree.TreePanel.superclass.constructor.call(this);
11259     this.el = Roo.get(el);
11260     this.el.addClass('x-tree');
11261     //console.log(root);
11262     if (root) {
11263         this.setRootNode( Roo.factory(root, Roo.tree));
11264     }
11265     if (loader) {
11266         this.loader = Roo.factory(loader, Roo.tree);
11267     }
11268    /**
11269     * Read-only. The id of the container element becomes this TreePanel's id.
11270     */
11271     this.id = this.el.id;
11272     this.addEvents({
11273         /**
11274         * @event beforeload
11275         * Fires before a node is loaded, return false to cancel
11276         * @param {Node} node The node being loaded
11277         */
11278         "beforeload" : true,
11279         /**
11280         * @event load
11281         * Fires when a node is loaded
11282         * @param {Node} node The node that was loaded
11283         */
11284         "load" : true,
11285         /**
11286         * @event textchange
11287         * Fires when the text for a node is changed
11288         * @param {Node} node The node
11289         * @param {String} text The new text
11290         * @param {String} oldText The old text
11291         */
11292         "textchange" : true,
11293         /**
11294         * @event beforeexpand
11295         * Fires before a node is expanded, return false to cancel.
11296         * @param {Node} node The node
11297         * @param {Boolean} deep
11298         * @param {Boolean} anim
11299         */
11300         "beforeexpand" : true,
11301         /**
11302         * @event beforecollapse
11303         * Fires before a node is collapsed, return false to cancel.
11304         * @param {Node} node The node
11305         * @param {Boolean} deep
11306         * @param {Boolean} anim
11307         */
11308         "beforecollapse" : true,
11309         /**
11310         * @event expand
11311         * Fires when a node is expanded
11312         * @param {Node} node The node
11313         */
11314         "expand" : true,
11315         /**
11316         * @event disabledchange
11317         * Fires when the disabled status of a node changes
11318         * @param {Node} node The node
11319         * @param {Boolean} disabled
11320         */
11321         "disabledchange" : true,
11322         /**
11323         * @event collapse
11324         * Fires when a node is collapsed
11325         * @param {Node} node The node
11326         */
11327         "collapse" : true,
11328         /**
11329         * @event beforeclick
11330         * Fires before click processing on a node. Return false to cancel the default action.
11331         * @param {Node} node The node
11332         * @param {Roo.EventObject} e The event object
11333         */
11334         "beforeclick":true,
11335         /**
11336         * @event checkchange
11337         * Fires when a node with a checkbox's checked property changes
11338         * @param {Node} this This node
11339         * @param {Boolean} checked
11340         */
11341         "checkchange":true,
11342         /**
11343         * @event click
11344         * Fires when a node is clicked
11345         * @param {Node} node The node
11346         * @param {Roo.EventObject} e The event object
11347         */
11348         "click":true,
11349         /**
11350         * @event dblclick
11351         * Fires when a node is double clicked
11352         * @param {Node} node The node
11353         * @param {Roo.EventObject} e The event object
11354         */
11355         "dblclick":true,
11356         /**
11357         * @event contextmenu
11358         * Fires when a node is right clicked
11359         * @param {Node} node The node
11360         * @param {Roo.EventObject} e The event object
11361         */
11362         "contextmenu":true,
11363         /**
11364         * @event beforechildrenrendered
11365         * Fires right before the child nodes for a node are rendered
11366         * @param {Node} node The node
11367         */
11368         "beforechildrenrendered":true,
11369         /**
11370         * @event startdrag
11371         * Fires when a node starts being dragged
11372         * @param {Roo.tree.TreePanel} this
11373         * @param {Roo.tree.TreeNode} node
11374         * @param {event} e The raw browser event
11375         */ 
11376        "startdrag" : true,
11377        /**
11378         * @event enddrag
11379         * Fires when a drag operation is complete
11380         * @param {Roo.tree.TreePanel} this
11381         * @param {Roo.tree.TreeNode} node
11382         * @param {event} e The raw browser event
11383         */
11384        "enddrag" : true,
11385        /**
11386         * @event dragdrop
11387         * Fires when a dragged node is dropped on a valid DD target
11388         * @param {Roo.tree.TreePanel} this
11389         * @param {Roo.tree.TreeNode} node
11390         * @param {DD} dd The dd it was dropped on
11391         * @param {event} e The raw browser event
11392         */
11393        "dragdrop" : true,
11394        /**
11395         * @event beforenodedrop
11396         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11397         * passed to handlers has the following properties:<br />
11398         * <ul style="padding:5px;padding-left:16px;">
11399         * <li>tree - The TreePanel</li>
11400         * <li>target - The node being targeted for the drop</li>
11401         * <li>data - The drag data from the drag source</li>
11402         * <li>point - The point of the drop - append, above or below</li>
11403         * <li>source - The drag source</li>
11404         * <li>rawEvent - Raw mouse event</li>
11405         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11406         * to be inserted by setting them on this object.</li>
11407         * <li>cancel - Set this to true to cancel the drop.</li>
11408         * </ul>
11409         * @param {Object} dropEvent
11410         */
11411        "beforenodedrop" : true,
11412        /**
11413         * @event nodedrop
11414         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11415         * passed to handlers has the following properties:<br />
11416         * <ul style="padding:5px;padding-left:16px;">
11417         * <li>tree - The TreePanel</li>
11418         * <li>target - The node being targeted for the drop</li>
11419         * <li>data - The drag data from the drag source</li>
11420         * <li>point - The point of the drop - append, above or below</li>
11421         * <li>source - The drag source</li>
11422         * <li>rawEvent - Raw mouse event</li>
11423         * <li>dropNode - Dropped node(s).</li>
11424         * </ul>
11425         * @param {Object} dropEvent
11426         */
11427        "nodedrop" : true,
11428         /**
11429         * @event nodedragover
11430         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11431         * passed to handlers has the following properties:<br />
11432         * <ul style="padding:5px;padding-left:16px;">
11433         * <li>tree - The TreePanel</li>
11434         * <li>target - The node being targeted for the drop</li>
11435         * <li>data - The drag data from the drag source</li>
11436         * <li>point - The point of the drop - append, above or below</li>
11437         * <li>source - The drag source</li>
11438         * <li>rawEvent - Raw mouse event</li>
11439         * <li>dropNode - Drop node(s) provided by the source.</li>
11440         * <li>cancel - Set this to true to signal drop not allowed.</li>
11441         * </ul>
11442         * @param {Object} dragOverEvent
11443         */
11444        "nodedragover" : true,
11445        /**
11446         * @event appendnode
11447         * Fires when append node to the tree
11448         * @param {Roo.tree.TreePanel} this
11449         * @param {Roo.tree.TreeNode} node
11450         * @param {Number} index The index of the newly appended node
11451         */
11452        "appendnode" : true
11453         
11454     });
11455     if(this.singleExpand){
11456        this.on("beforeexpand", this.restrictExpand, this);
11457     }
11458     if (this.editor) {
11459         this.editor.tree = this;
11460         this.editor = Roo.factory(this.editor, Roo.tree);
11461     }
11462     
11463     if (this.selModel) {
11464         this.selModel = Roo.factory(this.selModel, Roo.tree);
11465     }
11466    
11467 };
11468 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11469     rootVisible : true,
11470     animate: Roo.enableFx,
11471     lines : true,
11472     enableDD : false,
11473     hlDrop : Roo.enableFx,
11474   
11475     renderer: false,
11476     
11477     rendererTip: false,
11478     // private
11479     restrictExpand : function(node){
11480         var p = node.parentNode;
11481         if(p){
11482             if(p.expandedChild && p.expandedChild.parentNode == p){
11483                 p.expandedChild.collapse();
11484             }
11485             p.expandedChild = node;
11486         }
11487     },
11488
11489     // private override
11490     setRootNode : function(node){
11491         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11492         if(!this.rootVisible){
11493             node.ui = new Roo.tree.RootTreeNodeUI(node);
11494         }
11495         return node;
11496     },
11497
11498     /**
11499      * Returns the container element for this TreePanel
11500      */
11501     getEl : function(){
11502         return this.el;
11503     },
11504
11505     /**
11506      * Returns the default TreeLoader for this TreePanel
11507      */
11508     getLoader : function(){
11509         return this.loader;
11510     },
11511
11512     /**
11513      * Expand all nodes
11514      */
11515     expandAll : function(){
11516         this.root.expand(true);
11517     },
11518
11519     /**
11520      * Collapse all nodes
11521      */
11522     collapseAll : function(){
11523         this.root.collapse(true);
11524     },
11525
11526     /**
11527      * Returns the selection model used by this TreePanel
11528      */
11529     getSelectionModel : function(){
11530         if(!this.selModel){
11531             this.selModel = new Roo.tree.DefaultSelectionModel();
11532         }
11533         return this.selModel;
11534     },
11535
11536     /**
11537      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11538      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11539      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11540      * @return {Array}
11541      */
11542     getChecked : function(a, startNode){
11543         startNode = startNode || this.root;
11544         var r = [];
11545         var f = function(){
11546             if(this.attributes.checked){
11547                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11548             }
11549         }
11550         startNode.cascade(f);
11551         return r;
11552     },
11553
11554     /**
11555      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11556      * @param {String} path
11557      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11558      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11559      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11560      */
11561     expandPath : function(path, attr, callback){
11562         attr = attr || "id";
11563         var keys = path.split(this.pathSeparator);
11564         var curNode = this.root;
11565         if(curNode.attributes[attr] != keys[1]){ // invalid root
11566             if(callback){
11567                 callback(false, null);
11568             }
11569             return;
11570         }
11571         var index = 1;
11572         var f = function(){
11573             if(++index == keys.length){
11574                 if(callback){
11575                     callback(true, curNode);
11576                 }
11577                 return;
11578             }
11579             var c = curNode.findChild(attr, keys[index]);
11580             if(!c){
11581                 if(callback){
11582                     callback(false, curNode);
11583                 }
11584                 return;
11585             }
11586             curNode = c;
11587             c.expand(false, false, f);
11588         };
11589         curNode.expand(false, false, f);
11590     },
11591
11592     /**
11593      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11594      * @param {String} path
11595      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11596      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11597      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11598      */
11599     selectPath : function(path, attr, callback){
11600         attr = attr || "id";
11601         var keys = path.split(this.pathSeparator);
11602         var v = keys.pop();
11603         if(keys.length > 0){
11604             var f = function(success, node){
11605                 if(success && node){
11606                     var n = node.findChild(attr, v);
11607                     if(n){
11608                         n.select();
11609                         if(callback){
11610                             callback(true, n);
11611                         }
11612                     }else if(callback){
11613                         callback(false, n);
11614                     }
11615                 }else{
11616                     if(callback){
11617                         callback(false, n);
11618                     }
11619                 }
11620             };
11621             this.expandPath(keys.join(this.pathSeparator), attr, f);
11622         }else{
11623             this.root.select();
11624             if(callback){
11625                 callback(true, this.root);
11626             }
11627         }
11628     },
11629
11630     getTreeEl : function(){
11631         return this.el;
11632     },
11633
11634     /**
11635      * Trigger rendering of this TreePanel
11636      */
11637     render : function(){
11638         if (this.innerCt) {
11639             return this; // stop it rendering more than once!!
11640         }
11641         
11642         this.innerCt = this.el.createChild({tag:"ul",
11643                cls:"x-tree-root-ct " +
11644                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11645
11646         if(this.containerScroll){
11647             Roo.dd.ScrollManager.register(this.el);
11648         }
11649         if((this.enableDD || this.enableDrop) && !this.dropZone){
11650            /**
11651             * The dropZone used by this tree if drop is enabled
11652             * @type Roo.tree.TreeDropZone
11653             */
11654              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11655                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11656            });
11657         }
11658         if((this.enableDD || this.enableDrag) && !this.dragZone){
11659            /**
11660             * The dragZone used by this tree if drag is enabled
11661             * @type Roo.tree.TreeDragZone
11662             */
11663             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11664                ddGroup: this.ddGroup || "TreeDD",
11665                scroll: this.ddScroll
11666            });
11667         }
11668         this.getSelectionModel().init(this);
11669         if (!this.root) {
11670             Roo.log("ROOT not set in tree");
11671             return this;
11672         }
11673         this.root.render();
11674         if(!this.rootVisible){
11675             this.root.renderChildren();
11676         }
11677         return this;
11678     }
11679 });/*
11680  * Based on:
11681  * Ext JS Library 1.1.1
11682  * Copyright(c) 2006-2007, Ext JS, LLC.
11683  *
11684  * Originally Released Under LGPL - original licence link has changed is not relivant.
11685  *
11686  * Fork - LGPL
11687  * <script type="text/javascript">
11688  */
11689  
11690
11691 /**
11692  * @class Roo.tree.DefaultSelectionModel
11693  * @extends Roo.util.Observable
11694  * The default single selection for a TreePanel.
11695  * @param {Object} cfg Configuration
11696  */
11697 Roo.tree.DefaultSelectionModel = function(cfg){
11698    this.selNode = null;
11699    
11700    
11701    
11702    this.addEvents({
11703        /**
11704         * @event selectionchange
11705         * Fires when the selected node changes
11706         * @param {DefaultSelectionModel} this
11707         * @param {TreeNode} node the new selection
11708         */
11709        "selectionchange" : true,
11710
11711        /**
11712         * @event beforeselect
11713         * Fires before the selected node changes, return false to cancel the change
11714         * @param {DefaultSelectionModel} this
11715         * @param {TreeNode} node the new selection
11716         * @param {TreeNode} node the old selection
11717         */
11718        "beforeselect" : true
11719    });
11720    
11721     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11722 };
11723
11724 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11725     init : function(tree){
11726         this.tree = tree;
11727         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11728         tree.on("click", this.onNodeClick, this);
11729     },
11730     
11731     onNodeClick : function(node, e){
11732         if (e.ctrlKey && this.selNode == node)  {
11733             this.unselect(node);
11734             return;
11735         }
11736         this.select(node);
11737     },
11738     
11739     /**
11740      * Select a node.
11741      * @param {TreeNode} node The node to select
11742      * @return {TreeNode} The selected node
11743      */
11744     select : function(node){
11745         var last = this.selNode;
11746         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11747             if(last){
11748                 last.ui.onSelectedChange(false);
11749             }
11750             this.selNode = node;
11751             node.ui.onSelectedChange(true);
11752             this.fireEvent("selectionchange", this, node, last);
11753         }
11754         return node;
11755     },
11756     
11757     /**
11758      * Deselect a node.
11759      * @param {TreeNode} node The node to unselect
11760      */
11761     unselect : function(node){
11762         if(this.selNode == node){
11763             this.clearSelections();
11764         }    
11765     },
11766     
11767     /**
11768      * Clear all selections
11769      */
11770     clearSelections : function(){
11771         var n = this.selNode;
11772         if(n){
11773             n.ui.onSelectedChange(false);
11774             this.selNode = null;
11775             this.fireEvent("selectionchange", this, null);
11776         }
11777         return n;
11778     },
11779     
11780     /**
11781      * Get the selected node
11782      * @return {TreeNode} The selected node
11783      */
11784     getSelectedNode : function(){
11785         return this.selNode;    
11786     },
11787     
11788     /**
11789      * Returns true if the node is selected
11790      * @param {TreeNode} node The node to check
11791      * @return {Boolean}
11792      */
11793     isSelected : function(node){
11794         return this.selNode == node;  
11795     },
11796
11797     /**
11798      * Selects the node above the selected node in the tree, intelligently walking the nodes
11799      * @return TreeNode The new selection
11800      */
11801     selectPrevious : function(){
11802         var s = this.selNode || this.lastSelNode;
11803         if(!s){
11804             return null;
11805         }
11806         var ps = s.previousSibling;
11807         if(ps){
11808             if(!ps.isExpanded() || ps.childNodes.length < 1){
11809                 return this.select(ps);
11810             } else{
11811                 var lc = ps.lastChild;
11812                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11813                     lc = lc.lastChild;
11814                 }
11815                 return this.select(lc);
11816             }
11817         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11818             return this.select(s.parentNode);
11819         }
11820         return null;
11821     },
11822
11823     /**
11824      * Selects the node above the selected node in the tree, intelligently walking the nodes
11825      * @return TreeNode The new selection
11826      */
11827     selectNext : function(){
11828         var s = this.selNode || this.lastSelNode;
11829         if(!s){
11830             return null;
11831         }
11832         if(s.firstChild && s.isExpanded()){
11833              return this.select(s.firstChild);
11834          }else if(s.nextSibling){
11835              return this.select(s.nextSibling);
11836          }else if(s.parentNode){
11837             var newS = null;
11838             s.parentNode.bubble(function(){
11839                 if(this.nextSibling){
11840                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11841                     return false;
11842                 }
11843             });
11844             return newS;
11845          }
11846         return null;
11847     },
11848
11849     onKeyDown : function(e){
11850         var s = this.selNode || this.lastSelNode;
11851         // undesirable, but required
11852         var sm = this;
11853         if(!s){
11854             return;
11855         }
11856         var k = e.getKey();
11857         switch(k){
11858              case e.DOWN:
11859                  e.stopEvent();
11860                  this.selectNext();
11861              break;
11862              case e.UP:
11863                  e.stopEvent();
11864                  this.selectPrevious();
11865              break;
11866              case e.RIGHT:
11867                  e.preventDefault();
11868                  if(s.hasChildNodes()){
11869                      if(!s.isExpanded()){
11870                          s.expand();
11871                      }else if(s.firstChild){
11872                          this.select(s.firstChild, e);
11873                      }
11874                  }
11875              break;
11876              case e.LEFT:
11877                  e.preventDefault();
11878                  if(s.hasChildNodes() && s.isExpanded()){
11879                      s.collapse();
11880                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11881                      this.select(s.parentNode, e);
11882                  }
11883              break;
11884         };
11885     }
11886 });
11887
11888 /**
11889  * @class Roo.tree.MultiSelectionModel
11890  * @extends Roo.util.Observable
11891  * Multi selection for a TreePanel.
11892  * @param {Object} cfg Configuration
11893  */
11894 Roo.tree.MultiSelectionModel = function(){
11895    this.selNodes = [];
11896    this.selMap = {};
11897    this.addEvents({
11898        /**
11899         * @event selectionchange
11900         * Fires when the selected nodes change
11901         * @param {MultiSelectionModel} this
11902         * @param {Array} nodes Array of the selected nodes
11903         */
11904        "selectionchange" : true
11905    });
11906    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11907    
11908 };
11909
11910 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11911     init : function(tree){
11912         this.tree = tree;
11913         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11914         tree.on("click", this.onNodeClick, this);
11915     },
11916     
11917     onNodeClick : function(node, e){
11918         this.select(node, e, e.ctrlKey);
11919     },
11920     
11921     /**
11922      * Select a node.
11923      * @param {TreeNode} node The node to select
11924      * @param {EventObject} e (optional) An event associated with the selection
11925      * @param {Boolean} keepExisting True to retain existing selections
11926      * @return {TreeNode} The selected node
11927      */
11928     select : function(node, e, keepExisting){
11929         if(keepExisting !== true){
11930             this.clearSelections(true);
11931         }
11932         if(this.isSelected(node)){
11933             this.lastSelNode = node;
11934             return node;
11935         }
11936         this.selNodes.push(node);
11937         this.selMap[node.id] = node;
11938         this.lastSelNode = node;
11939         node.ui.onSelectedChange(true);
11940         this.fireEvent("selectionchange", this, this.selNodes);
11941         return node;
11942     },
11943     
11944     /**
11945      * Deselect a node.
11946      * @param {TreeNode} node The node to unselect
11947      */
11948     unselect : function(node){
11949         if(this.selMap[node.id]){
11950             node.ui.onSelectedChange(false);
11951             var sn = this.selNodes;
11952             var index = -1;
11953             if(sn.indexOf){
11954                 index = sn.indexOf(node);
11955             }else{
11956                 for(var i = 0, len = sn.length; i < len; i++){
11957                     if(sn[i] == node){
11958                         index = i;
11959                         break;
11960                     }
11961                 }
11962             }
11963             if(index != -1){
11964                 this.selNodes.splice(index, 1);
11965             }
11966             delete this.selMap[node.id];
11967             this.fireEvent("selectionchange", this, this.selNodes);
11968         }
11969     },
11970     
11971     /**
11972      * Clear all selections
11973      */
11974     clearSelections : function(suppressEvent){
11975         var sn = this.selNodes;
11976         if(sn.length > 0){
11977             for(var i = 0, len = sn.length; i < len; i++){
11978                 sn[i].ui.onSelectedChange(false);
11979             }
11980             this.selNodes = [];
11981             this.selMap = {};
11982             if(suppressEvent !== true){
11983                 this.fireEvent("selectionchange", this, this.selNodes);
11984             }
11985         }
11986     },
11987     
11988     /**
11989      * Returns true if the node is selected
11990      * @param {TreeNode} node The node to check
11991      * @return {Boolean}
11992      */
11993     isSelected : function(node){
11994         return this.selMap[node.id] ? true : false;  
11995     },
11996     
11997     /**
11998      * Returns an array of the selected nodes
11999      * @return {Array}
12000      */
12001     getSelectedNodes : function(){
12002         return this.selNodes;    
12003     },
12004
12005     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12006
12007     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12008
12009     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12010 });/*
12011  * Based on:
12012  * Ext JS Library 1.1.1
12013  * Copyright(c) 2006-2007, Ext JS, LLC.
12014  *
12015  * Originally Released Under LGPL - original licence link has changed is not relivant.
12016  *
12017  * Fork - LGPL
12018  * <script type="text/javascript">
12019  */
12020  
12021 /**
12022  * @class Roo.tree.TreeNode
12023  * @extends Roo.data.Node
12024  * @cfg {String} text The text for this node
12025  * @cfg {Boolean} expanded true to start the node expanded
12026  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12027  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12028  * @cfg {Boolean} disabled true to start the node disabled
12029  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12030  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12031  * @cfg {String} cls A css class to be added to the node
12032  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12033  * @cfg {String} href URL of the link used for the node (defaults to #)
12034  * @cfg {String} hrefTarget target frame for the link
12035  * @cfg {String} qtip An Ext QuickTip for the node
12036  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12037  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12038  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12039  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12040  * (defaults to undefined with no checkbox rendered)
12041  * @constructor
12042  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12043  */
12044 Roo.tree.TreeNode = function(attributes){
12045     attributes = attributes || {};
12046     if(typeof attributes == "string"){
12047         attributes = {text: attributes};
12048     }
12049     this.childrenRendered = false;
12050     this.rendered = false;
12051     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12052     this.expanded = attributes.expanded === true;
12053     this.isTarget = attributes.isTarget !== false;
12054     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12055     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12056
12057     /**
12058      * Read-only. The text for this node. To change it use setText().
12059      * @type String
12060      */
12061     this.text = attributes.text;
12062     /**
12063      * True if this node is disabled.
12064      * @type Boolean
12065      */
12066     this.disabled = attributes.disabled === true;
12067
12068     this.addEvents({
12069         /**
12070         * @event textchange
12071         * Fires when the text for this node is changed
12072         * @param {Node} this This node
12073         * @param {String} text The new text
12074         * @param {String} oldText The old text
12075         */
12076         "textchange" : true,
12077         /**
12078         * @event beforeexpand
12079         * Fires before this node is expanded, return false to cancel.
12080         * @param {Node} this This node
12081         * @param {Boolean} deep
12082         * @param {Boolean} anim
12083         */
12084         "beforeexpand" : true,
12085         /**
12086         * @event beforecollapse
12087         * Fires before this node is collapsed, return false to cancel.
12088         * @param {Node} this This node
12089         * @param {Boolean} deep
12090         * @param {Boolean} anim
12091         */
12092         "beforecollapse" : true,
12093         /**
12094         * @event expand
12095         * Fires when this node is expanded
12096         * @param {Node} this This node
12097         */
12098         "expand" : true,
12099         /**
12100         * @event disabledchange
12101         * Fires when the disabled status of this node changes
12102         * @param {Node} this This node
12103         * @param {Boolean} disabled
12104         */
12105         "disabledchange" : true,
12106         /**
12107         * @event collapse
12108         * Fires when this node is collapsed
12109         * @param {Node} this This node
12110         */
12111         "collapse" : true,
12112         /**
12113         * @event beforeclick
12114         * Fires before click processing. Return false to cancel the default action.
12115         * @param {Node} this This node
12116         * @param {Roo.EventObject} e The event object
12117         */
12118         "beforeclick":true,
12119         /**
12120         * @event checkchange
12121         * Fires when a node with a checkbox's checked property changes
12122         * @param {Node} this This node
12123         * @param {Boolean} checked
12124         */
12125         "checkchange":true,
12126         /**
12127         * @event click
12128         * Fires when this node is clicked
12129         * @param {Node} this This node
12130         * @param {Roo.EventObject} e The event object
12131         */
12132         "click":true,
12133         /**
12134         * @event dblclick
12135         * Fires when this node is double clicked
12136         * @param {Node} this This node
12137         * @param {Roo.EventObject} e The event object
12138         */
12139         "dblclick":true,
12140         /**
12141         * @event contextmenu
12142         * Fires when this node is right clicked
12143         * @param {Node} this This node
12144         * @param {Roo.EventObject} e The event object
12145         */
12146         "contextmenu":true,
12147         /**
12148         * @event beforechildrenrendered
12149         * Fires right before the child nodes for this node are rendered
12150         * @param {Node} this This node
12151         */
12152         "beforechildrenrendered":true
12153     });
12154
12155     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12156
12157     /**
12158      * Read-only. The UI for this node
12159      * @type TreeNodeUI
12160      */
12161     this.ui = new uiClass(this);
12162     
12163     // finally support items[]
12164     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12165         return;
12166     }
12167     
12168     
12169     Roo.each(this.attributes.items, function(c) {
12170         this.appendChild(Roo.factory(c,Roo.Tree));
12171     }, this);
12172     delete this.attributes.items;
12173     
12174     
12175     
12176 };
12177 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12178     preventHScroll: true,
12179     /**
12180      * Returns true if this node is expanded
12181      * @return {Boolean}
12182      */
12183     isExpanded : function(){
12184         return this.expanded;
12185     },
12186
12187     /**
12188      * Returns the UI object for this node
12189      * @return {TreeNodeUI}
12190      */
12191     getUI : function(){
12192         return this.ui;
12193     },
12194
12195     // private override
12196     setFirstChild : function(node){
12197         var of = this.firstChild;
12198         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12199         if(this.childrenRendered && of && node != of){
12200             of.renderIndent(true, true);
12201         }
12202         if(this.rendered){
12203             this.renderIndent(true, true);
12204         }
12205     },
12206
12207     // private override
12208     setLastChild : function(node){
12209         var ol = this.lastChild;
12210         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12211         if(this.childrenRendered && ol && node != ol){
12212             ol.renderIndent(true, true);
12213         }
12214         if(this.rendered){
12215             this.renderIndent(true, true);
12216         }
12217     },
12218
12219     // these methods are overridden to provide lazy rendering support
12220     // private override
12221     appendChild : function()
12222     {
12223         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12224         if(node && this.childrenRendered){
12225             node.render();
12226         }
12227         this.ui.updateExpandIcon();
12228         return node;
12229     },
12230
12231     // private override
12232     removeChild : function(node){
12233         this.ownerTree.getSelectionModel().unselect(node);
12234         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12235         // if it's been rendered remove dom node
12236         if(this.childrenRendered){
12237             node.ui.remove();
12238         }
12239         if(this.childNodes.length < 1){
12240             this.collapse(false, false);
12241         }else{
12242             this.ui.updateExpandIcon();
12243         }
12244         if(!this.firstChild) {
12245             this.childrenRendered = false;
12246         }
12247         return node;
12248     },
12249
12250     // private override
12251     insertBefore : function(node, refNode){
12252         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12253         if(newNode && refNode && this.childrenRendered){
12254             node.render();
12255         }
12256         this.ui.updateExpandIcon();
12257         return newNode;
12258     },
12259
12260     /**
12261      * Sets the text for this node
12262      * @param {String} text
12263      */
12264     setText : function(text){
12265         var oldText = this.text;
12266         this.text = text;
12267         this.attributes.text = text;
12268         if(this.rendered){ // event without subscribing
12269             this.ui.onTextChange(this, text, oldText);
12270         }
12271         this.fireEvent("textchange", this, text, oldText);
12272     },
12273
12274     /**
12275      * Triggers selection of this node
12276      */
12277     select : function(){
12278         this.getOwnerTree().getSelectionModel().select(this);
12279     },
12280
12281     /**
12282      * Triggers deselection of this node
12283      */
12284     unselect : function(){
12285         this.getOwnerTree().getSelectionModel().unselect(this);
12286     },
12287
12288     /**
12289      * Returns true if this node is selected
12290      * @return {Boolean}
12291      */
12292     isSelected : function(){
12293         return this.getOwnerTree().getSelectionModel().isSelected(this);
12294     },
12295
12296     /**
12297      * Expand this node.
12298      * @param {Boolean} deep (optional) True to expand all children as well
12299      * @param {Boolean} anim (optional) false to cancel the default animation
12300      * @param {Function} callback (optional) A callback to be called when
12301      * expanding this node completes (does not wait for deep expand to complete).
12302      * Called with 1 parameter, this node.
12303      */
12304     expand : function(deep, anim, callback){
12305         if(!this.expanded){
12306             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12307                 return;
12308             }
12309             if(!this.childrenRendered){
12310                 this.renderChildren();
12311             }
12312             this.expanded = true;
12313             
12314             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12315                 this.ui.animExpand(function(){
12316                     this.fireEvent("expand", this);
12317                     if(typeof callback == "function"){
12318                         callback(this);
12319                     }
12320                     if(deep === true){
12321                         this.expandChildNodes(true);
12322                     }
12323                 }.createDelegate(this));
12324                 return;
12325             }else{
12326                 this.ui.expand();
12327                 this.fireEvent("expand", this);
12328                 if(typeof callback == "function"){
12329                     callback(this);
12330                 }
12331             }
12332         }else{
12333            if(typeof callback == "function"){
12334                callback(this);
12335            }
12336         }
12337         if(deep === true){
12338             this.expandChildNodes(true);
12339         }
12340     },
12341
12342     isHiddenRoot : function(){
12343         return this.isRoot && !this.getOwnerTree().rootVisible;
12344     },
12345
12346     /**
12347      * Collapse this node.
12348      * @param {Boolean} deep (optional) True to collapse all children as well
12349      * @param {Boolean} anim (optional) false to cancel the default animation
12350      */
12351     collapse : function(deep, anim){
12352         if(this.expanded && !this.isHiddenRoot()){
12353             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12354                 return;
12355             }
12356             this.expanded = false;
12357             if((this.getOwnerTree().animate && anim !== false) || anim){
12358                 this.ui.animCollapse(function(){
12359                     this.fireEvent("collapse", this);
12360                     if(deep === true){
12361                         this.collapseChildNodes(true);
12362                     }
12363                 }.createDelegate(this));
12364                 return;
12365             }else{
12366                 this.ui.collapse();
12367                 this.fireEvent("collapse", this);
12368             }
12369         }
12370         if(deep === true){
12371             var cs = this.childNodes;
12372             for(var i = 0, len = cs.length; i < len; i++) {
12373                 cs[i].collapse(true, false);
12374             }
12375         }
12376     },
12377
12378     // private
12379     delayedExpand : function(delay){
12380         if(!this.expandProcId){
12381             this.expandProcId = this.expand.defer(delay, this);
12382         }
12383     },
12384
12385     // private
12386     cancelExpand : function(){
12387         if(this.expandProcId){
12388             clearTimeout(this.expandProcId);
12389         }
12390         this.expandProcId = false;
12391     },
12392
12393     /**
12394      * Toggles expanded/collapsed state of the node
12395      */
12396     toggle : function(){
12397         if(this.expanded){
12398             this.collapse();
12399         }else{
12400             this.expand();
12401         }
12402     },
12403
12404     /**
12405      * Ensures all parent nodes are expanded
12406      */
12407     ensureVisible : function(callback){
12408         var tree = this.getOwnerTree();
12409         tree.expandPath(this.parentNode.getPath(), false, function(){
12410             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12411             Roo.callback(callback);
12412         }.createDelegate(this));
12413     },
12414
12415     /**
12416      * Expand all child nodes
12417      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12418      */
12419     expandChildNodes : function(deep){
12420         var cs = this.childNodes;
12421         for(var i = 0, len = cs.length; i < len; i++) {
12422                 cs[i].expand(deep);
12423         }
12424     },
12425
12426     /**
12427      * Collapse all child nodes
12428      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12429      */
12430     collapseChildNodes : function(deep){
12431         var cs = this.childNodes;
12432         for(var i = 0, len = cs.length; i < len; i++) {
12433                 cs[i].collapse(deep);
12434         }
12435     },
12436
12437     /**
12438      * Disables this node
12439      */
12440     disable : function(){
12441         this.disabled = true;
12442         this.unselect();
12443         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12444             this.ui.onDisableChange(this, true);
12445         }
12446         this.fireEvent("disabledchange", this, true);
12447     },
12448
12449     /**
12450      * Enables this node
12451      */
12452     enable : function(){
12453         this.disabled = false;
12454         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12455             this.ui.onDisableChange(this, false);
12456         }
12457         this.fireEvent("disabledchange", this, false);
12458     },
12459
12460     // private
12461     renderChildren : function(suppressEvent){
12462         if(suppressEvent !== false){
12463             this.fireEvent("beforechildrenrendered", this);
12464         }
12465         var cs = this.childNodes;
12466         for(var i = 0, len = cs.length; i < len; i++){
12467             cs[i].render(true);
12468         }
12469         this.childrenRendered = true;
12470     },
12471
12472     // private
12473     sort : function(fn, scope){
12474         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12475         if(this.childrenRendered){
12476             var cs = this.childNodes;
12477             for(var i = 0, len = cs.length; i < len; i++){
12478                 cs[i].render(true);
12479             }
12480         }
12481     },
12482
12483     // private
12484     render : function(bulkRender){
12485         this.ui.render(bulkRender);
12486         if(!this.rendered){
12487             this.rendered = true;
12488             if(this.expanded){
12489                 this.expanded = false;
12490                 this.expand(false, false);
12491             }
12492         }
12493     },
12494
12495     // private
12496     renderIndent : function(deep, refresh){
12497         if(refresh){
12498             this.ui.childIndent = null;
12499         }
12500         this.ui.renderIndent();
12501         if(deep === true && this.childrenRendered){
12502             var cs = this.childNodes;
12503             for(var i = 0, len = cs.length; i < len; i++){
12504                 cs[i].renderIndent(true, refresh);
12505             }
12506         }
12507     }
12508 });/*
12509  * Based on:
12510  * Ext JS Library 1.1.1
12511  * Copyright(c) 2006-2007, Ext JS, LLC.
12512  *
12513  * Originally Released Under LGPL - original licence link has changed is not relivant.
12514  *
12515  * Fork - LGPL
12516  * <script type="text/javascript">
12517  */
12518  
12519 /**
12520  * @class Roo.tree.AsyncTreeNode
12521  * @extends Roo.tree.TreeNode
12522  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12523  * @constructor
12524  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12525  */
12526  Roo.tree.AsyncTreeNode = function(config){
12527     this.loaded = false;
12528     this.loading = false;
12529     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12530     /**
12531     * @event beforeload
12532     * Fires before this node is loaded, return false to cancel
12533     * @param {Node} this This node
12534     */
12535     this.addEvents({'beforeload':true, 'load': true});
12536     /**
12537     * @event load
12538     * Fires when this node is loaded
12539     * @param {Node} this This node
12540     */
12541     /**
12542      * The loader used by this node (defaults to using the tree's defined loader)
12543      * @type TreeLoader
12544      * @property loader
12545      */
12546 };
12547 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12548     expand : function(deep, anim, callback){
12549         if(this.loading){ // if an async load is already running, waiting til it's done
12550             var timer;
12551             var f = function(){
12552                 if(!this.loading){ // done loading
12553                     clearInterval(timer);
12554                     this.expand(deep, anim, callback);
12555                 }
12556             }.createDelegate(this);
12557             timer = setInterval(f, 200);
12558             return;
12559         }
12560         if(!this.loaded){
12561             if(this.fireEvent("beforeload", this) === false){
12562                 return;
12563             }
12564             this.loading = true;
12565             this.ui.beforeLoad(this);
12566             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12567             if(loader){
12568                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12569                 return;
12570             }
12571         }
12572         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12573     },
12574     
12575     /**
12576      * Returns true if this node is currently loading
12577      * @return {Boolean}
12578      */
12579     isLoading : function(){
12580         return this.loading;  
12581     },
12582     
12583     loadComplete : function(deep, anim, callback){
12584         this.loading = false;
12585         this.loaded = true;
12586         this.ui.afterLoad(this);
12587         this.fireEvent("load", this);
12588         this.expand(deep, anim, callback);
12589     },
12590     
12591     /**
12592      * Returns true if this node has been loaded
12593      * @return {Boolean}
12594      */
12595     isLoaded : function(){
12596         return this.loaded;
12597     },
12598     
12599     hasChildNodes : function(){
12600         if(!this.isLeaf() && !this.loaded){
12601             return true;
12602         }else{
12603             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12604         }
12605     },
12606
12607     /**
12608      * Trigger a reload for this node
12609      * @param {Function} callback
12610      */
12611     reload : function(callback){
12612         this.collapse(false, false);
12613         while(this.firstChild){
12614             this.removeChild(this.firstChild);
12615         }
12616         this.childrenRendered = false;
12617         this.loaded = false;
12618         if(this.isHiddenRoot()){
12619             this.expanded = false;
12620         }
12621         this.expand(false, false, callback);
12622     }
12623 });/*
12624  * Based on:
12625  * Ext JS Library 1.1.1
12626  * Copyright(c) 2006-2007, Ext JS, LLC.
12627  *
12628  * Originally Released Under LGPL - original licence link has changed is not relivant.
12629  *
12630  * Fork - LGPL
12631  * <script type="text/javascript">
12632  */
12633  
12634 /**
12635  * @class Roo.tree.TreeNodeUI
12636  * @constructor
12637  * @param {Object} node The node to render
12638  * The TreeNode UI implementation is separate from the
12639  * tree implementation. Unless you are customizing the tree UI,
12640  * you should never have to use this directly.
12641  */
12642 Roo.tree.TreeNodeUI = function(node){
12643     this.node = node;
12644     this.rendered = false;
12645     this.animating = false;
12646     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12647 };
12648
12649 Roo.tree.TreeNodeUI.prototype = {
12650     removeChild : function(node){
12651         if(this.rendered){
12652             this.ctNode.removeChild(node.ui.getEl());
12653         }
12654     },
12655
12656     beforeLoad : function(){
12657          this.addClass("x-tree-node-loading");
12658     },
12659
12660     afterLoad : function(){
12661          this.removeClass("x-tree-node-loading");
12662     },
12663
12664     onTextChange : function(node, text, oldText){
12665         if(this.rendered){
12666             this.textNode.innerHTML = text;
12667         }
12668     },
12669
12670     onDisableChange : function(node, state){
12671         this.disabled = state;
12672         if(state){
12673             this.addClass("x-tree-node-disabled");
12674         }else{
12675             this.removeClass("x-tree-node-disabled");
12676         }
12677     },
12678
12679     onSelectedChange : function(state){
12680         if(state){
12681             this.focus();
12682             this.addClass("x-tree-selected");
12683         }else{
12684             //this.blur();
12685             this.removeClass("x-tree-selected");
12686         }
12687     },
12688
12689     onMove : function(tree, node, oldParent, newParent, index, refNode){
12690         this.childIndent = null;
12691         if(this.rendered){
12692             var targetNode = newParent.ui.getContainer();
12693             if(!targetNode){//target not rendered
12694                 this.holder = document.createElement("div");
12695                 this.holder.appendChild(this.wrap);
12696                 return;
12697             }
12698             var insertBefore = refNode ? refNode.ui.getEl() : null;
12699             if(insertBefore){
12700                 targetNode.insertBefore(this.wrap, insertBefore);
12701             }else{
12702                 targetNode.appendChild(this.wrap);
12703             }
12704             this.node.renderIndent(true);
12705         }
12706     },
12707
12708     addClass : function(cls){
12709         if(this.elNode){
12710             Roo.fly(this.elNode).addClass(cls);
12711         }
12712     },
12713
12714     removeClass : function(cls){
12715         if(this.elNode){
12716             Roo.fly(this.elNode).removeClass(cls);
12717         }
12718     },
12719
12720     remove : function(){
12721         if(this.rendered){
12722             this.holder = document.createElement("div");
12723             this.holder.appendChild(this.wrap);
12724         }
12725     },
12726
12727     fireEvent : function(){
12728         return this.node.fireEvent.apply(this.node, arguments);
12729     },
12730
12731     initEvents : function(){
12732         this.node.on("move", this.onMove, this);
12733         var E = Roo.EventManager;
12734         var a = this.anchor;
12735
12736         var el = Roo.fly(a, '_treeui');
12737
12738         if(Roo.isOpera){ // opera render bug ignores the CSS
12739             el.setStyle("text-decoration", "none");
12740         }
12741
12742         el.on("click", this.onClick, this);
12743         el.on("dblclick", this.onDblClick, this);
12744
12745         if(this.checkbox){
12746             Roo.EventManager.on(this.checkbox,
12747                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12748         }
12749
12750         el.on("contextmenu", this.onContextMenu, this);
12751
12752         var icon = Roo.fly(this.iconNode);
12753         icon.on("click", this.onClick, this);
12754         icon.on("dblclick", this.onDblClick, this);
12755         icon.on("contextmenu", this.onContextMenu, this);
12756         E.on(this.ecNode, "click", this.ecClick, this, true);
12757
12758         if(this.node.disabled){
12759             this.addClass("x-tree-node-disabled");
12760         }
12761         if(this.node.hidden){
12762             this.addClass("x-tree-node-disabled");
12763         }
12764         var ot = this.node.getOwnerTree();
12765         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12766         if(dd && (!this.node.isRoot || ot.rootVisible)){
12767             Roo.dd.Registry.register(this.elNode, {
12768                 node: this.node,
12769                 handles: this.getDDHandles(),
12770                 isHandle: false
12771             });
12772         }
12773     },
12774
12775     getDDHandles : function(){
12776         return [this.iconNode, this.textNode];
12777     },
12778
12779     hide : function(){
12780         if(this.rendered){
12781             this.wrap.style.display = "none";
12782         }
12783     },
12784
12785     show : function(){
12786         if(this.rendered){
12787             this.wrap.style.display = "";
12788         }
12789     },
12790
12791     onContextMenu : function(e){
12792         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12793             e.preventDefault();
12794             this.focus();
12795             this.fireEvent("contextmenu", this.node, e);
12796         }
12797     },
12798
12799     onClick : function(e){
12800         if(this.dropping){
12801             e.stopEvent();
12802             return;
12803         }
12804         if(this.fireEvent("beforeclick", this.node, e) !== false){
12805             if(!this.disabled && this.node.attributes.href){
12806                 this.fireEvent("click", this.node, e);
12807                 return;
12808             }
12809             e.preventDefault();
12810             if(this.disabled){
12811                 return;
12812             }
12813
12814             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12815                 this.node.toggle();
12816             }
12817
12818             this.fireEvent("click", this.node, e);
12819         }else{
12820             e.stopEvent();
12821         }
12822     },
12823
12824     onDblClick : function(e){
12825         e.preventDefault();
12826         if(this.disabled){
12827             return;
12828         }
12829         if(this.checkbox){
12830             this.toggleCheck();
12831         }
12832         if(!this.animating && this.node.hasChildNodes()){
12833             this.node.toggle();
12834         }
12835         this.fireEvent("dblclick", this.node, e);
12836     },
12837
12838     onCheckChange : function(){
12839         var checked = this.checkbox.checked;
12840         this.node.attributes.checked = checked;
12841         this.fireEvent('checkchange', this.node, checked);
12842     },
12843
12844     ecClick : function(e){
12845         if(!this.animating && this.node.hasChildNodes()){
12846             this.node.toggle();
12847         }
12848     },
12849
12850     startDrop : function(){
12851         this.dropping = true;
12852     },
12853
12854     // delayed drop so the click event doesn't get fired on a drop
12855     endDrop : function(){
12856        setTimeout(function(){
12857            this.dropping = false;
12858        }.createDelegate(this), 50);
12859     },
12860
12861     expand : function(){
12862         this.updateExpandIcon();
12863         this.ctNode.style.display = "";
12864     },
12865
12866     focus : function(){
12867         if(!this.node.preventHScroll){
12868             try{this.anchor.focus();
12869             }catch(e){}
12870         }else if(!Roo.isIE){
12871             try{
12872                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12873                 var l = noscroll.scrollLeft;
12874                 this.anchor.focus();
12875                 noscroll.scrollLeft = l;
12876             }catch(e){}
12877         }
12878     },
12879
12880     toggleCheck : function(value){
12881         var cb = this.checkbox;
12882         if(cb){
12883             cb.checked = (value === undefined ? !cb.checked : value);
12884         }
12885     },
12886
12887     blur : function(){
12888         try{
12889             this.anchor.blur();
12890         }catch(e){}
12891     },
12892
12893     animExpand : function(callback){
12894         var ct = Roo.get(this.ctNode);
12895         ct.stopFx();
12896         if(!this.node.hasChildNodes()){
12897             this.updateExpandIcon();
12898             this.ctNode.style.display = "";
12899             Roo.callback(callback);
12900             return;
12901         }
12902         this.animating = true;
12903         this.updateExpandIcon();
12904
12905         ct.slideIn('t', {
12906            callback : function(){
12907                this.animating = false;
12908                Roo.callback(callback);
12909             },
12910             scope: this,
12911             duration: this.node.ownerTree.duration || .25
12912         });
12913     },
12914
12915     highlight : function(){
12916         var tree = this.node.getOwnerTree();
12917         Roo.fly(this.wrap).highlight(
12918             tree.hlColor || "C3DAF9",
12919             {endColor: tree.hlBaseColor}
12920         );
12921     },
12922
12923     collapse : function(){
12924         this.updateExpandIcon();
12925         this.ctNode.style.display = "none";
12926     },
12927
12928     animCollapse : function(callback){
12929         var ct = Roo.get(this.ctNode);
12930         ct.enableDisplayMode('block');
12931         ct.stopFx();
12932
12933         this.animating = true;
12934         this.updateExpandIcon();
12935
12936         ct.slideOut('t', {
12937             callback : function(){
12938                this.animating = false;
12939                Roo.callback(callback);
12940             },
12941             scope: this,
12942             duration: this.node.ownerTree.duration || .25
12943         });
12944     },
12945
12946     getContainer : function(){
12947         return this.ctNode;
12948     },
12949
12950     getEl : function(){
12951         return this.wrap;
12952     },
12953
12954     appendDDGhost : function(ghostNode){
12955         ghostNode.appendChild(this.elNode.cloneNode(true));
12956     },
12957
12958     getDDRepairXY : function(){
12959         return Roo.lib.Dom.getXY(this.iconNode);
12960     },
12961
12962     onRender : function(){
12963         this.render();
12964     },
12965
12966     render : function(bulkRender){
12967         var n = this.node, a = n.attributes;
12968         var targetNode = n.parentNode ?
12969               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12970
12971         if(!this.rendered){
12972             this.rendered = true;
12973
12974             this.renderElements(n, a, targetNode, bulkRender);
12975
12976             if(a.qtip){
12977                if(this.textNode.setAttributeNS){
12978                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12979                    if(a.qtipTitle){
12980                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12981                    }
12982                }else{
12983                    this.textNode.setAttribute("ext:qtip", a.qtip);
12984                    if(a.qtipTitle){
12985                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12986                    }
12987                }
12988             }else if(a.qtipCfg){
12989                 a.qtipCfg.target = Roo.id(this.textNode);
12990                 Roo.QuickTips.register(a.qtipCfg);
12991             }
12992             this.initEvents();
12993             if(!this.node.expanded){
12994                 this.updateExpandIcon();
12995             }
12996         }else{
12997             if(bulkRender === true) {
12998                 targetNode.appendChild(this.wrap);
12999             }
13000         }
13001     },
13002
13003     renderElements : function(n, a, targetNode, bulkRender)
13004     {
13005         // add some indent caching, this helps performance when rendering a large tree
13006         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13007         var t = n.getOwnerTree();
13008         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13009         if (typeof(n.attributes.html) != 'undefined') {
13010             txt = n.attributes.html;
13011         }
13012         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13013         var cb = typeof a.checked == 'boolean';
13014         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13015         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13016             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13017             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13018             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13019             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13020             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13021              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13022                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13023             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13024             "</li>"];
13025
13026         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13027             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13028                                 n.nextSibling.ui.getEl(), buf.join(""));
13029         }else{
13030             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13031         }
13032
13033         this.elNode = this.wrap.childNodes[0];
13034         this.ctNode = this.wrap.childNodes[1];
13035         var cs = this.elNode.childNodes;
13036         this.indentNode = cs[0];
13037         this.ecNode = cs[1];
13038         this.iconNode = cs[2];
13039         var index = 3;
13040         if(cb){
13041             this.checkbox = cs[3];
13042             index++;
13043         }
13044         this.anchor = cs[index];
13045         this.textNode = cs[index].firstChild;
13046     },
13047
13048     getAnchor : function(){
13049         return this.anchor;
13050     },
13051
13052     getTextEl : function(){
13053         return this.textNode;
13054     },
13055
13056     getIconEl : function(){
13057         return this.iconNode;
13058     },
13059
13060     isChecked : function(){
13061         return this.checkbox ? this.checkbox.checked : false;
13062     },
13063
13064     updateExpandIcon : function(){
13065         if(this.rendered){
13066             var n = this.node, c1, c2;
13067             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13068             var hasChild = n.hasChildNodes();
13069             if(hasChild){
13070                 if(n.expanded){
13071                     cls += "-minus";
13072                     c1 = "x-tree-node-collapsed";
13073                     c2 = "x-tree-node-expanded";
13074                 }else{
13075                     cls += "-plus";
13076                     c1 = "x-tree-node-expanded";
13077                     c2 = "x-tree-node-collapsed";
13078                 }
13079                 if(this.wasLeaf){
13080                     this.removeClass("x-tree-node-leaf");
13081                     this.wasLeaf = false;
13082                 }
13083                 if(this.c1 != c1 || this.c2 != c2){
13084                     Roo.fly(this.elNode).replaceClass(c1, c2);
13085                     this.c1 = c1; this.c2 = c2;
13086                 }
13087             }else{
13088                 // this changes non-leafs into leafs if they have no children.
13089                 // it's not very rational behaviour..
13090                 
13091                 if(!this.wasLeaf && this.node.leaf){
13092                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13093                     delete this.c1;
13094                     delete this.c2;
13095                     this.wasLeaf = true;
13096                 }
13097             }
13098             var ecc = "x-tree-ec-icon "+cls;
13099             if(this.ecc != ecc){
13100                 this.ecNode.className = ecc;
13101                 this.ecc = ecc;
13102             }
13103         }
13104     },
13105
13106     getChildIndent : function(){
13107         if(!this.childIndent){
13108             var buf = [];
13109             var p = this.node;
13110             while(p){
13111                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13112                     if(!p.isLast()) {
13113                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13114                     } else {
13115                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13116                     }
13117                 }
13118                 p = p.parentNode;
13119             }
13120             this.childIndent = buf.join("");
13121         }
13122         return this.childIndent;
13123     },
13124
13125     renderIndent : function(){
13126         if(this.rendered){
13127             var indent = "";
13128             var p = this.node.parentNode;
13129             if(p){
13130                 indent = p.ui.getChildIndent();
13131             }
13132             if(this.indentMarkup != indent){ // don't rerender if not required
13133                 this.indentNode.innerHTML = indent;
13134                 this.indentMarkup = indent;
13135             }
13136             this.updateExpandIcon();
13137         }
13138     }
13139 };
13140
13141 Roo.tree.RootTreeNodeUI = function(){
13142     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13143 };
13144 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13145     render : function(){
13146         if(!this.rendered){
13147             var targetNode = this.node.ownerTree.innerCt.dom;
13148             this.node.expanded = true;
13149             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13150             this.wrap = this.ctNode = targetNode.firstChild;
13151         }
13152     },
13153     collapse : function(){
13154     },
13155     expand : function(){
13156     }
13157 });/*
13158  * Based on:
13159  * Ext JS Library 1.1.1
13160  * Copyright(c) 2006-2007, Ext JS, LLC.
13161  *
13162  * Originally Released Under LGPL - original licence link has changed is not relivant.
13163  *
13164  * Fork - LGPL
13165  * <script type="text/javascript">
13166  */
13167 /**
13168  * @class Roo.tree.TreeLoader
13169  * @extends Roo.util.Observable
13170  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13171  * nodes from a specified URL. The response must be a javascript Array definition
13172  * who's elements are node definition objects. eg:
13173  * <pre><code>
13174 {  success : true,
13175    data :      [
13176    
13177     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13178     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13179     ]
13180 }
13181
13182
13183 </code></pre>
13184  * <br><br>
13185  * The old style respose with just an array is still supported, but not recommended.
13186  * <br><br>
13187  *
13188  * A server request is sent, and child nodes are loaded only when a node is expanded.
13189  * The loading node's id is passed to the server under the parameter name "node" to
13190  * enable the server to produce the correct child nodes.
13191  * <br><br>
13192  * To pass extra parameters, an event handler may be attached to the "beforeload"
13193  * event, and the parameters specified in the TreeLoader's baseParams property:
13194  * <pre><code>
13195     myTreeLoader.on("beforeload", function(treeLoader, node) {
13196         this.baseParams.category = node.attributes.category;
13197     }, this);
13198     
13199 </code></pre>
13200  *
13201  * This would pass an HTTP parameter called "category" to the server containing
13202  * the value of the Node's "category" attribute.
13203  * @constructor
13204  * Creates a new Treeloader.
13205  * @param {Object} config A config object containing config properties.
13206  */
13207 Roo.tree.TreeLoader = function(config){
13208     this.baseParams = {};
13209     this.requestMethod = "POST";
13210     Roo.apply(this, config);
13211
13212     this.addEvents({
13213     
13214         /**
13215          * @event beforeload
13216          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13217          * @param {Object} This TreeLoader object.
13218          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13219          * @param {Object} callback The callback function specified in the {@link #load} call.
13220          */
13221         beforeload : true,
13222         /**
13223          * @event load
13224          * Fires when the node has been successfuly loaded.
13225          * @param {Object} This TreeLoader object.
13226          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13227          * @param {Object} response The response object containing the data from the server.
13228          */
13229         load : true,
13230         /**
13231          * @event loadexception
13232          * Fires if the network request failed.
13233          * @param {Object} This TreeLoader object.
13234          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13235          * @param {Object} response The response object containing the data from the server.
13236          */
13237         loadexception : true,
13238         /**
13239          * @event create
13240          * Fires before a node is created, enabling you to return custom Node types 
13241          * @param {Object} This TreeLoader object.
13242          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13243          */
13244         create : true
13245     });
13246
13247     Roo.tree.TreeLoader.superclass.constructor.call(this);
13248 };
13249
13250 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13251     /**
13252     * @cfg {String} dataUrl The URL from which to request a Json string which
13253     * specifies an array of node definition object representing the child nodes
13254     * to be loaded.
13255     */
13256     /**
13257     * @cfg {String} requestMethod either GET or POST
13258     * defaults to POST (due to BC)
13259     * to be loaded.
13260     */
13261     /**
13262     * @cfg {Object} baseParams (optional) An object containing properties which
13263     * specify HTTP parameters to be passed to each request for child nodes.
13264     */
13265     /**
13266     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13267     * created by this loader. If the attributes sent by the server have an attribute in this object,
13268     * they take priority.
13269     */
13270     /**
13271     * @cfg {Object} uiProviders (optional) An object containing properties which
13272     * 
13273     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13274     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13275     * <i>uiProvider</i> attribute of a returned child node is a string rather
13276     * than a reference to a TreeNodeUI implementation, this that string value
13277     * is used as a property name in the uiProviders object. You can define the provider named
13278     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13279     */
13280     uiProviders : {},
13281
13282     /**
13283     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13284     * child nodes before loading.
13285     */
13286     clearOnLoad : true,
13287
13288     /**
13289     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13290     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13291     * Grid query { data : [ .....] }
13292     */
13293     
13294     root : false,
13295      /**
13296     * @cfg {String} queryParam (optional) 
13297     * Name of the query as it will be passed on the querystring (defaults to 'node')
13298     * eg. the request will be ?node=[id]
13299     */
13300     
13301     
13302     queryParam: false,
13303     
13304     /**
13305      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13306      * This is called automatically when a node is expanded, but may be used to reload
13307      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13308      * @param {Roo.tree.TreeNode} node
13309      * @param {Function} callback
13310      */
13311     load : function(node, callback){
13312         if(this.clearOnLoad){
13313             while(node.firstChild){
13314                 node.removeChild(node.firstChild);
13315             }
13316         }
13317         if(node.attributes.children){ // preloaded json children
13318             var cs = node.attributes.children;
13319             for(var i = 0, len = cs.length; i < len; i++){
13320                 node.appendChild(this.createNode(cs[i]));
13321             }
13322             if(typeof callback == "function"){
13323                 callback();
13324             }
13325         }else if(this.dataUrl){
13326             this.requestData(node, callback);
13327         }
13328     },
13329
13330     getParams: function(node){
13331         var buf = [], bp = this.baseParams;
13332         for(var key in bp){
13333             if(typeof bp[key] != "function"){
13334                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13335             }
13336         }
13337         var n = this.queryParam === false ? 'node' : this.queryParam;
13338         buf.push(n + "=", encodeURIComponent(node.id));
13339         return buf.join("");
13340     },
13341
13342     requestData : function(node, callback){
13343         if(this.fireEvent("beforeload", this, node, callback) !== false){
13344             this.transId = Roo.Ajax.request({
13345                 method:this.requestMethod,
13346                 url: this.dataUrl||this.url,
13347                 success: this.handleResponse,
13348                 failure: this.handleFailure,
13349                 scope: this,
13350                 argument: {callback: callback, node: node},
13351                 params: this.getParams(node)
13352             });
13353         }else{
13354             // if the load is cancelled, make sure we notify
13355             // the node that we are done
13356             if(typeof callback == "function"){
13357                 callback();
13358             }
13359         }
13360     },
13361
13362     isLoading : function(){
13363         return this.transId ? true : false;
13364     },
13365
13366     abort : function(){
13367         if(this.isLoading()){
13368             Roo.Ajax.abort(this.transId);
13369         }
13370     },
13371
13372     // private
13373     createNode : function(attr)
13374     {
13375         // apply baseAttrs, nice idea Corey!
13376         if(this.baseAttrs){
13377             Roo.applyIf(attr, this.baseAttrs);
13378         }
13379         if(this.applyLoader !== false){
13380             attr.loader = this;
13381         }
13382         // uiProvider = depreciated..
13383         
13384         if(typeof(attr.uiProvider) == 'string'){
13385            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13386                 /**  eval:var:attr */ eval(attr.uiProvider);
13387         }
13388         if(typeof(this.uiProviders['default']) != 'undefined') {
13389             attr.uiProvider = this.uiProviders['default'];
13390         }
13391         
13392         this.fireEvent('create', this, attr);
13393         
13394         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13395         return(attr.leaf ?
13396                         new Roo.tree.TreeNode(attr) :
13397                         new Roo.tree.AsyncTreeNode(attr));
13398     },
13399
13400     processResponse : function(response, node, callback)
13401     {
13402         var json = response.responseText;
13403         try {
13404             
13405             var o = Roo.decode(json);
13406             
13407             if (this.root === false && typeof(o.success) != undefined) {
13408                 this.root = 'data'; // the default behaviour for list like data..
13409                 }
13410                 
13411             if (this.root !== false &&  !o.success) {
13412                 // it's a failure condition.
13413                 var a = response.argument;
13414                 this.fireEvent("loadexception", this, a.node, response);
13415                 Roo.log("Load failed - should have a handler really");
13416                 return;
13417             }
13418             
13419             
13420             
13421             if (this.root !== false) {
13422                  o = o[this.root];
13423             }
13424             
13425             for(var i = 0, len = o.length; i < len; i++){
13426                 var n = this.createNode(o[i]);
13427                 if(n){
13428                     node.appendChild(n);
13429                 }
13430             }
13431             if(typeof callback == "function"){
13432                 callback(this, node);
13433             }
13434         }catch(e){
13435             this.handleFailure(response);
13436         }
13437     },
13438
13439     handleResponse : function(response){
13440         this.transId = false;
13441         var a = response.argument;
13442         this.processResponse(response, a.node, a.callback);
13443         this.fireEvent("load", this, a.node, response);
13444     },
13445
13446     handleFailure : function(response)
13447     {
13448         // should handle failure better..
13449         this.transId = false;
13450         var a = response.argument;
13451         this.fireEvent("loadexception", this, a.node, response);
13452         if(typeof a.callback == "function"){
13453             a.callback(this, a.node);
13454         }
13455     }
13456 });/*
13457  * Based on:
13458  * Ext JS Library 1.1.1
13459  * Copyright(c) 2006-2007, Ext JS, LLC.
13460  *
13461  * Originally Released Under LGPL - original licence link has changed is not relivant.
13462  *
13463  * Fork - LGPL
13464  * <script type="text/javascript">
13465  */
13466
13467 /**
13468 * @class Roo.tree.TreeFilter
13469 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13470 * @param {TreePanel} tree
13471 * @param {Object} config (optional)
13472  */
13473 Roo.tree.TreeFilter = function(tree, config){
13474     this.tree = tree;
13475     this.filtered = {};
13476     Roo.apply(this, config);
13477 };
13478
13479 Roo.tree.TreeFilter.prototype = {
13480     clearBlank:false,
13481     reverse:false,
13482     autoClear:false,
13483     remove:false,
13484
13485      /**
13486      * Filter the data by a specific attribute.
13487      * @param {String/RegExp} value Either string that the attribute value
13488      * should start with or a RegExp to test against the attribute
13489      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13490      * @param {TreeNode} startNode (optional) The node to start the filter at.
13491      */
13492     filter : function(value, attr, startNode){
13493         attr = attr || "text";
13494         var f;
13495         if(typeof value == "string"){
13496             var vlen = value.length;
13497             // auto clear empty filter
13498             if(vlen == 0 && this.clearBlank){
13499                 this.clear();
13500                 return;
13501             }
13502             value = value.toLowerCase();
13503             f = function(n){
13504                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13505             };
13506         }else if(value.exec){ // regex?
13507             f = function(n){
13508                 return value.test(n.attributes[attr]);
13509             };
13510         }else{
13511             throw 'Illegal filter type, must be string or regex';
13512         }
13513         this.filterBy(f, null, startNode);
13514         },
13515
13516     /**
13517      * Filter by a function. The passed function will be called with each
13518      * node in the tree (or from the startNode). If the function returns true, the node is kept
13519      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13520      * @param {Function} fn The filter function
13521      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13522      */
13523     filterBy : function(fn, scope, startNode){
13524         startNode = startNode || this.tree.root;
13525         if(this.autoClear){
13526             this.clear();
13527         }
13528         var af = this.filtered, rv = this.reverse;
13529         var f = function(n){
13530             if(n == startNode){
13531                 return true;
13532             }
13533             if(af[n.id]){
13534                 return false;
13535             }
13536             var m = fn.call(scope || n, n);
13537             if(!m || rv){
13538                 af[n.id] = n;
13539                 n.ui.hide();
13540                 return false;
13541             }
13542             return true;
13543         };
13544         startNode.cascade(f);
13545         if(this.remove){
13546            for(var id in af){
13547                if(typeof id != "function"){
13548                    var n = af[id];
13549                    if(n && n.parentNode){
13550                        n.parentNode.removeChild(n);
13551                    }
13552                }
13553            }
13554         }
13555     },
13556
13557     /**
13558      * Clears the current filter. Note: with the "remove" option
13559      * set a filter cannot be cleared.
13560      */
13561     clear : function(){
13562         var t = this.tree;
13563         var af = this.filtered;
13564         for(var id in af){
13565             if(typeof id != "function"){
13566                 var n = af[id];
13567                 if(n){
13568                     n.ui.show();
13569                 }
13570             }
13571         }
13572         this.filtered = {};
13573     }
13574 };
13575 /*
13576  * Based on:
13577  * Ext JS Library 1.1.1
13578  * Copyright(c) 2006-2007, Ext JS, LLC.
13579  *
13580  * Originally Released Under LGPL - original licence link has changed is not relivant.
13581  *
13582  * Fork - LGPL
13583  * <script type="text/javascript">
13584  */
13585  
13586
13587 /**
13588  * @class Roo.tree.TreeSorter
13589  * Provides sorting of nodes in a TreePanel
13590  * 
13591  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13592  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13593  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13594  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13595  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13596  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13597  * @constructor
13598  * @param {TreePanel} tree
13599  * @param {Object} config
13600  */
13601 Roo.tree.TreeSorter = function(tree, config){
13602     Roo.apply(this, config);
13603     tree.on("beforechildrenrendered", this.doSort, this);
13604     tree.on("append", this.updateSort, this);
13605     tree.on("insert", this.updateSort, this);
13606     
13607     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13608     var p = this.property || "text";
13609     var sortType = this.sortType;
13610     var fs = this.folderSort;
13611     var cs = this.caseSensitive === true;
13612     var leafAttr = this.leafAttr || 'leaf';
13613
13614     this.sortFn = function(n1, n2){
13615         if(fs){
13616             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13617                 return 1;
13618             }
13619             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13620                 return -1;
13621             }
13622         }
13623         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13624         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13625         if(v1 < v2){
13626                         return dsc ? +1 : -1;
13627                 }else if(v1 > v2){
13628                         return dsc ? -1 : +1;
13629         }else{
13630                 return 0;
13631         }
13632     };
13633 };
13634
13635 Roo.tree.TreeSorter.prototype = {
13636     doSort : function(node){
13637         node.sort(this.sortFn);
13638     },
13639     
13640     compareNodes : function(n1, n2){
13641         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13642     },
13643     
13644     updateSort : function(tree, node){
13645         if(node.childrenRendered){
13646             this.doSort.defer(1, this, [node]);
13647         }
13648     }
13649 };/*
13650  * Based on:
13651  * Ext JS Library 1.1.1
13652  * Copyright(c) 2006-2007, Ext JS, LLC.
13653  *
13654  * Originally Released Under LGPL - original licence link has changed is not relivant.
13655  *
13656  * Fork - LGPL
13657  * <script type="text/javascript">
13658  */
13659
13660 if(Roo.dd.DropZone){
13661     
13662 Roo.tree.TreeDropZone = function(tree, config){
13663     this.allowParentInsert = false;
13664     this.allowContainerDrop = false;
13665     this.appendOnly = false;
13666     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13667     this.tree = tree;
13668     this.lastInsertClass = "x-tree-no-status";
13669     this.dragOverData = {};
13670 };
13671
13672 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13673     ddGroup : "TreeDD",
13674     scroll:  true,
13675     
13676     expandDelay : 1000,
13677     
13678     expandNode : function(node){
13679         if(node.hasChildNodes() && !node.isExpanded()){
13680             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13681         }
13682     },
13683     
13684     queueExpand : function(node){
13685         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13686     },
13687     
13688     cancelExpand : function(){
13689         if(this.expandProcId){
13690             clearTimeout(this.expandProcId);
13691             this.expandProcId = false;
13692         }
13693     },
13694     
13695     isValidDropPoint : function(n, pt, dd, e, data){
13696         if(!n || !data){ return false; }
13697         var targetNode = n.node;
13698         var dropNode = data.node;
13699         // default drop rules
13700         if(!(targetNode && targetNode.isTarget && pt)){
13701             return false;
13702         }
13703         if(pt == "append" && targetNode.allowChildren === false){
13704             return false;
13705         }
13706         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13707             return false;
13708         }
13709         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13710             return false;
13711         }
13712         // reuse the object
13713         var overEvent = this.dragOverData;
13714         overEvent.tree = this.tree;
13715         overEvent.target = targetNode;
13716         overEvent.data = data;
13717         overEvent.point = pt;
13718         overEvent.source = dd;
13719         overEvent.rawEvent = e;
13720         overEvent.dropNode = dropNode;
13721         overEvent.cancel = false;  
13722         var result = this.tree.fireEvent("nodedragover", overEvent);
13723         return overEvent.cancel === false && result !== false;
13724     },
13725     
13726     getDropPoint : function(e, n, dd)
13727     {
13728         var tn = n.node;
13729         if(tn.isRoot){
13730             return tn.allowChildren !== false ? "append" : false; // always append for root
13731         }
13732         var dragEl = n.ddel;
13733         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13734         var y = Roo.lib.Event.getPageY(e);
13735         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13736         
13737         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13738         var noAppend = tn.allowChildren === false;
13739         if(this.appendOnly || tn.parentNode.allowChildren === false){
13740             return noAppend ? false : "append";
13741         }
13742         var noBelow = false;
13743         if(!this.allowParentInsert){
13744             noBelow = tn.hasChildNodes() && tn.isExpanded();
13745         }
13746         var q = (b - t) / (noAppend ? 2 : 3);
13747         if(y >= t && y < (t + q)){
13748             return "above";
13749         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13750             return "below";
13751         }else{
13752             return "append";
13753         }
13754     },
13755     
13756     onNodeEnter : function(n, dd, e, data)
13757     {
13758         this.cancelExpand();
13759     },
13760     
13761     onNodeOver : function(n, dd, e, data)
13762     {
13763        
13764         var pt = this.getDropPoint(e, n, dd);
13765         var node = n.node;
13766         
13767         // auto node expand check
13768         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13769             this.queueExpand(node);
13770         }else if(pt != "append"){
13771             this.cancelExpand();
13772         }
13773         
13774         // set the insert point style on the target node
13775         var returnCls = this.dropNotAllowed;
13776         if(this.isValidDropPoint(n, pt, dd, e, data)){
13777            if(pt){
13778                var el = n.ddel;
13779                var cls;
13780                if(pt == "above"){
13781                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13782                    cls = "x-tree-drag-insert-above";
13783                }else if(pt == "below"){
13784                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13785                    cls = "x-tree-drag-insert-below";
13786                }else{
13787                    returnCls = "x-tree-drop-ok-append";
13788                    cls = "x-tree-drag-append";
13789                }
13790                if(this.lastInsertClass != cls){
13791                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13792                    this.lastInsertClass = cls;
13793                }
13794            }
13795        }
13796        return returnCls;
13797     },
13798     
13799     onNodeOut : function(n, dd, e, data){
13800         
13801         this.cancelExpand();
13802         this.removeDropIndicators(n);
13803     },
13804     
13805     onNodeDrop : function(n, dd, e, data){
13806         var point = this.getDropPoint(e, n, dd);
13807         var targetNode = n.node;
13808         targetNode.ui.startDrop();
13809         if(!this.isValidDropPoint(n, point, dd, e, data)){
13810             targetNode.ui.endDrop();
13811             return false;
13812         }
13813         // first try to find the drop node
13814         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13815         var dropEvent = {
13816             tree : this.tree,
13817             target: targetNode,
13818             data: data,
13819             point: point,
13820             source: dd,
13821             rawEvent: e,
13822             dropNode: dropNode,
13823             cancel: !dropNode   
13824         };
13825         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13826         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13827             targetNode.ui.endDrop();
13828             return false;
13829         }
13830         // allow target changing
13831         targetNode = dropEvent.target;
13832         if(point == "append" && !targetNode.isExpanded()){
13833             targetNode.expand(false, null, function(){
13834                 this.completeDrop(dropEvent);
13835             }.createDelegate(this));
13836         }else{
13837             this.completeDrop(dropEvent);
13838         }
13839         return true;
13840     },
13841     
13842     completeDrop : function(de){
13843         var ns = de.dropNode, p = de.point, t = de.target;
13844         if(!(ns instanceof Array)){
13845             ns = [ns];
13846         }
13847         var n;
13848         for(var i = 0, len = ns.length; i < len; i++){
13849             n = ns[i];
13850             if(p == "above"){
13851                 t.parentNode.insertBefore(n, t);
13852             }else if(p == "below"){
13853                 t.parentNode.insertBefore(n, t.nextSibling);
13854             }else{
13855                 t.appendChild(n);
13856             }
13857         }
13858         n.ui.focus();
13859         if(this.tree.hlDrop){
13860             n.ui.highlight();
13861         }
13862         t.ui.endDrop();
13863         this.tree.fireEvent("nodedrop", de);
13864     },
13865     
13866     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13867         if(this.tree.hlDrop){
13868             dropNode.ui.focus();
13869             dropNode.ui.highlight();
13870         }
13871         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13872     },
13873     
13874     getTree : function(){
13875         return this.tree;
13876     },
13877     
13878     removeDropIndicators : function(n){
13879         if(n && n.ddel){
13880             var el = n.ddel;
13881             Roo.fly(el).removeClass([
13882                     "x-tree-drag-insert-above",
13883                     "x-tree-drag-insert-below",
13884                     "x-tree-drag-append"]);
13885             this.lastInsertClass = "_noclass";
13886         }
13887     },
13888     
13889     beforeDragDrop : function(target, e, id){
13890         this.cancelExpand();
13891         return true;
13892     },
13893     
13894     afterRepair : function(data){
13895         if(data && Roo.enableFx){
13896             data.node.ui.highlight();
13897         }
13898         this.hideProxy();
13899     } 
13900     
13901 });
13902
13903 }
13904 /*
13905  * Based on:
13906  * Ext JS Library 1.1.1
13907  * Copyright(c) 2006-2007, Ext JS, LLC.
13908  *
13909  * Originally Released Under LGPL - original licence link has changed is not relivant.
13910  *
13911  * Fork - LGPL
13912  * <script type="text/javascript">
13913  */
13914  
13915
13916 if(Roo.dd.DragZone){
13917 Roo.tree.TreeDragZone = function(tree, config){
13918     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13919     this.tree = tree;
13920 };
13921
13922 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13923     ddGroup : "TreeDD",
13924    
13925     onBeforeDrag : function(data, e){
13926         var n = data.node;
13927         return n && n.draggable && !n.disabled;
13928     },
13929      
13930     
13931     onInitDrag : function(e){
13932         var data = this.dragData;
13933         this.tree.getSelectionModel().select(data.node);
13934         this.proxy.update("");
13935         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13936         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13937     },
13938     
13939     getRepairXY : function(e, data){
13940         return data.node.ui.getDDRepairXY();
13941     },
13942     
13943     onEndDrag : function(data, e){
13944         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13945         
13946         
13947     },
13948     
13949     onValidDrop : function(dd, e, id){
13950         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13951         this.hideProxy();
13952     },
13953     
13954     beforeInvalidDrop : function(e, id){
13955         // this scrolls the original position back into view
13956         var sm = this.tree.getSelectionModel();
13957         sm.clearSelections();
13958         sm.select(this.dragData.node);
13959     }
13960 });
13961 }/*
13962  * Based on:
13963  * Ext JS Library 1.1.1
13964  * Copyright(c) 2006-2007, Ext JS, LLC.
13965  *
13966  * Originally Released Under LGPL - original licence link has changed is not relivant.
13967  *
13968  * Fork - LGPL
13969  * <script type="text/javascript">
13970  */
13971 /**
13972  * @class Roo.tree.TreeEditor
13973  * @extends Roo.Editor
13974  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13975  * as the editor field.
13976  * @constructor
13977  * @param {Object} config (used to be the tree panel.)
13978  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13979  * 
13980  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13981  * @cfg {Roo.form.TextField} field [required] The field configuration
13982  *
13983  * 
13984  */
13985 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13986     var tree = config;
13987     var field;
13988     if (oldconfig) { // old style..
13989         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13990     } else {
13991         // new style..
13992         tree = config.tree;
13993         config.field = config.field  || {};
13994         config.field.xtype = 'TextField';
13995         field = Roo.factory(config.field, Roo.form);
13996     }
13997     config = config || {};
13998     
13999     
14000     this.addEvents({
14001         /**
14002          * @event beforenodeedit
14003          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14004          * false from the handler of this event.
14005          * @param {Editor} this
14006          * @param {Roo.tree.Node} node 
14007          */
14008         "beforenodeedit" : true
14009     });
14010     
14011     //Roo.log(config);
14012     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14013
14014     this.tree = tree;
14015
14016     tree.on('beforeclick', this.beforeNodeClick, this);
14017     tree.getTreeEl().on('mousedown', this.hide, this);
14018     this.on('complete', this.updateNode, this);
14019     this.on('beforestartedit', this.fitToTree, this);
14020     this.on('startedit', this.bindScroll, this, {delay:10});
14021     this.on('specialkey', this.onSpecialKey, this);
14022 };
14023
14024 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14025     /**
14026      * @cfg {String} alignment
14027      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14028      */
14029     alignment: "l-l",
14030     // inherit
14031     autoSize: false,
14032     /**
14033      * @cfg {Boolean} hideEl
14034      * True to hide the bound element while the editor is displayed (defaults to false)
14035      */
14036     hideEl : false,
14037     /**
14038      * @cfg {String} cls
14039      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14040      */
14041     cls: "x-small-editor x-tree-editor",
14042     /**
14043      * @cfg {Boolean} shim
14044      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14045      */
14046     shim:false,
14047     // inherit
14048     shadow:"frame",
14049     /**
14050      * @cfg {Number} maxWidth
14051      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14052      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14053      * scroll and client offsets into account prior to each edit.
14054      */
14055     maxWidth: 250,
14056
14057     editDelay : 350,
14058
14059     // private
14060     fitToTree : function(ed, el){
14061         var td = this.tree.getTreeEl().dom, nd = el.dom;
14062         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14063             td.scrollLeft = nd.offsetLeft;
14064         }
14065         var w = Math.min(
14066                 this.maxWidth,
14067                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14068         this.setSize(w, '');
14069         
14070         return this.fireEvent('beforenodeedit', this, this.editNode);
14071         
14072     },
14073
14074     // private
14075     triggerEdit : function(node){
14076         this.completeEdit();
14077         this.editNode = node;
14078         this.startEdit(node.ui.textNode, node.text);
14079     },
14080
14081     // private
14082     bindScroll : function(){
14083         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14084     },
14085
14086     // private
14087     beforeNodeClick : function(node, e){
14088         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14089         this.lastClick = new Date();
14090         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14091             e.stopEvent();
14092             this.triggerEdit(node);
14093             return false;
14094         }
14095         return true;
14096     },
14097
14098     // private
14099     updateNode : function(ed, value){
14100         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14101         this.editNode.setText(value);
14102     },
14103
14104     // private
14105     onHide : function(){
14106         Roo.tree.TreeEditor.superclass.onHide.call(this);
14107         if(this.editNode){
14108             this.editNode.ui.focus();
14109         }
14110     },
14111
14112     // private
14113     onSpecialKey : function(field, e){
14114         var k = e.getKey();
14115         if(k == e.ESC){
14116             e.stopEvent();
14117             this.cancelEdit();
14118         }else if(k == e.ENTER && !e.hasModifier()){
14119             e.stopEvent();
14120             this.completeEdit();
14121         }
14122     }
14123 });//<Script type="text/javascript">
14124 /*
14125  * Based on:
14126  * Ext JS Library 1.1.1
14127  * Copyright(c) 2006-2007, Ext JS, LLC.
14128  *
14129  * Originally Released Under LGPL - original licence link has changed is not relivant.
14130  *
14131  * Fork - LGPL
14132  * <script type="text/javascript">
14133  */
14134  
14135 /**
14136  * Not documented??? - probably should be...
14137  */
14138
14139 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14140     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14141     
14142     renderElements : function(n, a, targetNode, bulkRender){
14143         //consel.log("renderElements?");
14144         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14145
14146         var t = n.getOwnerTree();
14147         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14148         
14149         var cols = t.columns;
14150         var bw = t.borderWidth;
14151         var c = cols[0];
14152         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14153          var cb = typeof a.checked == "boolean";
14154         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14155         var colcls = 'x-t-' + tid + '-c0';
14156         var buf = [
14157             '<li class="x-tree-node">',
14158             
14159                 
14160                 '<div class="x-tree-node-el ', a.cls,'">',
14161                     // extran...
14162                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14163                 
14164                 
14165                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14166                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14167                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14168                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14169                            (a.iconCls ? ' '+a.iconCls : ''),
14170                            '" unselectable="on" />',
14171                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14172                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14173                              
14174                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14175                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14176                             '<span unselectable="on" qtip="' + tx + '">',
14177                              tx,
14178                              '</span></a>' ,
14179                     '</div>',
14180                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14181                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14182                  ];
14183         for(var i = 1, len = cols.length; i < len; i++){
14184             c = cols[i];
14185             colcls = 'x-t-' + tid + '-c' +i;
14186             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14187             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14188                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14189                       "</div>");
14190          }
14191          
14192          buf.push(
14193             '</a>',
14194             '<div class="x-clear"></div></div>',
14195             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14196             "</li>");
14197         
14198         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14199             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14200                                 n.nextSibling.ui.getEl(), buf.join(""));
14201         }else{
14202             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14203         }
14204         var el = this.wrap.firstChild;
14205         this.elRow = el;
14206         this.elNode = el.firstChild;
14207         this.ranchor = el.childNodes[1];
14208         this.ctNode = this.wrap.childNodes[1];
14209         var cs = el.firstChild.childNodes;
14210         this.indentNode = cs[0];
14211         this.ecNode = cs[1];
14212         this.iconNode = cs[2];
14213         var index = 3;
14214         if(cb){
14215             this.checkbox = cs[3];
14216             index++;
14217         }
14218         this.anchor = cs[index];
14219         
14220         this.textNode = cs[index].firstChild;
14221         
14222         //el.on("click", this.onClick, this);
14223         //el.on("dblclick", this.onDblClick, this);
14224         
14225         
14226        // console.log(this);
14227     },
14228     initEvents : function(){
14229         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14230         
14231             
14232         var a = this.ranchor;
14233
14234         var el = Roo.get(a);
14235
14236         if(Roo.isOpera){ // opera render bug ignores the CSS
14237             el.setStyle("text-decoration", "none");
14238         }
14239
14240         el.on("click", this.onClick, this);
14241         el.on("dblclick", this.onDblClick, this);
14242         el.on("contextmenu", this.onContextMenu, this);
14243         
14244     },
14245     
14246     /*onSelectedChange : function(state){
14247         if(state){
14248             this.focus();
14249             this.addClass("x-tree-selected");
14250         }else{
14251             //this.blur();
14252             this.removeClass("x-tree-selected");
14253         }
14254     },*/
14255     addClass : function(cls){
14256         if(this.elRow){
14257             Roo.fly(this.elRow).addClass(cls);
14258         }
14259         
14260     },
14261     
14262     
14263     removeClass : function(cls){
14264         if(this.elRow){
14265             Roo.fly(this.elRow).removeClass(cls);
14266         }
14267     }
14268
14269     
14270     
14271 });//<Script type="text/javascript">
14272
14273 /*
14274  * Based on:
14275  * Ext JS Library 1.1.1
14276  * Copyright(c) 2006-2007, Ext JS, LLC.
14277  *
14278  * Originally Released Under LGPL - original licence link has changed is not relivant.
14279  *
14280  * Fork - LGPL
14281  * <script type="text/javascript">
14282  */
14283  
14284
14285 /**
14286  * @class Roo.tree.ColumnTree
14287  * @extends Roo.data.TreePanel
14288  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14289  * @cfg {int} borderWidth  compined right/left border allowance
14290  * @constructor
14291  * @param {String/HTMLElement/Element} el The container element
14292  * @param {Object} config
14293  */
14294 Roo.tree.ColumnTree =  function(el, config)
14295 {
14296    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14297    this.addEvents({
14298         /**
14299         * @event resize
14300         * Fire this event on a container when it resizes
14301         * @param {int} w Width
14302         * @param {int} h Height
14303         */
14304        "resize" : true
14305     });
14306     this.on('resize', this.onResize, this);
14307 };
14308
14309 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14310     //lines:false,
14311     
14312     
14313     borderWidth: Roo.isBorderBox ? 0 : 2, 
14314     headEls : false,
14315     
14316     render : function(){
14317         // add the header.....
14318        
14319         Roo.tree.ColumnTree.superclass.render.apply(this);
14320         
14321         this.el.addClass('x-column-tree');
14322         
14323         this.headers = this.el.createChild(
14324             {cls:'x-tree-headers'},this.innerCt.dom);
14325    
14326         var cols = this.columns, c;
14327         var totalWidth = 0;
14328         this.headEls = [];
14329         var  len = cols.length;
14330         for(var i = 0; i < len; i++){
14331              c = cols[i];
14332              totalWidth += c.width;
14333             this.headEls.push(this.headers.createChild({
14334                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14335                  cn: {
14336                      cls:'x-tree-hd-text',
14337                      html: c.header
14338                  },
14339                  style:'width:'+(c.width-this.borderWidth)+'px;'
14340              }));
14341         }
14342         this.headers.createChild({cls:'x-clear'});
14343         // prevent floats from wrapping when clipped
14344         this.headers.setWidth(totalWidth);
14345         //this.innerCt.setWidth(totalWidth);
14346         this.innerCt.setStyle({ overflow: 'auto' });
14347         this.onResize(this.width, this.height);
14348              
14349         
14350     },
14351     onResize : function(w,h)
14352     {
14353         this.height = h;
14354         this.width = w;
14355         // resize cols..
14356         this.innerCt.setWidth(this.width);
14357         this.innerCt.setHeight(this.height-20);
14358         
14359         // headers...
14360         var cols = this.columns, c;
14361         var totalWidth = 0;
14362         var expEl = false;
14363         var len = cols.length;
14364         for(var i = 0; i < len; i++){
14365             c = cols[i];
14366             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14367                 // it's the expander..
14368                 expEl  = this.headEls[i];
14369                 continue;
14370             }
14371             totalWidth += c.width;
14372             
14373         }
14374         if (expEl) {
14375             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14376         }
14377         this.headers.setWidth(w-20);
14378
14379         
14380         
14381         
14382     }
14383 });
14384 /*
14385  * Based on:
14386  * Ext JS Library 1.1.1
14387  * Copyright(c) 2006-2007, Ext JS, LLC.
14388  *
14389  * Originally Released Under LGPL - original licence link has changed is not relivant.
14390  *
14391  * Fork - LGPL
14392  * <script type="text/javascript">
14393  */
14394  
14395 /**
14396  * @class Roo.menu.Menu
14397  * @extends Roo.util.Observable
14398  * @children Roo.menu.BaseItem
14399  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14400  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14401  * @constructor
14402  * Creates a new Menu
14403  * @param {Object} config Configuration options
14404  */
14405 Roo.menu.Menu = function(config){
14406     
14407     Roo.menu.Menu.superclass.constructor.call(this, config);
14408     
14409     this.id = this.id || Roo.id();
14410     this.addEvents({
14411         /**
14412          * @event beforeshow
14413          * Fires before this menu is displayed
14414          * @param {Roo.menu.Menu} this
14415          */
14416         beforeshow : true,
14417         /**
14418          * @event beforehide
14419          * Fires before this menu is hidden
14420          * @param {Roo.menu.Menu} this
14421          */
14422         beforehide : true,
14423         /**
14424          * @event show
14425          * Fires after this menu is displayed
14426          * @param {Roo.menu.Menu} this
14427          */
14428         show : true,
14429         /**
14430          * @event hide
14431          * Fires after this menu is hidden
14432          * @param {Roo.menu.Menu} this
14433          */
14434         hide : true,
14435         /**
14436          * @event click
14437          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14438          * @param {Roo.menu.Menu} this
14439          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14440          * @param {Roo.EventObject} e
14441          */
14442         click : true,
14443         /**
14444          * @event mouseover
14445          * Fires when the mouse is hovering over this menu
14446          * @param {Roo.menu.Menu} this
14447          * @param {Roo.EventObject} e
14448          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14449          */
14450         mouseover : true,
14451         /**
14452          * @event mouseout
14453          * Fires when the mouse exits this menu
14454          * @param {Roo.menu.Menu} this
14455          * @param {Roo.EventObject} e
14456          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14457          */
14458         mouseout : true,
14459         /**
14460          * @event itemclick
14461          * Fires when a menu item contained in this menu is clicked
14462          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14463          * @param {Roo.EventObject} e
14464          */
14465         itemclick: true
14466     });
14467     if (this.registerMenu) {
14468         Roo.menu.MenuMgr.register(this);
14469     }
14470     
14471     var mis = this.items;
14472     this.items = new Roo.util.MixedCollection();
14473     if(mis){
14474         this.add.apply(this, mis);
14475     }
14476 };
14477
14478 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14479     /**
14480      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14481      */
14482     minWidth : 120,
14483     /**
14484      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14485      * for bottom-right shadow (defaults to "sides")
14486      */
14487     shadow : "sides",
14488     /**
14489      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14490      * this menu (defaults to "tl-tr?")
14491      */
14492     subMenuAlign : "tl-tr?",
14493     /**
14494      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14495      * relative to its element of origin (defaults to "tl-bl?")
14496      */
14497     defaultAlign : "tl-bl?",
14498     /**
14499      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14500      */
14501     allowOtherMenus : false,
14502     /**
14503      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14504      */
14505     registerMenu : true,
14506
14507     hidden:true,
14508
14509     // private
14510     render : function(){
14511         if(this.el){
14512             return;
14513         }
14514         var el = this.el = new Roo.Layer({
14515             cls: "x-menu",
14516             shadow:this.shadow,
14517             constrain: false,
14518             parentEl: this.parentEl || document.body,
14519             zindex:15000
14520         });
14521
14522         this.keyNav = new Roo.menu.MenuNav(this);
14523
14524         if(this.plain){
14525             el.addClass("x-menu-plain");
14526         }
14527         if(this.cls){
14528             el.addClass(this.cls);
14529         }
14530         // generic focus element
14531         this.focusEl = el.createChild({
14532             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14533         });
14534         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14535         //disabling touch- as it's causing issues ..
14536         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14537         ul.on('click'   , this.onClick, this);
14538         
14539         
14540         ul.on("mouseover", this.onMouseOver, this);
14541         ul.on("mouseout", this.onMouseOut, this);
14542         this.items.each(function(item){
14543             if (item.hidden) {
14544                 return;
14545             }
14546             
14547             var li = document.createElement("li");
14548             li.className = "x-menu-list-item";
14549             ul.dom.appendChild(li);
14550             item.render(li, this);
14551         }, this);
14552         this.ul = ul;
14553         this.autoWidth();
14554     },
14555
14556     // private
14557     autoWidth : function(){
14558         var el = this.el, ul = this.ul;
14559         if(!el){
14560             return;
14561         }
14562         var w = this.width;
14563         if(w){
14564             el.setWidth(w);
14565         }else if(Roo.isIE){
14566             el.setWidth(this.minWidth);
14567             var t = el.dom.offsetWidth; // force recalc
14568             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14569         }
14570     },
14571
14572     // private
14573     delayAutoWidth : function(){
14574         if(this.rendered){
14575             if(!this.awTask){
14576                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14577             }
14578             this.awTask.delay(20);
14579         }
14580     },
14581
14582     // private
14583     findTargetItem : function(e){
14584         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14585         if(t && t.menuItemId){
14586             return this.items.get(t.menuItemId);
14587         }
14588     },
14589
14590     // private
14591     onClick : function(e){
14592         Roo.log("menu.onClick");
14593         var t = this.findTargetItem(e);
14594         if(!t){
14595             return;
14596         }
14597         Roo.log(e);
14598         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14599             if(t == this.activeItem && t.shouldDeactivate(e)){
14600                 this.activeItem.deactivate();
14601                 delete this.activeItem;
14602                 return;
14603             }
14604             if(t.canActivate){
14605                 this.setActiveItem(t, true);
14606             }
14607             return;
14608             
14609             
14610         }
14611         
14612         t.onClick(e);
14613         this.fireEvent("click", this, t, e);
14614     },
14615
14616     // private
14617     setActiveItem : function(item, autoExpand){
14618         if(item != this.activeItem){
14619             if(this.activeItem){
14620                 this.activeItem.deactivate();
14621             }
14622             this.activeItem = item;
14623             item.activate(autoExpand);
14624         }else if(autoExpand){
14625             item.expandMenu();
14626         }
14627     },
14628
14629     // private
14630     tryActivate : function(start, step){
14631         var items = this.items;
14632         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14633             var item = items.get(i);
14634             if(!item.disabled && item.canActivate){
14635                 this.setActiveItem(item, false);
14636                 return item;
14637             }
14638         }
14639         return false;
14640     },
14641
14642     // private
14643     onMouseOver : function(e){
14644         var t;
14645         if(t = this.findTargetItem(e)){
14646             if(t.canActivate && !t.disabled){
14647                 this.setActiveItem(t, true);
14648             }
14649         }
14650         this.fireEvent("mouseover", this, e, t);
14651     },
14652
14653     // private
14654     onMouseOut : function(e){
14655         var t;
14656         if(t = this.findTargetItem(e)){
14657             if(t == this.activeItem && t.shouldDeactivate(e)){
14658                 this.activeItem.deactivate();
14659                 delete this.activeItem;
14660             }
14661         }
14662         this.fireEvent("mouseout", this, e, t);
14663     },
14664
14665     /**
14666      * Read-only.  Returns true if the menu is currently displayed, else false.
14667      * @type Boolean
14668      */
14669     isVisible : function(){
14670         return this.el && !this.hidden;
14671     },
14672
14673     /**
14674      * Displays this menu relative to another element
14675      * @param {String/HTMLElement/Roo.Element} element The element to align to
14676      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14677      * the element (defaults to this.defaultAlign)
14678      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14679      */
14680     show : function(el, pos, parentMenu){
14681         this.parentMenu = parentMenu;
14682         if(!this.el){
14683             this.render();
14684         }
14685         this.fireEvent("beforeshow", this);
14686         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14687     },
14688
14689     /**
14690      * Displays this menu at a specific xy position
14691      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14692      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14693      */
14694     showAt : function(xy, parentMenu, /* private: */_e){
14695         this.parentMenu = parentMenu;
14696         if(!this.el){
14697             this.render();
14698         }
14699         if(_e !== false){
14700             this.fireEvent("beforeshow", this);
14701             xy = this.el.adjustForConstraints(xy);
14702         }
14703         this.el.setXY(xy);
14704         this.el.show();
14705         this.hidden = false;
14706         this.focus();
14707         this.fireEvent("show", this);
14708     },
14709
14710     focus : function(){
14711         if(!this.hidden){
14712             this.doFocus.defer(50, this);
14713         }
14714     },
14715
14716     doFocus : function(){
14717         if(!this.hidden){
14718             this.focusEl.focus();
14719         }
14720     },
14721
14722     /**
14723      * Hides this menu and optionally all parent menus
14724      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14725      */
14726     hide : function(deep){
14727         if(this.el && this.isVisible()){
14728             this.fireEvent("beforehide", this);
14729             if(this.activeItem){
14730                 this.activeItem.deactivate();
14731                 this.activeItem = null;
14732             }
14733             this.el.hide();
14734             this.hidden = true;
14735             this.fireEvent("hide", this);
14736         }
14737         if(deep === true && this.parentMenu){
14738             this.parentMenu.hide(true);
14739         }
14740     },
14741
14742     /**
14743      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14744      * Any of the following are valid:
14745      * <ul>
14746      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14747      * <li>An HTMLElement object which will be converted to a menu item</li>
14748      * <li>A menu item config object that will be created as a new menu item</li>
14749      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14750      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14751      * </ul>
14752      * Usage:
14753      * <pre><code>
14754 // Create the menu
14755 var menu = new Roo.menu.Menu();
14756
14757 // Create a menu item to add by reference
14758 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14759
14760 // Add a bunch of items at once using different methods.
14761 // Only the last item added will be returned.
14762 var item = menu.add(
14763     menuItem,                // add existing item by ref
14764     'Dynamic Item',          // new TextItem
14765     '-',                     // new separator
14766     { text: 'Config Item' }  // new item by config
14767 );
14768 </code></pre>
14769      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14770      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14771      */
14772     add : function(){
14773         var a = arguments, l = a.length, item;
14774         for(var i = 0; i < l; i++){
14775             var el = a[i];
14776             if ((typeof(el) == "object") && el.xtype && el.xns) {
14777                 el = Roo.factory(el, Roo.menu);
14778             }
14779             
14780             if(el.render){ // some kind of Item
14781                 item = this.addItem(el);
14782             }else if(typeof el == "string"){ // string
14783                 if(el == "separator" || el == "-"){
14784                     item = this.addSeparator();
14785                 }else{
14786                     item = this.addText(el);
14787                 }
14788             }else if(el.tagName || el.el){ // element
14789                 item = this.addElement(el);
14790             }else if(typeof el == "object"){ // must be menu item config?
14791                 item = this.addMenuItem(el);
14792             }
14793         }
14794         return item;
14795     },
14796
14797     /**
14798      * Returns this menu's underlying {@link Roo.Element} object
14799      * @return {Roo.Element} The element
14800      */
14801     getEl : function(){
14802         if(!this.el){
14803             this.render();
14804         }
14805         return this.el;
14806     },
14807
14808     /**
14809      * Adds a separator bar to the menu
14810      * @return {Roo.menu.Item} The menu item that was added
14811      */
14812     addSeparator : function(){
14813         return this.addItem(new Roo.menu.Separator());
14814     },
14815
14816     /**
14817      * Adds an {@link Roo.Element} object to the menu
14818      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14819      * @return {Roo.menu.Item} The menu item that was added
14820      */
14821     addElement : function(el){
14822         return this.addItem(new Roo.menu.BaseItem(el));
14823     },
14824
14825     /**
14826      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14827      * @param {Roo.menu.Item} item The menu item to add
14828      * @return {Roo.menu.Item} The menu item that was added
14829      */
14830     addItem : function(item){
14831         this.items.add(item);
14832         if(this.ul){
14833             var li = document.createElement("li");
14834             li.className = "x-menu-list-item";
14835             this.ul.dom.appendChild(li);
14836             item.render(li, this);
14837             this.delayAutoWidth();
14838         }
14839         return item;
14840     },
14841
14842     /**
14843      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14844      * @param {Object} config A MenuItem config object
14845      * @return {Roo.menu.Item} The menu item that was added
14846      */
14847     addMenuItem : function(config){
14848         if(!(config instanceof Roo.menu.Item)){
14849             if(typeof config.checked == "boolean"){ // must be check menu item config?
14850                 config = new Roo.menu.CheckItem(config);
14851             }else{
14852                 config = new Roo.menu.Item(config);
14853             }
14854         }
14855         return this.addItem(config);
14856     },
14857
14858     /**
14859      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14860      * @param {String} text The text to display in the menu item
14861      * @return {Roo.menu.Item} The menu item that was added
14862      */
14863     addText : function(text){
14864         return this.addItem(new Roo.menu.TextItem({ text : text }));
14865     },
14866
14867     /**
14868      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14869      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14870      * @param {Roo.menu.Item} item The menu item to add
14871      * @return {Roo.menu.Item} The menu item that was added
14872      */
14873     insert : function(index, item){
14874         this.items.insert(index, item);
14875         if(this.ul){
14876             var li = document.createElement("li");
14877             li.className = "x-menu-list-item";
14878             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14879             item.render(li, this);
14880             this.delayAutoWidth();
14881         }
14882         return item;
14883     },
14884
14885     /**
14886      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14887      * @param {Roo.menu.Item} item The menu item to remove
14888      */
14889     remove : function(item){
14890         this.items.removeKey(item.id);
14891         item.destroy();
14892     },
14893
14894     /**
14895      * Removes and destroys all items in the menu
14896      */
14897     removeAll : function(){
14898         var f;
14899         while(f = this.items.first()){
14900             this.remove(f);
14901         }
14902     }
14903 });
14904
14905 // MenuNav is a private utility class used internally by the Menu
14906 Roo.menu.MenuNav = function(menu){
14907     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14908     this.scope = this.menu = menu;
14909 };
14910
14911 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14912     doRelay : function(e, h){
14913         var k = e.getKey();
14914         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14915             this.menu.tryActivate(0, 1);
14916             return false;
14917         }
14918         return h.call(this.scope || this, e, this.menu);
14919     },
14920
14921     up : function(e, m){
14922         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14923             m.tryActivate(m.items.length-1, -1);
14924         }
14925     },
14926
14927     down : function(e, m){
14928         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14929             m.tryActivate(0, 1);
14930         }
14931     },
14932
14933     right : function(e, m){
14934         if(m.activeItem){
14935             m.activeItem.expandMenu(true);
14936         }
14937     },
14938
14939     left : function(e, m){
14940         m.hide();
14941         if(m.parentMenu && m.parentMenu.activeItem){
14942             m.parentMenu.activeItem.activate();
14943         }
14944     },
14945
14946     enter : function(e, m){
14947         if(m.activeItem){
14948             e.stopPropagation();
14949             m.activeItem.onClick(e);
14950             m.fireEvent("click", this, m.activeItem);
14951             return true;
14952         }
14953     }
14954 });/*
14955  * Based on:
14956  * Ext JS Library 1.1.1
14957  * Copyright(c) 2006-2007, Ext JS, LLC.
14958  *
14959  * Originally Released Under LGPL - original licence link has changed is not relivant.
14960  *
14961  * Fork - LGPL
14962  * <script type="text/javascript">
14963  */
14964  
14965 /**
14966  * @class Roo.menu.MenuMgr
14967  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14968  * @singleton
14969  */
14970 Roo.menu.MenuMgr = function(){
14971    var menus, active, groups = {}, attached = false, lastShow = new Date();
14972
14973    // private - called when first menu is created
14974    function init(){
14975        menus = {};
14976        active = new Roo.util.MixedCollection();
14977        Roo.get(document).addKeyListener(27, function(){
14978            if(active.length > 0){
14979                hideAll();
14980            }
14981        });
14982    }
14983
14984    // private
14985    function hideAll(){
14986        if(active && active.length > 0){
14987            var c = active.clone();
14988            c.each(function(m){
14989                m.hide();
14990            });
14991        }
14992    }
14993
14994    // private
14995    function onHide(m){
14996        active.remove(m);
14997        if(active.length < 1){
14998            Roo.get(document).un("mousedown", onMouseDown);
14999            attached = false;
15000        }
15001    }
15002
15003    // private
15004    function onShow(m){
15005        var last = active.last();
15006        lastShow = new Date();
15007        active.add(m);
15008        if(!attached){
15009            Roo.get(document).on("mousedown", onMouseDown);
15010            attached = true;
15011        }
15012        if(m.parentMenu){
15013           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15014           m.parentMenu.activeChild = m;
15015        }else if(last && last.isVisible()){
15016           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15017        }
15018    }
15019
15020    // private
15021    function onBeforeHide(m){
15022        if(m.activeChild){
15023            m.activeChild.hide();
15024        }
15025        if(m.autoHideTimer){
15026            clearTimeout(m.autoHideTimer);
15027            delete m.autoHideTimer;
15028        }
15029    }
15030
15031    // private
15032    function onBeforeShow(m){
15033        var pm = m.parentMenu;
15034        if(!pm && !m.allowOtherMenus){
15035            hideAll();
15036        }else if(pm && pm.activeChild && active != m){
15037            pm.activeChild.hide();
15038        }
15039    }
15040
15041    // private
15042    function onMouseDown(e){
15043        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15044            hideAll();
15045        }
15046    }
15047
15048    // private
15049    function onBeforeCheck(mi, state){
15050        if(state){
15051            var g = groups[mi.group];
15052            for(var i = 0, l = g.length; i < l; i++){
15053                if(g[i] != mi){
15054                    g[i].setChecked(false);
15055                }
15056            }
15057        }
15058    }
15059
15060    return {
15061
15062        /**
15063         * Hides all menus that are currently visible
15064         */
15065        hideAll : function(){
15066             hideAll();  
15067        },
15068
15069        // private
15070        register : function(menu){
15071            if(!menus){
15072                init();
15073            }
15074            menus[menu.id] = menu;
15075            menu.on("beforehide", onBeforeHide);
15076            menu.on("hide", onHide);
15077            menu.on("beforeshow", onBeforeShow);
15078            menu.on("show", onShow);
15079            var g = menu.group;
15080            if(g && menu.events["checkchange"]){
15081                if(!groups[g]){
15082                    groups[g] = [];
15083                }
15084                groups[g].push(menu);
15085                menu.on("checkchange", onCheck);
15086            }
15087        },
15088
15089         /**
15090          * Returns a {@link Roo.menu.Menu} object
15091          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15092          * be used to generate and return a new Menu instance.
15093          */
15094        get : function(menu){
15095            if(typeof menu == "string"){ // menu id
15096                return menus[menu];
15097            }else if(menu.events){  // menu instance
15098                return menu;
15099            }else if(typeof menu.length == 'number'){ // array of menu items?
15100                return new Roo.menu.Menu({items:menu});
15101            }else{ // otherwise, must be a config
15102                return new Roo.menu.Menu(menu);
15103            }
15104        },
15105
15106        // private
15107        unregister : function(menu){
15108            delete menus[menu.id];
15109            menu.un("beforehide", onBeforeHide);
15110            menu.un("hide", onHide);
15111            menu.un("beforeshow", onBeforeShow);
15112            menu.un("show", onShow);
15113            var g = menu.group;
15114            if(g && menu.events["checkchange"]){
15115                groups[g].remove(menu);
15116                menu.un("checkchange", onCheck);
15117            }
15118        },
15119
15120        // private
15121        registerCheckable : function(menuItem){
15122            var g = menuItem.group;
15123            if(g){
15124                if(!groups[g]){
15125                    groups[g] = [];
15126                }
15127                groups[g].push(menuItem);
15128                menuItem.on("beforecheckchange", onBeforeCheck);
15129            }
15130        },
15131
15132        // private
15133        unregisterCheckable : function(menuItem){
15134            var g = menuItem.group;
15135            if(g){
15136                groups[g].remove(menuItem);
15137                menuItem.un("beforecheckchange", onBeforeCheck);
15138            }
15139        }
15140    };
15141 }();/*
15142  * Based on:
15143  * Ext JS Library 1.1.1
15144  * Copyright(c) 2006-2007, Ext JS, LLC.
15145  *
15146  * Originally Released Under LGPL - original licence link has changed is not relivant.
15147  *
15148  * Fork - LGPL
15149  * <script type="text/javascript">
15150  */
15151  
15152
15153 /**
15154  * @class Roo.menu.BaseItem
15155  * @extends Roo.Component
15156  * @abstract
15157  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15158  * management and base configuration options shared by all menu components.
15159  * @constructor
15160  * Creates a new BaseItem
15161  * @param {Object} config Configuration options
15162  */
15163 Roo.menu.BaseItem = function(config){
15164     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15165
15166     this.addEvents({
15167         /**
15168          * @event click
15169          * Fires when this item is clicked
15170          * @param {Roo.menu.BaseItem} this
15171          * @param {Roo.EventObject} e
15172          */
15173         click: true,
15174         /**
15175          * @event activate
15176          * Fires when this item is activated
15177          * @param {Roo.menu.BaseItem} this
15178          */
15179         activate : true,
15180         /**
15181          * @event deactivate
15182          * Fires when this item is deactivated
15183          * @param {Roo.menu.BaseItem} this
15184          */
15185         deactivate : true
15186     });
15187
15188     if(this.handler){
15189         this.on("click", this.handler, this.scope, true);
15190     }
15191 };
15192
15193 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15194     /**
15195      * @cfg {Function} handler
15196      * A function that will handle the click event of this menu item (defaults to undefined)
15197      */
15198     /**
15199      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15200      */
15201     canActivate : false,
15202     
15203      /**
15204      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15205      */
15206     hidden: false,
15207     
15208     /**
15209      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15210      */
15211     activeClass : "x-menu-item-active",
15212     /**
15213      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15214      */
15215     hideOnClick : true,
15216     /**
15217      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15218      */
15219     hideDelay : 100,
15220
15221     // private
15222     ctype: "Roo.menu.BaseItem",
15223
15224     // private
15225     actionMode : "container",
15226
15227     // private
15228     render : function(container, parentMenu){
15229         this.parentMenu = parentMenu;
15230         Roo.menu.BaseItem.superclass.render.call(this, container);
15231         this.container.menuItemId = this.id;
15232     },
15233
15234     // private
15235     onRender : function(container, position){
15236         this.el = Roo.get(this.el);
15237         container.dom.appendChild(this.el.dom);
15238     },
15239
15240     // private
15241     onClick : function(e){
15242         if(!this.disabled && this.fireEvent("click", this, e) !== false
15243                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15244             this.handleClick(e);
15245         }else{
15246             e.stopEvent();
15247         }
15248     },
15249
15250     // private
15251     activate : function(){
15252         if(this.disabled){
15253             return false;
15254         }
15255         var li = this.container;
15256         li.addClass(this.activeClass);
15257         this.region = li.getRegion().adjust(2, 2, -2, -2);
15258         this.fireEvent("activate", this);
15259         return true;
15260     },
15261
15262     // private
15263     deactivate : function(){
15264         this.container.removeClass(this.activeClass);
15265         this.fireEvent("deactivate", this);
15266     },
15267
15268     // private
15269     shouldDeactivate : function(e){
15270         return !this.region || !this.region.contains(e.getPoint());
15271     },
15272
15273     // private
15274     handleClick : function(e){
15275         if(this.hideOnClick){
15276             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15277         }
15278     },
15279
15280     // private
15281     expandMenu : function(autoActivate){
15282         // do nothing
15283     },
15284
15285     // private
15286     hideMenu : function(){
15287         // do nothing
15288     }
15289 });/*
15290  * Based on:
15291  * Ext JS Library 1.1.1
15292  * Copyright(c) 2006-2007, Ext JS, LLC.
15293  *
15294  * Originally Released Under LGPL - original licence link has changed is not relivant.
15295  *
15296  * Fork - LGPL
15297  * <script type="text/javascript">
15298  */
15299  
15300 /**
15301  * @class Roo.menu.Adapter
15302  * @extends Roo.menu.BaseItem
15303  * @abstract
15304  * 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.
15305  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15306  * @constructor
15307  * Creates a new Adapter
15308  * @param {Object} config Configuration options
15309  */
15310 Roo.menu.Adapter = function(component, config){
15311     Roo.menu.Adapter.superclass.constructor.call(this, config);
15312     this.component = component;
15313 };
15314 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15315     // private
15316     canActivate : true,
15317
15318     // private
15319     onRender : function(container, position){
15320         this.component.render(container);
15321         this.el = this.component.getEl();
15322     },
15323
15324     // private
15325     activate : function(){
15326         if(this.disabled){
15327             return false;
15328         }
15329         this.component.focus();
15330         this.fireEvent("activate", this);
15331         return true;
15332     },
15333
15334     // private
15335     deactivate : function(){
15336         this.fireEvent("deactivate", this);
15337     },
15338
15339     // private
15340     disable : function(){
15341         this.component.disable();
15342         Roo.menu.Adapter.superclass.disable.call(this);
15343     },
15344
15345     // private
15346     enable : function(){
15347         this.component.enable();
15348         Roo.menu.Adapter.superclass.enable.call(this);
15349     }
15350 });/*
15351  * Based on:
15352  * Ext JS Library 1.1.1
15353  * Copyright(c) 2006-2007, Ext JS, LLC.
15354  *
15355  * Originally Released Under LGPL - original licence link has changed is not relivant.
15356  *
15357  * Fork - LGPL
15358  * <script type="text/javascript">
15359  */
15360
15361 /**
15362  * @class Roo.menu.TextItem
15363  * @extends Roo.menu.BaseItem
15364  * Adds a static text string to a menu, usually used as either a heading or group separator.
15365  * Note: old style constructor with text is still supported.
15366  * 
15367  * @constructor
15368  * Creates a new TextItem
15369  * @param {Object} cfg Configuration
15370  */
15371 Roo.menu.TextItem = function(cfg){
15372     if (typeof(cfg) == 'string') {
15373         this.text = cfg;
15374     } else {
15375         Roo.apply(this,cfg);
15376     }
15377     
15378     Roo.menu.TextItem.superclass.constructor.call(this);
15379 };
15380
15381 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15382     /**
15383      * @cfg {String} text Text to show on item.
15384      */
15385     text : '',
15386     
15387     /**
15388      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15389      */
15390     hideOnClick : false,
15391     /**
15392      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15393      */
15394     itemCls : "x-menu-text",
15395
15396     // private
15397     onRender : function(){
15398         var s = document.createElement("span");
15399         s.className = this.itemCls;
15400         s.innerHTML = this.text;
15401         this.el = s;
15402         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15403     }
15404 });/*
15405  * Based on:
15406  * Ext JS Library 1.1.1
15407  * Copyright(c) 2006-2007, Ext JS, LLC.
15408  *
15409  * Originally Released Under LGPL - original licence link has changed is not relivant.
15410  *
15411  * Fork - LGPL
15412  * <script type="text/javascript">
15413  */
15414
15415 /**
15416  * @class Roo.menu.Separator
15417  * @extends Roo.menu.BaseItem
15418  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15419  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15420  * @constructor
15421  * @param {Object} config Configuration options
15422  */
15423 Roo.menu.Separator = function(config){
15424     Roo.menu.Separator.superclass.constructor.call(this, config);
15425 };
15426
15427 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15428     /**
15429      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15430      */
15431     itemCls : "x-menu-sep",
15432     /**
15433      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15434      */
15435     hideOnClick : false,
15436
15437     // private
15438     onRender : function(li){
15439         var s = document.createElement("span");
15440         s.className = this.itemCls;
15441         s.innerHTML = "&#160;";
15442         this.el = s;
15443         li.addClass("x-menu-sep-li");
15444         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15445     }
15446 });/*
15447  * Based on:
15448  * Ext JS Library 1.1.1
15449  * Copyright(c) 2006-2007, Ext JS, LLC.
15450  *
15451  * Originally Released Under LGPL - original licence link has changed is not relivant.
15452  *
15453  * Fork - LGPL
15454  * <script type="text/javascript">
15455  */
15456 /**
15457  * @class Roo.menu.Item
15458  * @extends Roo.menu.BaseItem
15459  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15460  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15461  * activation and click handling.
15462  * @constructor
15463  * Creates a new Item
15464  * @param {Object} config Configuration options
15465  */
15466 Roo.menu.Item = function(config){
15467     Roo.menu.Item.superclass.constructor.call(this, config);
15468     if(this.menu){
15469         this.menu = Roo.menu.MenuMgr.get(this.menu);
15470     }
15471 };
15472 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15473     /**
15474      * @cfg {Roo.menu.Menu} menu
15475      * A Sub menu
15476      */
15477     /**
15478      * @cfg {String} text
15479      * The text to show on the menu item.
15480      */
15481     text: '',
15482      /**
15483      * @cfg {String} HTML to render in menu
15484      * The text to show on the menu item (HTML version).
15485      */
15486     html: '',
15487     /**
15488      * @cfg {String} icon
15489      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15490      */
15491     icon: undefined,
15492     /**
15493      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15494      */
15495     itemCls : "x-menu-item",
15496     /**
15497      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15498      */
15499     canActivate : true,
15500     /**
15501      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15502      */
15503     showDelay: 200,
15504     // doc'd in BaseItem
15505     hideDelay: 200,
15506
15507     // private
15508     ctype: "Roo.menu.Item",
15509     
15510     // private
15511     onRender : function(container, position){
15512         var el = document.createElement("a");
15513         el.hideFocus = true;
15514         el.unselectable = "on";
15515         el.href = this.href || "#";
15516         if(this.hrefTarget){
15517             el.target = this.hrefTarget;
15518         }
15519         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15520         
15521         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15522         
15523         el.innerHTML = String.format(
15524                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15525                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15526         this.el = el;
15527         Roo.menu.Item.superclass.onRender.call(this, container, position);
15528     },
15529
15530     /**
15531      * Sets the text to display in this menu item
15532      * @param {String} text The text to display
15533      * @param {Boolean} isHTML true to indicate text is pure html.
15534      */
15535     setText : function(text, isHTML){
15536         if (isHTML) {
15537             this.html = text;
15538         } else {
15539             this.text = text;
15540             this.html = '';
15541         }
15542         if(this.rendered){
15543             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15544      
15545             this.el.update(String.format(
15546                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15547                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15548             this.parentMenu.autoWidth();
15549         }
15550     },
15551
15552     // private
15553     handleClick : function(e){
15554         if(!this.href){ // if no link defined, stop the event automatically
15555             e.stopEvent();
15556         }
15557         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15558     },
15559
15560     // private
15561     activate : function(autoExpand){
15562         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15563             this.focus();
15564             if(autoExpand){
15565                 this.expandMenu();
15566             }
15567         }
15568         return true;
15569     },
15570
15571     // private
15572     shouldDeactivate : function(e){
15573         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15574             if(this.menu && this.menu.isVisible()){
15575                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15576             }
15577             return true;
15578         }
15579         return false;
15580     },
15581
15582     // private
15583     deactivate : function(){
15584         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15585         this.hideMenu();
15586     },
15587
15588     // private
15589     expandMenu : function(autoActivate){
15590         if(!this.disabled && this.menu){
15591             clearTimeout(this.hideTimer);
15592             delete this.hideTimer;
15593             if(!this.menu.isVisible() && !this.showTimer){
15594                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15595             }else if (this.menu.isVisible() && autoActivate){
15596                 this.menu.tryActivate(0, 1);
15597             }
15598         }
15599     },
15600
15601     // private
15602     deferExpand : function(autoActivate){
15603         delete this.showTimer;
15604         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15605         if(autoActivate){
15606             this.menu.tryActivate(0, 1);
15607         }
15608     },
15609
15610     // private
15611     hideMenu : function(){
15612         clearTimeout(this.showTimer);
15613         delete this.showTimer;
15614         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15615             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15616         }
15617     },
15618
15619     // private
15620     deferHide : function(){
15621         delete this.hideTimer;
15622         this.menu.hide();
15623     }
15624 });/*
15625  * Based on:
15626  * Ext JS Library 1.1.1
15627  * Copyright(c) 2006-2007, Ext JS, LLC.
15628  *
15629  * Originally Released Under LGPL - original licence link has changed is not relivant.
15630  *
15631  * Fork - LGPL
15632  * <script type="text/javascript">
15633  */
15634  
15635 /**
15636  * @class Roo.menu.CheckItem
15637  * @extends Roo.menu.Item
15638  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15639  * @constructor
15640  * Creates a new CheckItem
15641  * @param {Object} config Configuration options
15642  */
15643 Roo.menu.CheckItem = function(config){
15644     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15645     this.addEvents({
15646         /**
15647          * @event beforecheckchange
15648          * Fires before the checked value is set, providing an opportunity to cancel if needed
15649          * @param {Roo.menu.CheckItem} this
15650          * @param {Boolean} checked The new checked value that will be set
15651          */
15652         "beforecheckchange" : true,
15653         /**
15654          * @event checkchange
15655          * Fires after the checked value has been set
15656          * @param {Roo.menu.CheckItem} this
15657          * @param {Boolean} checked The checked value that was set
15658          */
15659         "checkchange" : true
15660     });
15661     if(this.checkHandler){
15662         this.on('checkchange', this.checkHandler, this.scope);
15663     }
15664 };
15665 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15666     /**
15667      * @cfg {String} group
15668      * All check items with the same group name will automatically be grouped into a single-select
15669      * radio button group (defaults to '')
15670      */
15671     /**
15672      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15673      */
15674     itemCls : "x-menu-item x-menu-check-item",
15675     /**
15676      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15677      */
15678     groupClass : "x-menu-group-item",
15679
15680     /**
15681      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15682      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15683      * initialized with checked = true will be rendered as checked.
15684      */
15685     checked: false,
15686
15687     // private
15688     ctype: "Roo.menu.CheckItem",
15689
15690     // private
15691     onRender : function(c){
15692         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15693         if(this.group){
15694             this.el.addClass(this.groupClass);
15695         }
15696         Roo.menu.MenuMgr.registerCheckable(this);
15697         if(this.checked){
15698             this.checked = false;
15699             this.setChecked(true, true);
15700         }
15701     },
15702
15703     // private
15704     destroy : function(){
15705         if(this.rendered){
15706             Roo.menu.MenuMgr.unregisterCheckable(this);
15707         }
15708         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15709     },
15710
15711     /**
15712      * Set the checked state of this item
15713      * @param {Boolean} checked The new checked value
15714      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15715      */
15716     setChecked : function(state, suppressEvent){
15717         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15718             if(this.container){
15719                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15720             }
15721             this.checked = state;
15722             if(suppressEvent !== true){
15723                 this.fireEvent("checkchange", this, state);
15724             }
15725         }
15726     },
15727
15728     // private
15729     handleClick : function(e){
15730        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15731            this.setChecked(!this.checked);
15732        }
15733        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15734     }
15735 });/*
15736  * Based on:
15737  * Ext JS Library 1.1.1
15738  * Copyright(c) 2006-2007, Ext JS, LLC.
15739  *
15740  * Originally Released Under LGPL - original licence link has changed is not relivant.
15741  *
15742  * Fork - LGPL
15743  * <script type="text/javascript">
15744  */
15745  
15746 /**
15747  * @class Roo.menu.DateItem
15748  * @extends Roo.menu.Adapter
15749  * A menu item that wraps the {@link Roo.DatPicker} component.
15750  * @constructor
15751  * Creates a new DateItem
15752  * @param {Object} config Configuration options
15753  */
15754 Roo.menu.DateItem = function(config){
15755     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15756     /** The Roo.DatePicker object @type Roo.DatePicker */
15757     this.picker = this.component;
15758     this.addEvents({select: true});
15759     
15760     this.picker.on("render", function(picker){
15761         picker.getEl().swallowEvent("click");
15762         picker.container.addClass("x-menu-date-item");
15763     });
15764
15765     this.picker.on("select", this.onSelect, this);
15766 };
15767
15768 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15769     // private
15770     onSelect : function(picker, date){
15771         this.fireEvent("select", this, date, picker);
15772         Roo.menu.DateItem.superclass.handleClick.call(this);
15773     }
15774 });/*
15775  * Based on:
15776  * Ext JS Library 1.1.1
15777  * Copyright(c) 2006-2007, Ext JS, LLC.
15778  *
15779  * Originally Released Under LGPL - original licence link has changed is not relivant.
15780  *
15781  * Fork - LGPL
15782  * <script type="text/javascript">
15783  */
15784  
15785 /**
15786  * @class Roo.menu.ColorItem
15787  * @extends Roo.menu.Adapter
15788  * A menu item that wraps the {@link Roo.ColorPalette} component.
15789  * @constructor
15790  * Creates a new ColorItem
15791  * @param {Object} config Configuration options
15792  */
15793 Roo.menu.ColorItem = function(config){
15794     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15795     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15796     this.palette = this.component;
15797     this.relayEvents(this.palette, ["select"]);
15798     if(this.selectHandler){
15799         this.on('select', this.selectHandler, this.scope);
15800     }
15801 };
15802 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15803  * Based on:
15804  * Ext JS Library 1.1.1
15805  * Copyright(c) 2006-2007, Ext JS, LLC.
15806  *
15807  * Originally Released Under LGPL - original licence link has changed is not relivant.
15808  *
15809  * Fork - LGPL
15810  * <script type="text/javascript">
15811  */
15812  
15813
15814 /**
15815  * @class Roo.menu.DateMenu
15816  * @extends Roo.menu.Menu
15817  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15818  * @constructor
15819  * Creates a new DateMenu
15820  * @param {Object} config Configuration options
15821  */
15822 Roo.menu.DateMenu = function(config){
15823     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15824     this.plain = true;
15825     var di = new Roo.menu.DateItem(config);
15826     this.add(di);
15827     /**
15828      * The {@link Roo.DatePicker} instance for this DateMenu
15829      * @type DatePicker
15830      */
15831     this.picker = di.picker;
15832     /**
15833      * @event select
15834      * @param {DatePicker} picker
15835      * @param {Date} date
15836      */
15837     this.relayEvents(di, ["select"]);
15838     this.on('beforeshow', function(){
15839         if(this.picker){
15840             this.picker.hideMonthPicker(false);
15841         }
15842     }, this);
15843 };
15844 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15845     cls:'x-date-menu'
15846 });/*
15847  * Based on:
15848  * Ext JS Library 1.1.1
15849  * Copyright(c) 2006-2007, Ext JS, LLC.
15850  *
15851  * Originally Released Under LGPL - original licence link has changed is not relivant.
15852  *
15853  * Fork - LGPL
15854  * <script type="text/javascript">
15855  */
15856  
15857
15858 /**
15859  * @class Roo.menu.ColorMenu
15860  * @extends Roo.menu.Menu
15861  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15862  * @constructor
15863  * Creates a new ColorMenu
15864  * @param {Object} config Configuration options
15865  */
15866 Roo.menu.ColorMenu = function(config){
15867     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15868     this.plain = true;
15869     var ci = new Roo.menu.ColorItem(config);
15870     this.add(ci);
15871     /**
15872      * The {@link Roo.ColorPalette} instance for this ColorMenu
15873      * @type ColorPalette
15874      */
15875     this.palette = ci.palette;
15876     /**
15877      * @event select
15878      * @param {ColorPalette} palette
15879      * @param {String} color
15880      */
15881     this.relayEvents(ci, ["select"]);
15882 };
15883 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15884  * Based on:
15885  * Ext JS Library 1.1.1
15886  * Copyright(c) 2006-2007, Ext JS, LLC.
15887  *
15888  * Originally Released Under LGPL - original licence link has changed is not relivant.
15889  *
15890  * Fork - LGPL
15891  * <script type="text/javascript">
15892  */
15893  
15894 /**
15895  * @class Roo.form.TextItem
15896  * @extends Roo.BoxComponent
15897  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15898  * @constructor
15899  * Creates a new TextItem
15900  * @param {Object} config Configuration options
15901  */
15902 Roo.form.TextItem = function(config){
15903     Roo.form.TextItem.superclass.constructor.call(this, config);
15904 };
15905
15906 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15907     
15908     /**
15909      * @cfg {String} tag the tag for this item (default div)
15910      */
15911     tag : 'div',
15912     /**
15913      * @cfg {String} html the content for this item
15914      */
15915     html : '',
15916     
15917     getAutoCreate : function()
15918     {
15919         var cfg = {
15920             id: this.id,
15921             tag: this.tag,
15922             html: this.html,
15923             cls: 'x-form-item'
15924         };
15925         
15926         return cfg;
15927         
15928     },
15929     
15930     onRender : function(ct, position)
15931     {
15932         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15933         
15934         if(!this.el){
15935             var cfg = this.getAutoCreate();
15936             if(!cfg.name){
15937                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15938             }
15939             if (!cfg.name.length) {
15940                 delete cfg.name;
15941             }
15942             this.el = ct.createChild(cfg, position);
15943         }
15944     },
15945     /*
15946      * setHTML
15947      * @param {String} html update the Contents of the element.
15948      */
15949     setHTML : function(html)
15950     {
15951         this.fieldEl.dom.innerHTML = html;
15952     }
15953     
15954 });/*
15955  * Based on:
15956  * Ext JS Library 1.1.1
15957  * Copyright(c) 2006-2007, Ext JS, LLC.
15958  *
15959  * Originally Released Under LGPL - original licence link has changed is not relivant.
15960  *
15961  * Fork - LGPL
15962  * <script type="text/javascript">
15963  */
15964  
15965 /**
15966  * @class Roo.form.Field
15967  * @extends Roo.BoxComponent
15968  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15969  * @constructor
15970  * Creates a new Field
15971  * @param {Object} config Configuration options
15972  */
15973 Roo.form.Field = function(config){
15974     Roo.form.Field.superclass.constructor.call(this, config);
15975 };
15976
15977 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15978     /**
15979      * @cfg {String} fieldLabel Label to use when rendering a form.
15980      */
15981        /**
15982      * @cfg {String} qtip Mouse over tip
15983      */
15984      
15985     /**
15986      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15987      */
15988     invalidClass : "x-form-invalid",
15989     /**
15990      * @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")
15991      */
15992     invalidText : "The value in this field is invalid",
15993     /**
15994      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15995      */
15996     focusClass : "x-form-focus",
15997     /**
15998      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
15999       automatic validation (defaults to "keyup").
16000      */
16001     validationEvent : "keyup",
16002     /**
16003      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16004      */
16005     validateOnBlur : true,
16006     /**
16007      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16008      */
16009     validationDelay : 250,
16010     /**
16011      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16012      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16013      */
16014     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16015     /**
16016      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16017      */
16018     fieldClass : "x-form-field",
16019     /**
16020      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16021      *<pre>
16022 Value         Description
16023 -----------   ----------------------------------------------------------------------
16024 qtip          Display a quick tip when the user hovers over the field
16025 title         Display a default browser title attribute popup
16026 under         Add a block div beneath the field containing the error text
16027 side          Add an error icon to the right of the field with a popup on hover
16028 [element id]  Add the error text directly to the innerHTML of the specified element
16029 </pre>
16030      */
16031     msgTarget : 'qtip',
16032     /**
16033      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16034      */
16035     msgFx : 'normal',
16036
16037     /**
16038      * @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.
16039      */
16040     readOnly : false,
16041
16042     /**
16043      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16044      */
16045     disabled : false,
16046
16047     /**
16048      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16049      */
16050     inputType : undefined,
16051     
16052     /**
16053      * @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).
16054          */
16055         tabIndex : undefined,
16056         
16057     // private
16058     isFormField : true,
16059
16060     // private
16061     hasFocus : false,
16062     /**
16063      * @property {Roo.Element} fieldEl
16064      * Element Containing the rendered Field (with label etc.)
16065      */
16066     /**
16067      * @cfg {Mixed} value A value to initialize this field with.
16068      */
16069     value : undefined,
16070
16071     /**
16072      * @cfg {String} name The field's HTML name attribute.
16073      */
16074     /**
16075      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16076      */
16077     // private
16078     loadedValue : false,
16079      
16080      
16081         // private ??
16082         initComponent : function(){
16083         Roo.form.Field.superclass.initComponent.call(this);
16084         this.addEvents({
16085             /**
16086              * @event focus
16087              * Fires when this field receives input focus.
16088              * @param {Roo.form.Field} this
16089              */
16090             focus : true,
16091             /**
16092              * @event blur
16093              * Fires when this field loses input focus.
16094              * @param {Roo.form.Field} this
16095              */
16096             blur : true,
16097             /**
16098              * @event specialkey
16099              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16100              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16101              * @param {Roo.form.Field} this
16102              * @param {Roo.EventObject} e The event object
16103              */
16104             specialkey : true,
16105             /**
16106              * @event change
16107              * Fires just before the field blurs if the field value has changed.
16108              * @param {Roo.form.Field} this
16109              * @param {Mixed} newValue The new value
16110              * @param {Mixed} oldValue The original value
16111              */
16112             change : true,
16113             /**
16114              * @event invalid
16115              * Fires after the field has been marked as invalid.
16116              * @param {Roo.form.Field} this
16117              * @param {String} msg The validation message
16118              */
16119             invalid : true,
16120             /**
16121              * @event valid
16122              * Fires after the field has been validated with no errors.
16123              * @param {Roo.form.Field} this
16124              */
16125             valid : true,
16126              /**
16127              * @event keyup
16128              * Fires after the key up
16129              * @param {Roo.form.Field} this
16130              * @param {Roo.EventObject}  e The event Object
16131              */
16132             keyup : true
16133         });
16134     },
16135
16136     /**
16137      * Returns the name attribute of the field if available
16138      * @return {String} name The field name
16139      */
16140     getName: function(){
16141          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16142     },
16143
16144     // private
16145     onRender : function(ct, position){
16146         Roo.form.Field.superclass.onRender.call(this, ct, position);
16147         if(!this.el){
16148             var cfg = this.getAutoCreate();
16149             if(!cfg.name){
16150                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16151             }
16152             if (!cfg.name.length) {
16153                 delete cfg.name;
16154             }
16155             if(this.inputType){
16156                 cfg.type = this.inputType;
16157             }
16158             this.el = ct.createChild(cfg, position);
16159         }
16160         var type = this.el.dom.type;
16161         if(type){
16162             if(type == 'password'){
16163                 type = 'text';
16164             }
16165             this.el.addClass('x-form-'+type);
16166         }
16167         if(this.readOnly){
16168             this.el.dom.readOnly = true;
16169         }
16170         if(this.tabIndex !== undefined){
16171             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16172         }
16173
16174         this.el.addClass([this.fieldClass, this.cls]);
16175         this.initValue();
16176     },
16177
16178     /**
16179      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16180      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16181      * @return {Roo.form.Field} this
16182      */
16183     applyTo : function(target){
16184         this.allowDomMove = false;
16185         this.el = Roo.get(target);
16186         this.render(this.el.dom.parentNode);
16187         return this;
16188     },
16189
16190     // private
16191     initValue : function(){
16192         if(this.value !== undefined){
16193             this.setValue(this.value);
16194         }else if(this.el.dom.value.length > 0){
16195             this.setValue(this.el.dom.value);
16196         }
16197     },
16198
16199     /**
16200      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16201      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16202      */
16203     isDirty : function() {
16204         if(this.disabled) {
16205             return false;
16206         }
16207         return String(this.getValue()) !== String(this.originalValue);
16208     },
16209
16210     /**
16211      * stores the current value in loadedValue
16212      */
16213     resetHasChanged : function()
16214     {
16215         this.loadedValue = String(this.getValue());
16216     },
16217     /**
16218      * checks the current value against the 'loaded' value.
16219      * Note - will return false if 'resetHasChanged' has not been called first.
16220      */
16221     hasChanged : function()
16222     {
16223         if(this.disabled || this.readOnly) {
16224             return false;
16225         }
16226         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16227     },
16228     
16229     
16230     
16231     // private
16232     afterRender : function(){
16233         Roo.form.Field.superclass.afterRender.call(this);
16234         this.initEvents();
16235     },
16236
16237     // private
16238     fireKey : function(e){
16239         //Roo.log('field ' + e.getKey());
16240         if(e.isNavKeyPress()){
16241             this.fireEvent("specialkey", this, e);
16242         }
16243     },
16244
16245     /**
16246      * Resets the current field value to the originally loaded value and clears any validation messages
16247      */
16248     reset : function(){
16249         this.setValue(this.resetValue);
16250         this.originalValue = this.getValue();
16251         this.clearInvalid();
16252     },
16253
16254     // private
16255     initEvents : function(){
16256         // safari killled keypress - so keydown is now used..
16257         this.el.on("keydown" , this.fireKey,  this);
16258         this.el.on("focus", this.onFocus,  this);
16259         this.el.on("blur", this.onBlur,  this);
16260         this.el.relayEvent('keyup', this);
16261
16262         // reference to original value for reset
16263         this.originalValue = this.getValue();
16264         this.resetValue =  this.getValue();
16265     },
16266
16267     // private
16268     onFocus : function(){
16269         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16270             this.el.addClass(this.focusClass);
16271         }
16272         if(!this.hasFocus){
16273             this.hasFocus = true;
16274             this.startValue = this.getValue();
16275             this.fireEvent("focus", this);
16276         }
16277     },
16278
16279     beforeBlur : Roo.emptyFn,
16280
16281     // private
16282     onBlur : function(){
16283         this.beforeBlur();
16284         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16285             this.el.removeClass(this.focusClass);
16286         }
16287         this.hasFocus = false;
16288         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16289             this.validate();
16290         }
16291         var v = this.getValue();
16292         if(String(v) !== String(this.startValue)){
16293             this.fireEvent('change', this, v, this.startValue);
16294         }
16295         this.fireEvent("blur", this);
16296     },
16297
16298     /**
16299      * Returns whether or not the field value is currently valid
16300      * @param {Boolean} preventMark True to disable marking the field invalid
16301      * @return {Boolean} True if the value is valid, else false
16302      */
16303     isValid : function(preventMark){
16304         if(this.disabled){
16305             return true;
16306         }
16307         var restore = this.preventMark;
16308         this.preventMark = preventMark === true;
16309         var v = this.validateValue(this.processValue(this.getRawValue()));
16310         this.preventMark = restore;
16311         return v;
16312     },
16313
16314     /**
16315      * Validates the field value
16316      * @return {Boolean} True if the value is valid, else false
16317      */
16318     validate : function(){
16319         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16320             this.clearInvalid();
16321             return true;
16322         }
16323         return false;
16324     },
16325
16326     processValue : function(value){
16327         return value;
16328     },
16329
16330     // private
16331     // Subclasses should provide the validation implementation by overriding this
16332     validateValue : function(value){
16333         return true;
16334     },
16335
16336     /**
16337      * Mark this field as invalid
16338      * @param {String} msg The validation message
16339      */
16340     markInvalid : function(msg){
16341         if(!this.rendered || this.preventMark){ // not rendered
16342             return;
16343         }
16344         
16345         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16346         
16347         obj.el.addClass(this.invalidClass);
16348         msg = msg || this.invalidText;
16349         switch(this.msgTarget){
16350             case 'qtip':
16351                 obj.el.dom.qtip = msg;
16352                 obj.el.dom.qclass = 'x-form-invalid-tip';
16353                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16354                     Roo.QuickTips.enable();
16355                 }
16356                 break;
16357             case 'title':
16358                 this.el.dom.title = msg;
16359                 break;
16360             case 'under':
16361                 if(!this.errorEl){
16362                     var elp = this.el.findParent('.x-form-element', 5, true);
16363                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16364                     this.errorEl.setWidth(elp.getWidth(true)-20);
16365                 }
16366                 this.errorEl.update(msg);
16367                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16368                 break;
16369             case 'side':
16370                 if(!this.errorIcon){
16371                     var elp = this.el.findParent('.x-form-element', 5, true);
16372                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16373                 }
16374                 this.alignErrorIcon();
16375                 this.errorIcon.dom.qtip = msg;
16376                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16377                 this.errorIcon.show();
16378                 this.on('resize', this.alignErrorIcon, this);
16379                 break;
16380             default:
16381                 var t = Roo.getDom(this.msgTarget);
16382                 t.innerHTML = msg;
16383                 t.style.display = this.msgDisplay;
16384                 break;
16385         }
16386         this.fireEvent('invalid', this, msg);
16387     },
16388
16389     // private
16390     alignErrorIcon : function(){
16391         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16392     },
16393
16394     /**
16395      * Clear any invalid styles/messages for this field
16396      */
16397     clearInvalid : function(){
16398         if(!this.rendered || this.preventMark){ // not rendered
16399             return;
16400         }
16401         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16402         
16403         obj.el.removeClass(this.invalidClass);
16404         switch(this.msgTarget){
16405             case 'qtip':
16406                 obj.el.dom.qtip = '';
16407                 break;
16408             case 'title':
16409                 this.el.dom.title = '';
16410                 break;
16411             case 'under':
16412                 if(this.errorEl){
16413                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16414                 }
16415                 break;
16416             case 'side':
16417                 if(this.errorIcon){
16418                     this.errorIcon.dom.qtip = '';
16419                     this.errorIcon.hide();
16420                     this.un('resize', this.alignErrorIcon, this);
16421                 }
16422                 break;
16423             default:
16424                 var t = Roo.getDom(this.msgTarget);
16425                 t.innerHTML = '';
16426                 t.style.display = 'none';
16427                 break;
16428         }
16429         this.fireEvent('valid', this);
16430     },
16431
16432     /**
16433      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16434      * @return {Mixed} value The field value
16435      */
16436     getRawValue : function(){
16437         var v = this.el.getValue();
16438         
16439         return v;
16440     },
16441
16442     /**
16443      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16444      * @return {Mixed} value The field value
16445      */
16446     getValue : function(){
16447         var v = this.el.getValue();
16448          
16449         return v;
16450     },
16451
16452     /**
16453      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16454      * @param {Mixed} value The value to set
16455      */
16456     setRawValue : function(v){
16457         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16458     },
16459
16460     /**
16461      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16462      * @param {Mixed} value The value to set
16463      */
16464     setValue : function(v){
16465         this.value = v;
16466         if(this.rendered){
16467             this.el.dom.value = (v === null || v === undefined ? '' : v);
16468              this.validate();
16469         }
16470     },
16471
16472     adjustSize : function(w, h){
16473         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16474         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16475         return s;
16476     },
16477
16478     adjustWidth : function(tag, w){
16479         tag = tag.toLowerCase();
16480         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16481             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16482                 if(tag == 'input'){
16483                     return w + 2;
16484                 }
16485                 if(tag == 'textarea'){
16486                     return w-2;
16487                 }
16488             }else if(Roo.isOpera){
16489                 if(tag == 'input'){
16490                     return w + 2;
16491                 }
16492                 if(tag == 'textarea'){
16493                     return w-2;
16494                 }
16495             }
16496         }
16497         return w;
16498     }
16499 });
16500
16501
16502 // anything other than normal should be considered experimental
16503 Roo.form.Field.msgFx = {
16504     normal : {
16505         show: function(msgEl, f){
16506             msgEl.setDisplayed('block');
16507         },
16508
16509         hide : function(msgEl, f){
16510             msgEl.setDisplayed(false).update('');
16511         }
16512     },
16513
16514     slide : {
16515         show: function(msgEl, f){
16516             msgEl.slideIn('t', {stopFx:true});
16517         },
16518
16519         hide : function(msgEl, f){
16520             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16521         }
16522     },
16523
16524     slideRight : {
16525         show: function(msgEl, f){
16526             msgEl.fixDisplay();
16527             msgEl.alignTo(f.el, 'tl-tr');
16528             msgEl.slideIn('l', {stopFx:true});
16529         },
16530
16531         hide : function(msgEl, f){
16532             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16533         }
16534     }
16535 };/*
16536  * Based on:
16537  * Ext JS Library 1.1.1
16538  * Copyright(c) 2006-2007, Ext JS, LLC.
16539  *
16540  * Originally Released Under LGPL - original licence link has changed is not relivant.
16541  *
16542  * Fork - LGPL
16543  * <script type="text/javascript">
16544  */
16545  
16546
16547 /**
16548  * @class Roo.form.TextField
16549  * @extends Roo.form.Field
16550  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16551  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16552  * @constructor
16553  * Creates a new TextField
16554  * @param {Object} config Configuration options
16555  */
16556 Roo.form.TextField = function(config){
16557     Roo.form.TextField.superclass.constructor.call(this, config);
16558     this.addEvents({
16559         /**
16560          * @event autosize
16561          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16562          * according to the default logic, but this event provides a hook for the developer to apply additional
16563          * logic at runtime to resize the field if needed.
16564              * @param {Roo.form.Field} this This text field
16565              * @param {Number} width The new field width
16566              */
16567         autosize : true
16568     });
16569 };
16570
16571 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16572     /**
16573      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16574      */
16575     grow : false,
16576     /**
16577      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16578      */
16579     growMin : 30,
16580     /**
16581      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16582      */
16583     growMax : 800,
16584     /**
16585      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16586      */
16587     vtype : null,
16588     /**
16589      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16590      */
16591     maskRe : null,
16592     /**
16593      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16594      */
16595     disableKeyFilter : false,
16596     /**
16597      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16598      */
16599     allowBlank : true,
16600     /**
16601      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16602      */
16603     minLength : 0,
16604     /**
16605      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16606      */
16607     maxLength : Number.MAX_VALUE,
16608     /**
16609      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16610      */
16611     minLengthText : "The minimum length for this field is {0}",
16612     /**
16613      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16614      */
16615     maxLengthText : "The maximum length for this field is {0}",
16616     /**
16617      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16618      */
16619     selectOnFocus : false,
16620     /**
16621      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16622      */    
16623     allowLeadingSpace : false,
16624     /**
16625      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16626      */
16627     blankText : "This field is required",
16628     /**
16629      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16630      * If available, this function will be called only after the basic validators all return true, and will be passed the
16631      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16632      */
16633     validator : null,
16634     /**
16635      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16636      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16637      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16638      */
16639     regex : null,
16640     /**
16641      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16642      */
16643     regexText : "",
16644     /**
16645      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16646      */
16647     emptyText : null,
16648    
16649
16650     // private
16651     initEvents : function()
16652     {
16653         if (this.emptyText) {
16654             this.el.attr('placeholder', this.emptyText);
16655         }
16656         
16657         Roo.form.TextField.superclass.initEvents.call(this);
16658         if(this.validationEvent == 'keyup'){
16659             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16660             this.el.on('keyup', this.filterValidation, this);
16661         }
16662         else if(this.validationEvent !== false){
16663             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16664         }
16665         
16666         if(this.selectOnFocus){
16667             this.on("focus", this.preFocus, this);
16668         }
16669         if (!this.allowLeadingSpace) {
16670             this.on('blur', this.cleanLeadingSpace, this);
16671         }
16672         
16673         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16674             this.el.on("keypress", this.filterKeys, this);
16675         }
16676         if(this.grow){
16677             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16678             this.el.on("click", this.autoSize,  this);
16679         }
16680         if(this.el.is('input[type=password]') && Roo.isSafari){
16681             this.el.on('keydown', this.SafariOnKeyDown, this);
16682         }
16683     },
16684
16685     processValue : function(value){
16686         if(this.stripCharsRe){
16687             var newValue = value.replace(this.stripCharsRe, '');
16688             if(newValue !== value){
16689                 this.setRawValue(newValue);
16690                 return newValue;
16691             }
16692         }
16693         return value;
16694     },
16695
16696     filterValidation : function(e){
16697         if(!e.isNavKeyPress()){
16698             this.validationTask.delay(this.validationDelay);
16699         }
16700     },
16701
16702     // private
16703     onKeyUp : function(e){
16704         if(!e.isNavKeyPress()){
16705             this.autoSize();
16706         }
16707     },
16708     // private - clean the leading white space
16709     cleanLeadingSpace : function(e)
16710     {
16711         if ( this.inputType == 'file') {
16712             return;
16713         }
16714         
16715         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16716     },
16717     /**
16718      * Resets the current field value to the originally-loaded value and clears any validation messages.
16719      *  
16720      */
16721     reset : function(){
16722         Roo.form.TextField.superclass.reset.call(this);
16723        
16724     }, 
16725     // private
16726     preFocus : function(){
16727         
16728         if(this.selectOnFocus){
16729             this.el.dom.select();
16730         }
16731     },
16732
16733     
16734     // private
16735     filterKeys : function(e){
16736         var k = e.getKey();
16737         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16738             return;
16739         }
16740         var c = e.getCharCode(), cc = String.fromCharCode(c);
16741         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16742             return;
16743         }
16744         if(!this.maskRe.test(cc)){
16745             e.stopEvent();
16746         }
16747     },
16748
16749     setValue : function(v){
16750         
16751         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16752         
16753         this.autoSize();
16754     },
16755
16756     /**
16757      * Validates a value according to the field's validation rules and marks the field as invalid
16758      * if the validation fails
16759      * @param {Mixed} value The value to validate
16760      * @return {Boolean} True if the value is valid, else false
16761      */
16762     validateValue : function(value){
16763         if(value.length < 1)  { // if it's blank
16764              if(this.allowBlank){
16765                 this.clearInvalid();
16766                 return true;
16767              }else{
16768                 this.markInvalid(this.blankText);
16769                 return false;
16770              }
16771         }
16772         if(value.length < this.minLength){
16773             this.markInvalid(String.format(this.minLengthText, this.minLength));
16774             return false;
16775         }
16776         if(value.length > this.maxLength){
16777             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16778             return false;
16779         }
16780         if(this.vtype){
16781             var vt = Roo.form.VTypes;
16782             if(!vt[this.vtype](value, this)){
16783                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16784                 return false;
16785             }
16786         }
16787         if(typeof this.validator == "function"){
16788             var msg = this.validator(value);
16789             if(msg !== true){
16790                 this.markInvalid(msg);
16791                 return false;
16792             }
16793         }
16794         if(this.regex && !this.regex.test(value)){
16795             this.markInvalid(this.regexText);
16796             return false;
16797         }
16798         return true;
16799     },
16800
16801     /**
16802      * Selects text in this field
16803      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16804      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16805      */
16806     selectText : function(start, end){
16807         var v = this.getRawValue();
16808         if(v.length > 0){
16809             start = start === undefined ? 0 : start;
16810             end = end === undefined ? v.length : end;
16811             var d = this.el.dom;
16812             if(d.setSelectionRange){
16813                 d.setSelectionRange(start, end);
16814             }else if(d.createTextRange){
16815                 var range = d.createTextRange();
16816                 range.moveStart("character", start);
16817                 range.moveEnd("character", v.length-end);
16818                 range.select();
16819             }
16820         }
16821     },
16822
16823     /**
16824      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16825      * This only takes effect if grow = true, and fires the autosize event.
16826      */
16827     autoSize : function(){
16828         if(!this.grow || !this.rendered){
16829             return;
16830         }
16831         if(!this.metrics){
16832             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16833         }
16834         var el = this.el;
16835         var v = el.dom.value;
16836         var d = document.createElement('div');
16837         d.appendChild(document.createTextNode(v));
16838         v = d.innerHTML;
16839         d = null;
16840         v += "&#160;";
16841         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16842         this.el.setWidth(w);
16843         this.fireEvent("autosize", this, w);
16844     },
16845     
16846     // private
16847     SafariOnKeyDown : function(event)
16848     {
16849         // this is a workaround for a password hang bug on chrome/ webkit.
16850         
16851         var isSelectAll = false;
16852         
16853         if(this.el.dom.selectionEnd > 0){
16854             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16855         }
16856         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16857             event.preventDefault();
16858             this.setValue('');
16859             return;
16860         }
16861         
16862         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16863             
16864             event.preventDefault();
16865             // this is very hacky as keydown always get's upper case.
16866             
16867             var cc = String.fromCharCode(event.getCharCode());
16868             
16869             
16870             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16871             
16872         }
16873         
16874         
16875     }
16876 });/*
16877  * Based on:
16878  * Ext JS Library 1.1.1
16879  * Copyright(c) 2006-2007, Ext JS, LLC.
16880  *
16881  * Originally Released Under LGPL - original licence link has changed is not relivant.
16882  *
16883  * Fork - LGPL
16884  * <script type="text/javascript">
16885  */
16886  
16887 /**
16888  * @class Roo.form.Hidden
16889  * @extends Roo.form.TextField
16890  * Simple Hidden element used on forms 
16891  * 
16892  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16893  * 
16894  * @constructor
16895  * Creates a new Hidden form element.
16896  * @param {Object} config Configuration options
16897  */
16898
16899
16900
16901 // easy hidden field...
16902 Roo.form.Hidden = function(config){
16903     Roo.form.Hidden.superclass.constructor.call(this, config);
16904 };
16905   
16906 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16907     fieldLabel:      '',
16908     inputType:      'hidden',
16909     width:          50,
16910     allowBlank:     true,
16911     labelSeparator: '',
16912     hidden:         true,
16913     itemCls :       'x-form-item-display-none'
16914
16915
16916 });
16917
16918
16919 /*
16920  * Based on:
16921  * Ext JS Library 1.1.1
16922  * Copyright(c) 2006-2007, Ext JS, LLC.
16923  *
16924  * Originally Released Under LGPL - original licence link has changed is not relivant.
16925  *
16926  * Fork - LGPL
16927  * <script type="text/javascript">
16928  */
16929  
16930 /**
16931  * @class Roo.form.TriggerField
16932  * @extends Roo.form.TextField
16933  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16934  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16935  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16936  * for which you can provide a custom implementation.  For example:
16937  * <pre><code>
16938 var trigger = new Roo.form.TriggerField();
16939 trigger.onTriggerClick = myTriggerFn;
16940 trigger.applyTo('my-field');
16941 </code></pre>
16942  *
16943  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16944  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16945  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16946  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16947  * @constructor
16948  * Create a new TriggerField.
16949  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16950  * to the base TextField)
16951  */
16952 Roo.form.TriggerField = function(config){
16953     this.mimicing = false;
16954     Roo.form.TriggerField.superclass.constructor.call(this, config);
16955 };
16956
16957 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16958     /**
16959      * @cfg {String} triggerClass A CSS class to apply to the trigger
16960      */
16961     /**
16962      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16963      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16964      */
16965     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16966     /**
16967      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16968      */
16969     hideTrigger:false,
16970
16971     /** @cfg {Boolean} grow @hide */
16972     /** @cfg {Number} growMin @hide */
16973     /** @cfg {Number} growMax @hide */
16974
16975     /**
16976      * @hide 
16977      * @method
16978      */
16979     autoSize: Roo.emptyFn,
16980     // private
16981     monitorTab : true,
16982     // private
16983     deferHeight : true,
16984
16985     
16986     actionMode : 'wrap',
16987     // private
16988     onResize : function(w, h){
16989         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16990         if(typeof w == 'number'){
16991             var x = w - this.trigger.getWidth();
16992             this.el.setWidth(this.adjustWidth('input', x));
16993             this.trigger.setStyle('left', x+'px');
16994         }
16995     },
16996
16997     // private
16998     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16999
17000     // private
17001     getResizeEl : function(){
17002         return this.wrap;
17003     },
17004
17005     // private
17006     getPositionEl : function(){
17007         return this.wrap;
17008     },
17009
17010     // private
17011     alignErrorIcon : function(){
17012         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17013     },
17014
17015     // private
17016     onRender : function(ct, position){
17017         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17018         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17019         this.trigger = this.wrap.createChild(this.triggerConfig ||
17020                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17021         if(this.hideTrigger){
17022             this.trigger.setDisplayed(false);
17023         }
17024         this.initTrigger();
17025         if(!this.width){
17026             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17027         }
17028     },
17029
17030     // private
17031     initTrigger : function(){
17032         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17033         this.trigger.addClassOnOver('x-form-trigger-over');
17034         this.trigger.addClassOnClick('x-form-trigger-click');
17035     },
17036
17037     // private
17038     onDestroy : function(){
17039         if(this.trigger){
17040             this.trigger.removeAllListeners();
17041             this.trigger.remove();
17042         }
17043         if(this.wrap){
17044             this.wrap.remove();
17045         }
17046         Roo.form.TriggerField.superclass.onDestroy.call(this);
17047     },
17048
17049     // private
17050     onFocus : function(){
17051         Roo.form.TriggerField.superclass.onFocus.call(this);
17052         if(!this.mimicing){
17053             this.wrap.addClass('x-trigger-wrap-focus');
17054             this.mimicing = true;
17055             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17056             if(this.monitorTab){
17057                 this.el.on("keydown", this.checkTab, this);
17058             }
17059         }
17060     },
17061
17062     // private
17063     checkTab : function(e){
17064         if(e.getKey() == e.TAB){
17065             this.triggerBlur();
17066         }
17067     },
17068
17069     // private
17070     onBlur : function(){
17071         // do nothing
17072     },
17073
17074     // private
17075     mimicBlur : function(e, t){
17076         if(!this.wrap.contains(t) && this.validateBlur()){
17077             this.triggerBlur();
17078         }
17079     },
17080
17081     // private
17082     triggerBlur : function(){
17083         this.mimicing = false;
17084         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17085         if(this.monitorTab){
17086             this.el.un("keydown", this.checkTab, this);
17087         }
17088         this.wrap.removeClass('x-trigger-wrap-focus');
17089         Roo.form.TriggerField.superclass.onBlur.call(this);
17090     },
17091
17092     // private
17093     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17094     validateBlur : function(e, t){
17095         return true;
17096     },
17097
17098     // private
17099     onDisable : function(){
17100         Roo.form.TriggerField.superclass.onDisable.call(this);
17101         if(this.wrap){
17102             this.wrap.addClass('x-item-disabled');
17103         }
17104     },
17105
17106     // private
17107     onEnable : function(){
17108         Roo.form.TriggerField.superclass.onEnable.call(this);
17109         if(this.wrap){
17110             this.wrap.removeClass('x-item-disabled');
17111         }
17112     },
17113
17114     // private
17115     onShow : function(){
17116         var ae = this.getActionEl();
17117         
17118         if(ae){
17119             ae.dom.style.display = '';
17120             ae.dom.style.visibility = 'visible';
17121         }
17122     },
17123
17124     // private
17125     
17126     onHide : function(){
17127         var ae = this.getActionEl();
17128         ae.dom.style.display = 'none';
17129     },
17130
17131     /**
17132      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17133      * by an implementing function.
17134      * @method
17135      * @param {EventObject} e
17136      */
17137     onTriggerClick : Roo.emptyFn
17138 });
17139
17140 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17141 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17142 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17143 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17144     initComponent : function(){
17145         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17146
17147         this.triggerConfig = {
17148             tag:'span', cls:'x-form-twin-triggers', cn:[
17149             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17150             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17151         ]};
17152     },
17153
17154     getTrigger : function(index){
17155         return this.triggers[index];
17156     },
17157
17158     initTrigger : function(){
17159         var ts = this.trigger.select('.x-form-trigger', true);
17160         this.wrap.setStyle('overflow', 'hidden');
17161         var triggerField = this;
17162         ts.each(function(t, all, index){
17163             t.hide = function(){
17164                 var w = triggerField.wrap.getWidth();
17165                 this.dom.style.display = 'none';
17166                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17167             };
17168             t.show = function(){
17169                 var w = triggerField.wrap.getWidth();
17170                 this.dom.style.display = '';
17171                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17172             };
17173             var triggerIndex = 'Trigger'+(index+1);
17174
17175             if(this['hide'+triggerIndex]){
17176                 t.dom.style.display = 'none';
17177             }
17178             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17179             t.addClassOnOver('x-form-trigger-over');
17180             t.addClassOnClick('x-form-trigger-click');
17181         }, this);
17182         this.triggers = ts.elements;
17183     },
17184
17185     onTrigger1Click : Roo.emptyFn,
17186     onTrigger2Click : Roo.emptyFn
17187 });/*
17188  * Based on:
17189  * Ext JS Library 1.1.1
17190  * Copyright(c) 2006-2007, Ext JS, LLC.
17191  *
17192  * Originally Released Under LGPL - original licence link has changed is not relivant.
17193  *
17194  * Fork - LGPL
17195  * <script type="text/javascript">
17196  */
17197  
17198 /**
17199  * @class Roo.form.TextArea
17200  * @extends Roo.form.TextField
17201  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17202  * support for auto-sizing.
17203  * @constructor
17204  * Creates a new TextArea
17205  * @param {Object} config Configuration options
17206  */
17207 Roo.form.TextArea = function(config){
17208     Roo.form.TextArea.superclass.constructor.call(this, config);
17209     // these are provided exchanges for backwards compat
17210     // minHeight/maxHeight were replaced by growMin/growMax to be
17211     // compatible with TextField growing config values
17212     if(this.minHeight !== undefined){
17213         this.growMin = this.minHeight;
17214     }
17215     if(this.maxHeight !== undefined){
17216         this.growMax = this.maxHeight;
17217     }
17218 };
17219
17220 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17221     /**
17222      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17223      */
17224     growMin : 60,
17225     /**
17226      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17227      */
17228     growMax: 1000,
17229     /**
17230      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17231      * in the field (equivalent to setting overflow: hidden, defaults to false)
17232      */
17233     preventScrollbars: false,
17234     /**
17235      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17236      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17237      */
17238
17239     // private
17240     onRender : function(ct, position){
17241         if(!this.el){
17242             this.defaultAutoCreate = {
17243                 tag: "textarea",
17244                 style:"width:300px;height:60px;",
17245                 autocomplete: "new-password"
17246             };
17247         }
17248         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17249         if(this.grow){
17250             this.textSizeEl = Roo.DomHelper.append(document.body, {
17251                 tag: "pre", cls: "x-form-grow-sizer"
17252             });
17253             if(this.preventScrollbars){
17254                 this.el.setStyle("overflow", "hidden");
17255             }
17256             this.el.setHeight(this.growMin);
17257         }
17258     },
17259
17260     onDestroy : function(){
17261         if(this.textSizeEl){
17262             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17263         }
17264         Roo.form.TextArea.superclass.onDestroy.call(this);
17265     },
17266
17267     // private
17268     onKeyUp : function(e){
17269         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17270             this.autoSize();
17271         }
17272     },
17273
17274     /**
17275      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17276      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17277      */
17278     autoSize : function(){
17279         if(!this.grow || !this.textSizeEl){
17280             return;
17281         }
17282         var el = this.el;
17283         var v = el.dom.value;
17284         var ts = this.textSizeEl;
17285
17286         ts.innerHTML = '';
17287         ts.appendChild(document.createTextNode(v));
17288         v = ts.innerHTML;
17289
17290         Roo.fly(ts).setWidth(this.el.getWidth());
17291         if(v.length < 1){
17292             v = "&#160;&#160;";
17293         }else{
17294             if(Roo.isIE){
17295                 v = v.replace(/\n/g, '<p>&#160;</p>');
17296             }
17297             v += "&#160;\n&#160;";
17298         }
17299         ts.innerHTML = v;
17300         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17301         if(h != this.lastHeight){
17302             this.lastHeight = h;
17303             this.el.setHeight(h);
17304             this.fireEvent("autosize", this, h);
17305         }
17306     }
17307 });/*
17308  * Based on:
17309  * Ext JS Library 1.1.1
17310  * Copyright(c) 2006-2007, Ext JS, LLC.
17311  *
17312  * Originally Released Under LGPL - original licence link has changed is not relivant.
17313  *
17314  * Fork - LGPL
17315  * <script type="text/javascript">
17316  */
17317  
17318
17319 /**
17320  * @class Roo.form.NumberField
17321  * @extends Roo.form.TextField
17322  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17323  * @constructor
17324  * Creates a new NumberField
17325  * @param {Object} config Configuration options
17326  */
17327 Roo.form.NumberField = function(config){
17328     Roo.form.NumberField.superclass.constructor.call(this, config);
17329 };
17330
17331 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17332     /**
17333      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17334      */
17335     fieldClass: "x-form-field x-form-num-field",
17336     /**
17337      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17338      */
17339     allowDecimals : true,
17340     /**
17341      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17342      */
17343     decimalSeparator : ".",
17344     /**
17345      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17346      */
17347     decimalPrecision : 2,
17348     /**
17349      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17350      */
17351     allowNegative : true,
17352     /**
17353      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17354      */
17355     minValue : Number.NEGATIVE_INFINITY,
17356     /**
17357      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17358      */
17359     maxValue : Number.MAX_VALUE,
17360     /**
17361      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17362      */
17363     minText : "The minimum value for this field is {0}",
17364     /**
17365      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17366      */
17367     maxText : "The maximum value for this field is {0}",
17368     /**
17369      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17370      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17371      */
17372     nanText : "{0} is not a valid number",
17373
17374     // private
17375     initEvents : function(){
17376         Roo.form.NumberField.superclass.initEvents.call(this);
17377         var allowed = "0123456789";
17378         if(this.allowDecimals){
17379             allowed += this.decimalSeparator;
17380         }
17381         if(this.allowNegative){
17382             allowed += "-";
17383         }
17384         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17385         var keyPress = function(e){
17386             var k = e.getKey();
17387             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17388                 return;
17389             }
17390             var c = e.getCharCode();
17391             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17392                 e.stopEvent();
17393             }
17394         };
17395         this.el.on("keypress", keyPress, this);
17396     },
17397
17398     // private
17399     validateValue : function(value){
17400         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17401             return false;
17402         }
17403         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17404              return true;
17405         }
17406         var num = this.parseValue(value);
17407         if(isNaN(num)){
17408             this.markInvalid(String.format(this.nanText, value));
17409             return false;
17410         }
17411         if(num < this.minValue){
17412             this.markInvalid(String.format(this.minText, this.minValue));
17413             return false;
17414         }
17415         if(num > this.maxValue){
17416             this.markInvalid(String.format(this.maxText, this.maxValue));
17417             return false;
17418         }
17419         return true;
17420     },
17421
17422     getValue : function(){
17423         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17424     },
17425
17426     // private
17427     parseValue : function(value){
17428         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17429         return isNaN(value) ? '' : value;
17430     },
17431
17432     // private
17433     fixPrecision : function(value){
17434         var nan = isNaN(value);
17435         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17436             return nan ? '' : value;
17437         }
17438         return parseFloat(value).toFixed(this.decimalPrecision);
17439     },
17440
17441     setValue : function(v){
17442         v = this.fixPrecision(v);
17443         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17444     },
17445
17446     // private
17447     decimalPrecisionFcn : function(v){
17448         return Math.floor(v);
17449     },
17450
17451     beforeBlur : function(){
17452         var v = this.parseValue(this.getRawValue());
17453         if(v){
17454             this.setValue(v);
17455         }
17456     }
17457 });/*
17458  * Based on:
17459  * Ext JS Library 1.1.1
17460  * Copyright(c) 2006-2007, Ext JS, LLC.
17461  *
17462  * Originally Released Under LGPL - original licence link has changed is not relivant.
17463  *
17464  * Fork - LGPL
17465  * <script type="text/javascript">
17466  */
17467  
17468 /**
17469  * @class Roo.form.DateField
17470  * @extends Roo.form.TriggerField
17471  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17472 * @constructor
17473 * Create a new DateField
17474 * @param {Object} config
17475  */
17476 Roo.form.DateField = function(config)
17477 {
17478     Roo.form.DateField.superclass.constructor.call(this, config);
17479     
17480       this.addEvents({
17481          
17482         /**
17483          * @event select
17484          * Fires when a date is selected
17485              * @param {Roo.form.DateField} combo This combo box
17486              * @param {Date} date The date selected
17487              */
17488         'select' : true
17489          
17490     });
17491     
17492     
17493     if(typeof this.minValue == "string") {
17494         this.minValue = this.parseDate(this.minValue);
17495     }
17496     if(typeof this.maxValue == "string") {
17497         this.maxValue = this.parseDate(this.maxValue);
17498     }
17499     this.ddMatch = null;
17500     if(this.disabledDates){
17501         var dd = this.disabledDates;
17502         var re = "(?:";
17503         for(var i = 0; i < dd.length; i++){
17504             re += dd[i];
17505             if(i != dd.length-1) {
17506                 re += "|";
17507             }
17508         }
17509         this.ddMatch = new RegExp(re + ")");
17510     }
17511 };
17512
17513 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17514     /**
17515      * @cfg {String} format
17516      * The default date format string which can be overriden for localization support.  The format must be
17517      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17518      */
17519     format : "m/d/y",
17520     /**
17521      * @cfg {String} altFormats
17522      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17523      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17524      */
17525     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17526     /**
17527      * @cfg {Array} disabledDays
17528      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17529      */
17530     disabledDays : null,
17531     /**
17532      * @cfg {String} disabledDaysText
17533      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17534      */
17535     disabledDaysText : "Disabled",
17536     /**
17537      * @cfg {Array} disabledDates
17538      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17539      * expression so they are very powerful. Some examples:
17540      * <ul>
17541      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17542      * <li>["03/08", "09/16"] would disable those days for every year</li>
17543      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17544      * <li>["03/../2006"] would disable every day in March 2006</li>
17545      * <li>["^03"] would disable every day in every March</li>
17546      * </ul>
17547      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17548      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17549      */
17550     disabledDates : null,
17551     /**
17552      * @cfg {String} disabledDatesText
17553      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17554      */
17555     disabledDatesText : "Disabled",
17556     /**
17557      * @cfg {Date/String} minValue
17558      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17559      * valid format (defaults to null).
17560      */
17561     minValue : null,
17562     /**
17563      * @cfg {Date/String} maxValue
17564      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17565      * valid format (defaults to null).
17566      */
17567     maxValue : null,
17568     /**
17569      * @cfg {String} minText
17570      * The error text to display when the date in the cell is before minValue (defaults to
17571      * 'The date in this field must be after {minValue}').
17572      */
17573     minText : "The date in this field must be equal to or after {0}",
17574     /**
17575      * @cfg {String} maxText
17576      * The error text to display when the date in the cell is after maxValue (defaults to
17577      * 'The date in this field must be before {maxValue}').
17578      */
17579     maxText : "The date in this field must be equal to or before {0}",
17580     /**
17581      * @cfg {String} invalidText
17582      * The error text to display when the date in the field is invalid (defaults to
17583      * '{value} is not a valid date - it must be in the format {format}').
17584      */
17585     invalidText : "{0} is not a valid date - it must be in the format {1}",
17586     /**
17587      * @cfg {String} triggerClass
17588      * An additional CSS class used to style the trigger button.  The trigger will always get the
17589      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17590      * which displays a calendar icon).
17591      */
17592     triggerClass : 'x-form-date-trigger',
17593     
17594
17595     /**
17596      * @cfg {Boolean} useIso
17597      * if enabled, then the date field will use a hidden field to store the 
17598      * real value as iso formated date. default (false)
17599      */ 
17600     useIso : false,
17601     /**
17602      * @cfg {String/Object} autoCreate
17603      * A DomHelper element spec, or true for a default element spec (defaults to
17604      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17605      */ 
17606     // private
17607     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17608     
17609     // private
17610     hiddenField: false,
17611     
17612     onRender : function(ct, position)
17613     {
17614         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17615         if (this.useIso) {
17616             //this.el.dom.removeAttribute('name'); 
17617             Roo.log("Changing name?");
17618             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17619             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17620                     'before', true);
17621             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17622             // prevent input submission
17623             this.hiddenName = this.name;
17624         }
17625             
17626             
17627     },
17628     
17629     // private
17630     validateValue : function(value)
17631     {
17632         value = this.formatDate(value);
17633         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17634             Roo.log('super failed');
17635             return false;
17636         }
17637         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17638              return true;
17639         }
17640         var svalue = value;
17641         value = this.parseDate(value);
17642         if(!value){
17643             Roo.log('parse date failed' + svalue);
17644             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17645             return false;
17646         }
17647         var time = value.getTime();
17648         if(this.minValue && time < this.minValue.getTime()){
17649             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17650             return false;
17651         }
17652         if(this.maxValue && time > this.maxValue.getTime()){
17653             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17654             return false;
17655         }
17656         if(this.disabledDays){
17657             var day = value.getDay();
17658             for(var i = 0; i < this.disabledDays.length; i++) {
17659                 if(day === this.disabledDays[i]){
17660                     this.markInvalid(this.disabledDaysText);
17661                     return false;
17662                 }
17663             }
17664         }
17665         var fvalue = this.formatDate(value);
17666         if(this.ddMatch && this.ddMatch.test(fvalue)){
17667             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17668             return false;
17669         }
17670         return true;
17671     },
17672
17673     // private
17674     // Provides logic to override the default TriggerField.validateBlur which just returns true
17675     validateBlur : function(){
17676         return !this.menu || !this.menu.isVisible();
17677     },
17678     
17679     getName: function()
17680     {
17681         // returns hidden if it's set..
17682         if (!this.rendered) {return ''};
17683         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17684         
17685     },
17686
17687     /**
17688      * Returns the current date value of the date field.
17689      * @return {Date} The date value
17690      */
17691     getValue : function(){
17692         
17693         return  this.hiddenField ?
17694                 this.hiddenField.value :
17695                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17696     },
17697
17698     /**
17699      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17700      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17701      * (the default format used is "m/d/y").
17702      * <br />Usage:
17703      * <pre><code>
17704 //All of these calls set the same date value (May 4, 2006)
17705
17706 //Pass a date object:
17707 var dt = new Date('5/4/06');
17708 dateField.setValue(dt);
17709
17710 //Pass a date string (default format):
17711 dateField.setValue('5/4/06');
17712
17713 //Pass a date string (custom format):
17714 dateField.format = 'Y-m-d';
17715 dateField.setValue('2006-5-4');
17716 </code></pre>
17717      * @param {String/Date} date The date or valid date string
17718      */
17719     setValue : function(date){
17720         if (this.hiddenField) {
17721             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17722         }
17723         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17724         // make sure the value field is always stored as a date..
17725         this.value = this.parseDate(date);
17726         
17727         
17728     },
17729
17730     // private
17731     parseDate : function(value){
17732         if(!value || value instanceof Date){
17733             return value;
17734         }
17735         var v = Date.parseDate(value, this.format);
17736          if (!v && this.useIso) {
17737             v = Date.parseDate(value, 'Y-m-d');
17738         }
17739         if(!v && this.altFormats){
17740             if(!this.altFormatsArray){
17741                 this.altFormatsArray = this.altFormats.split("|");
17742             }
17743             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17744                 v = Date.parseDate(value, this.altFormatsArray[i]);
17745             }
17746         }
17747         return v;
17748     },
17749
17750     // private
17751     formatDate : function(date, fmt){
17752         return (!date || !(date instanceof Date)) ?
17753                date : date.dateFormat(fmt || this.format);
17754     },
17755
17756     // private
17757     menuListeners : {
17758         select: function(m, d){
17759             
17760             this.setValue(d);
17761             this.fireEvent('select', this, d);
17762         },
17763         show : function(){ // retain focus styling
17764             this.onFocus();
17765         },
17766         hide : function(){
17767             this.focus.defer(10, this);
17768             var ml = this.menuListeners;
17769             this.menu.un("select", ml.select,  this);
17770             this.menu.un("show", ml.show,  this);
17771             this.menu.un("hide", ml.hide,  this);
17772         }
17773     },
17774
17775     // private
17776     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17777     onTriggerClick : function(){
17778         if(this.disabled){
17779             return;
17780         }
17781         if(this.menu == null){
17782             this.menu = new Roo.menu.DateMenu();
17783         }
17784         Roo.apply(this.menu.picker,  {
17785             showClear: this.allowBlank,
17786             minDate : this.minValue,
17787             maxDate : this.maxValue,
17788             disabledDatesRE : this.ddMatch,
17789             disabledDatesText : this.disabledDatesText,
17790             disabledDays : this.disabledDays,
17791             disabledDaysText : this.disabledDaysText,
17792             format : this.useIso ? 'Y-m-d' : this.format,
17793             minText : String.format(this.minText, this.formatDate(this.minValue)),
17794             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17795         });
17796         this.menu.on(Roo.apply({}, this.menuListeners, {
17797             scope:this
17798         }));
17799         this.menu.picker.setValue(this.getValue() || new Date());
17800         this.menu.show(this.el, "tl-bl?");
17801     },
17802
17803     beforeBlur : function(){
17804         var v = this.parseDate(this.getRawValue());
17805         if(v){
17806             this.setValue(v);
17807         }
17808     },
17809
17810     /*@
17811      * overide
17812      * 
17813      */
17814     isDirty : function() {
17815         if(this.disabled) {
17816             return false;
17817         }
17818         
17819         if(typeof(this.startValue) === 'undefined'){
17820             return false;
17821         }
17822         
17823         return String(this.getValue()) !== String(this.startValue);
17824         
17825     },
17826     // @overide
17827     cleanLeadingSpace : function(e)
17828     {
17829        return;
17830     }
17831     
17832 });/*
17833  * Based on:
17834  * Ext JS Library 1.1.1
17835  * Copyright(c) 2006-2007, Ext JS, LLC.
17836  *
17837  * Originally Released Under LGPL - original licence link has changed is not relivant.
17838  *
17839  * Fork - LGPL
17840  * <script type="text/javascript">
17841  */
17842  
17843 /**
17844  * @class Roo.form.MonthField
17845  * @extends Roo.form.TriggerField
17846  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17847 * @constructor
17848 * Create a new MonthField
17849 * @param {Object} config
17850  */
17851 Roo.form.MonthField = function(config){
17852     
17853     Roo.form.MonthField.superclass.constructor.call(this, config);
17854     
17855       this.addEvents({
17856          
17857         /**
17858          * @event select
17859          * Fires when a date is selected
17860              * @param {Roo.form.MonthFieeld} combo This combo box
17861              * @param {Date} date The date selected
17862              */
17863         'select' : true
17864          
17865     });
17866     
17867     
17868     if(typeof this.minValue == "string") {
17869         this.minValue = this.parseDate(this.minValue);
17870     }
17871     if(typeof this.maxValue == "string") {
17872         this.maxValue = this.parseDate(this.maxValue);
17873     }
17874     this.ddMatch = null;
17875     if(this.disabledDates){
17876         var dd = this.disabledDates;
17877         var re = "(?:";
17878         for(var i = 0; i < dd.length; i++){
17879             re += dd[i];
17880             if(i != dd.length-1) {
17881                 re += "|";
17882             }
17883         }
17884         this.ddMatch = new RegExp(re + ")");
17885     }
17886 };
17887
17888 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17889     /**
17890      * @cfg {String} format
17891      * The default date format string which can be overriden for localization support.  The format must be
17892      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17893      */
17894     format : "M Y",
17895     /**
17896      * @cfg {String} altFormats
17897      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17898      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17899      */
17900     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17901     /**
17902      * @cfg {Array} disabledDays
17903      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17904      */
17905     disabledDays : [0,1,2,3,4,5,6],
17906     /**
17907      * @cfg {String} disabledDaysText
17908      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17909      */
17910     disabledDaysText : "Disabled",
17911     /**
17912      * @cfg {Array} disabledDates
17913      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17914      * expression so they are very powerful. Some examples:
17915      * <ul>
17916      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17917      * <li>["03/08", "09/16"] would disable those days for every year</li>
17918      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17919      * <li>["03/../2006"] would disable every day in March 2006</li>
17920      * <li>["^03"] would disable every day in every March</li>
17921      * </ul>
17922      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17923      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17924      */
17925     disabledDates : null,
17926     /**
17927      * @cfg {String} disabledDatesText
17928      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17929      */
17930     disabledDatesText : "Disabled",
17931     /**
17932      * @cfg {Date/String} minValue
17933      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17934      * valid format (defaults to null).
17935      */
17936     minValue : null,
17937     /**
17938      * @cfg {Date/String} maxValue
17939      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17940      * valid format (defaults to null).
17941      */
17942     maxValue : null,
17943     /**
17944      * @cfg {String} minText
17945      * The error text to display when the date in the cell is before minValue (defaults to
17946      * 'The date in this field must be after {minValue}').
17947      */
17948     minText : "The date in this field must be equal to or after {0}",
17949     /**
17950      * @cfg {String} maxTextf
17951      * The error text to display when the date in the cell is after maxValue (defaults to
17952      * 'The date in this field must be before {maxValue}').
17953      */
17954     maxText : "The date in this field must be equal to or before {0}",
17955     /**
17956      * @cfg {String} invalidText
17957      * The error text to display when the date in the field is invalid (defaults to
17958      * '{value} is not a valid date - it must be in the format {format}').
17959      */
17960     invalidText : "{0} is not a valid date - it must be in the format {1}",
17961     /**
17962      * @cfg {String} triggerClass
17963      * An additional CSS class used to style the trigger button.  The trigger will always get the
17964      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17965      * which displays a calendar icon).
17966      */
17967     triggerClass : 'x-form-date-trigger',
17968     
17969
17970     /**
17971      * @cfg {Boolean} useIso
17972      * if enabled, then the date field will use a hidden field to store the 
17973      * real value as iso formated date. default (true)
17974      */ 
17975     useIso : true,
17976     /**
17977      * @cfg {String/Object} autoCreate
17978      * A DomHelper element spec, or true for a default element spec (defaults to
17979      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17980      */ 
17981     // private
17982     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17983     
17984     // private
17985     hiddenField: false,
17986     
17987     hideMonthPicker : false,
17988     
17989     onRender : function(ct, position)
17990     {
17991         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17992         if (this.useIso) {
17993             this.el.dom.removeAttribute('name'); 
17994             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17995                     'before', true);
17996             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17997             // prevent input submission
17998             this.hiddenName = this.name;
17999         }
18000             
18001             
18002     },
18003     
18004     // private
18005     validateValue : function(value)
18006     {
18007         value = this.formatDate(value);
18008         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18009             return false;
18010         }
18011         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18012              return true;
18013         }
18014         var svalue = value;
18015         value = this.parseDate(value);
18016         if(!value){
18017             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18018             return false;
18019         }
18020         var time = value.getTime();
18021         if(this.minValue && time < this.minValue.getTime()){
18022             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18023             return false;
18024         }
18025         if(this.maxValue && time > this.maxValue.getTime()){
18026             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18027             return false;
18028         }
18029         /*if(this.disabledDays){
18030             var day = value.getDay();
18031             for(var i = 0; i < this.disabledDays.length; i++) {
18032                 if(day === this.disabledDays[i]){
18033                     this.markInvalid(this.disabledDaysText);
18034                     return false;
18035                 }
18036             }
18037         }
18038         */
18039         var fvalue = this.formatDate(value);
18040         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18041             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18042             return false;
18043         }
18044         */
18045         return true;
18046     },
18047
18048     // private
18049     // Provides logic to override the default TriggerField.validateBlur which just returns true
18050     validateBlur : function(){
18051         return !this.menu || !this.menu.isVisible();
18052     },
18053
18054     /**
18055      * Returns the current date value of the date field.
18056      * @return {Date} The date value
18057      */
18058     getValue : function(){
18059         
18060         
18061         
18062         return  this.hiddenField ?
18063                 this.hiddenField.value :
18064                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18065     },
18066
18067     /**
18068      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18069      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18070      * (the default format used is "m/d/y").
18071      * <br />Usage:
18072      * <pre><code>
18073 //All of these calls set the same date value (May 4, 2006)
18074
18075 //Pass a date object:
18076 var dt = new Date('5/4/06');
18077 monthField.setValue(dt);
18078
18079 //Pass a date string (default format):
18080 monthField.setValue('5/4/06');
18081
18082 //Pass a date string (custom format):
18083 monthField.format = 'Y-m-d';
18084 monthField.setValue('2006-5-4');
18085 </code></pre>
18086      * @param {String/Date} date The date or valid date string
18087      */
18088     setValue : function(date){
18089         Roo.log('month setValue' + date);
18090         // can only be first of month..
18091         
18092         var val = this.parseDate(date);
18093         
18094         if (this.hiddenField) {
18095             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18096         }
18097         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18098         this.value = this.parseDate(date);
18099     },
18100
18101     // private
18102     parseDate : function(value){
18103         if(!value || value instanceof Date){
18104             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18105             return value;
18106         }
18107         var v = Date.parseDate(value, this.format);
18108         if (!v && this.useIso) {
18109             v = Date.parseDate(value, 'Y-m-d');
18110         }
18111         if (v) {
18112             // 
18113             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18114         }
18115         
18116         
18117         if(!v && this.altFormats){
18118             if(!this.altFormatsArray){
18119                 this.altFormatsArray = this.altFormats.split("|");
18120             }
18121             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18122                 v = Date.parseDate(value, this.altFormatsArray[i]);
18123             }
18124         }
18125         return v;
18126     },
18127
18128     // private
18129     formatDate : function(date, fmt){
18130         return (!date || !(date instanceof Date)) ?
18131                date : date.dateFormat(fmt || this.format);
18132     },
18133
18134     // private
18135     menuListeners : {
18136         select: function(m, d){
18137             this.setValue(d);
18138             this.fireEvent('select', this, d);
18139         },
18140         show : function(){ // retain focus styling
18141             this.onFocus();
18142         },
18143         hide : function(){
18144             this.focus.defer(10, this);
18145             var ml = this.menuListeners;
18146             this.menu.un("select", ml.select,  this);
18147             this.menu.un("show", ml.show,  this);
18148             this.menu.un("hide", ml.hide,  this);
18149         }
18150     },
18151     // private
18152     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18153     onTriggerClick : function(){
18154         if(this.disabled){
18155             return;
18156         }
18157         if(this.menu == null){
18158             this.menu = new Roo.menu.DateMenu();
18159            
18160         }
18161         
18162         Roo.apply(this.menu.picker,  {
18163             
18164             showClear: this.allowBlank,
18165             minDate : this.minValue,
18166             maxDate : this.maxValue,
18167             disabledDatesRE : this.ddMatch,
18168             disabledDatesText : this.disabledDatesText,
18169             
18170             format : this.useIso ? 'Y-m-d' : this.format,
18171             minText : String.format(this.minText, this.formatDate(this.minValue)),
18172             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18173             
18174         });
18175          this.menu.on(Roo.apply({}, this.menuListeners, {
18176             scope:this
18177         }));
18178        
18179         
18180         var m = this.menu;
18181         var p = m.picker;
18182         
18183         // hide month picker get's called when we called by 'before hide';
18184         
18185         var ignorehide = true;
18186         p.hideMonthPicker  = function(disableAnim){
18187             if (ignorehide) {
18188                 return;
18189             }
18190              if(this.monthPicker){
18191                 Roo.log("hideMonthPicker called");
18192                 if(disableAnim === true){
18193                     this.monthPicker.hide();
18194                 }else{
18195                     this.monthPicker.slideOut('t', {duration:.2});
18196                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18197                     p.fireEvent("select", this, this.value);
18198                     m.hide();
18199                 }
18200             }
18201         }
18202         
18203         Roo.log('picker set value');
18204         Roo.log(this.getValue());
18205         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18206         m.show(this.el, 'tl-bl?');
18207         ignorehide  = false;
18208         // this will trigger hideMonthPicker..
18209         
18210         
18211         // hidden the day picker
18212         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18213         
18214         
18215         
18216       
18217         
18218         p.showMonthPicker.defer(100, p);
18219     
18220         
18221        
18222     },
18223
18224     beforeBlur : function(){
18225         var v = this.parseDate(this.getRawValue());
18226         if(v){
18227             this.setValue(v);
18228         }
18229     }
18230
18231     /** @cfg {Boolean} grow @hide */
18232     /** @cfg {Number} growMin @hide */
18233     /** @cfg {Number} growMax @hide */
18234     /**
18235      * @hide
18236      * @method autoSize
18237      */
18238 });/*
18239  * Based on:
18240  * Ext JS Library 1.1.1
18241  * Copyright(c) 2006-2007, Ext JS, LLC.
18242  *
18243  * Originally Released Under LGPL - original licence link has changed is not relivant.
18244  *
18245  * Fork - LGPL
18246  * <script type="text/javascript">
18247  */
18248  
18249
18250 /**
18251  * @class Roo.form.ComboBox
18252  * @extends Roo.form.TriggerField
18253  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18254  * @constructor
18255  * Create a new ComboBox.
18256  * @param {Object} config Configuration options
18257  */
18258 Roo.form.ComboBox = function(config){
18259     Roo.form.ComboBox.superclass.constructor.call(this, config);
18260     this.addEvents({
18261         /**
18262          * @event expand
18263          * Fires when the dropdown list is expanded
18264              * @param {Roo.form.ComboBox} combo This combo box
18265              */
18266         'expand' : true,
18267         /**
18268          * @event collapse
18269          * Fires when the dropdown list is collapsed
18270              * @param {Roo.form.ComboBox} combo This combo box
18271              */
18272         'collapse' : true,
18273         /**
18274          * @event beforeselect
18275          * Fires before a list item is selected. Return false to cancel the selection.
18276              * @param {Roo.form.ComboBox} combo This combo box
18277              * @param {Roo.data.Record} record The data record returned from the underlying store
18278              * @param {Number} index The index of the selected item in the dropdown list
18279              */
18280         'beforeselect' : true,
18281         /**
18282          * @event select
18283          * Fires when a list item is selected
18284              * @param {Roo.form.ComboBox} combo This combo box
18285              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18286              * @param {Number} index The index of the selected item in the dropdown list
18287              */
18288         'select' : true,
18289         /**
18290          * @event beforequery
18291          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18292          * The event object passed has these properties:
18293              * @param {Roo.form.ComboBox} combo This combo box
18294              * @param {String} query The query
18295              * @param {Boolean} forceAll true to force "all" query
18296              * @param {Boolean} cancel true to cancel the query
18297              * @param {Object} e The query event object
18298              */
18299         'beforequery': true,
18300          /**
18301          * @event add
18302          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18303              * @param {Roo.form.ComboBox} combo This combo box
18304              */
18305         'add' : true,
18306         /**
18307          * @event edit
18308          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18309              * @param {Roo.form.ComboBox} combo This combo box
18310              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18311              */
18312         'edit' : true
18313         
18314         
18315     });
18316     if(this.transform){
18317         this.allowDomMove = false;
18318         var s = Roo.getDom(this.transform);
18319         if(!this.hiddenName){
18320             this.hiddenName = s.name;
18321         }
18322         if(!this.store){
18323             this.mode = 'local';
18324             var d = [], opts = s.options;
18325             for(var i = 0, len = opts.length;i < len; i++){
18326                 var o = opts[i];
18327                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18328                 if(o.selected) {
18329                     this.value = value;
18330                 }
18331                 d.push([value, o.text]);
18332             }
18333             this.store = new Roo.data.SimpleStore({
18334                 'id': 0,
18335                 fields: ['value', 'text'],
18336                 data : d
18337             });
18338             this.valueField = 'value';
18339             this.displayField = 'text';
18340         }
18341         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18342         if(!this.lazyRender){
18343             this.target = true;
18344             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18345             s.parentNode.removeChild(s); // remove it
18346             this.render(this.el.parentNode);
18347         }else{
18348             s.parentNode.removeChild(s); // remove it
18349         }
18350
18351     }
18352     if (this.store) {
18353         this.store = Roo.factory(this.store, Roo.data);
18354     }
18355     
18356     this.selectedIndex = -1;
18357     if(this.mode == 'local'){
18358         if(config.queryDelay === undefined){
18359             this.queryDelay = 10;
18360         }
18361         if(config.minChars === undefined){
18362             this.minChars = 0;
18363         }
18364     }
18365 };
18366
18367 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18368     /**
18369      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18370      */
18371     /**
18372      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18373      * rendering into an Roo.Editor, defaults to false)
18374      */
18375     /**
18376      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18377      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18378      */
18379     /**
18380      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18381      */
18382     /**
18383      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18384      * the dropdown list (defaults to undefined, with no header element)
18385      */
18386
18387      /**
18388      * @cfg {String/Roo.Template} tpl The template to use to render the output
18389      */
18390      
18391     // private
18392     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18393     /**
18394      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18395      */
18396     listWidth: undefined,
18397     /**
18398      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18399      * mode = 'remote' or 'text' if mode = 'local')
18400      */
18401     displayField: undefined,
18402     /**
18403      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18404      * mode = 'remote' or 'value' if mode = 'local'). 
18405      * Note: use of a valueField requires the user make a selection
18406      * in order for a value to be mapped.
18407      */
18408     valueField: undefined,
18409     
18410     
18411     /**
18412      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18413      * field's data value (defaults to the underlying DOM element's name)
18414      */
18415     hiddenName: undefined,
18416     /**
18417      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18418      */
18419     listClass: '',
18420     /**
18421      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18422      */
18423     selectedClass: 'x-combo-selected',
18424     /**
18425      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18426      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18427      * which displays a downward arrow icon).
18428      */
18429     triggerClass : 'x-form-arrow-trigger',
18430     /**
18431      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18432      */
18433     shadow:'sides',
18434     /**
18435      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18436      * anchor positions (defaults to 'tl-bl')
18437      */
18438     listAlign: 'tl-bl?',
18439     /**
18440      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18441      */
18442     maxHeight: 300,
18443     /**
18444      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18445      * query specified by the allQuery config option (defaults to 'query')
18446      */
18447     triggerAction: 'query',
18448     /**
18449      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18450      * (defaults to 4, does not apply if editable = false)
18451      */
18452     minChars : 4,
18453     /**
18454      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18455      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18456      */
18457     typeAhead: false,
18458     /**
18459      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18460      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18461      */
18462     queryDelay: 500,
18463     /**
18464      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18465      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18466      */
18467     pageSize: 0,
18468     /**
18469      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18470      * when editable = true (defaults to false)
18471      */
18472     selectOnFocus:false,
18473     /**
18474      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18475      */
18476     queryParam: 'query',
18477     /**
18478      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18479      * when mode = 'remote' (defaults to 'Loading...')
18480      */
18481     loadingText: 'Loading...',
18482     /**
18483      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18484      */
18485     resizable: false,
18486     /**
18487      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18488      */
18489     handleHeight : 8,
18490     /**
18491      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18492      * traditional select (defaults to true)
18493      */
18494     editable: true,
18495     /**
18496      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18497      */
18498     allQuery: '',
18499     /**
18500      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18501      */
18502     mode: 'remote',
18503     /**
18504      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18505      * listWidth has a higher value)
18506      */
18507     minListWidth : 70,
18508     /**
18509      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18510      * allow the user to set arbitrary text into the field (defaults to false)
18511      */
18512     forceSelection:false,
18513     /**
18514      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18515      * if typeAhead = true (defaults to 250)
18516      */
18517     typeAheadDelay : 250,
18518     /**
18519      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18520      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18521      */
18522     valueNotFoundText : undefined,
18523     /**
18524      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18525      */
18526     blockFocus : false,
18527     
18528     /**
18529      * @cfg {Boolean} disableClear Disable showing of clear button.
18530      */
18531     disableClear : false,
18532     /**
18533      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18534      */
18535     alwaysQuery : false,
18536     
18537     //private
18538     addicon : false,
18539     editicon: false,
18540     
18541     // element that contains real text value.. (when hidden is used..)
18542      
18543     // private
18544     onRender : function(ct, position)
18545     {
18546         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18547         
18548         if(this.hiddenName){
18549             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18550                     'before', true);
18551             this.hiddenField.value =
18552                 this.hiddenValue !== undefined ? this.hiddenValue :
18553                 this.value !== undefined ? this.value : '';
18554
18555             // prevent input submission
18556             this.el.dom.removeAttribute('name');
18557              
18558              
18559         }
18560         
18561         if(Roo.isGecko){
18562             this.el.dom.setAttribute('autocomplete', 'off');
18563         }
18564
18565         var cls = 'x-combo-list';
18566
18567         this.list = new Roo.Layer({
18568             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18569         });
18570
18571         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18572         this.list.setWidth(lw);
18573         this.list.swallowEvent('mousewheel');
18574         this.assetHeight = 0;
18575
18576         if(this.title){
18577             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18578             this.assetHeight += this.header.getHeight();
18579         }
18580
18581         this.innerList = this.list.createChild({cls:cls+'-inner'});
18582         this.innerList.on('mouseover', this.onViewOver, this);
18583         this.innerList.on('mousemove', this.onViewMove, this);
18584         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18585         
18586         if(this.allowBlank && !this.pageSize && !this.disableClear){
18587             this.footer = this.list.createChild({cls:cls+'-ft'});
18588             this.pageTb = new Roo.Toolbar(this.footer);
18589            
18590         }
18591         if(this.pageSize){
18592             this.footer = this.list.createChild({cls:cls+'-ft'});
18593             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18594                     {pageSize: this.pageSize});
18595             
18596         }
18597         
18598         if (this.pageTb && this.allowBlank && !this.disableClear) {
18599             var _this = this;
18600             this.pageTb.add(new Roo.Toolbar.Fill(), {
18601                 cls: 'x-btn-icon x-btn-clear',
18602                 text: '&#160;',
18603                 handler: function()
18604                 {
18605                     _this.collapse();
18606                     _this.clearValue();
18607                     _this.onSelect(false, -1);
18608                 }
18609             });
18610         }
18611         if (this.footer) {
18612             this.assetHeight += this.footer.getHeight();
18613         }
18614         
18615
18616         if(!this.tpl){
18617             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18618         }
18619
18620         this.view = new Roo.View(this.innerList, this.tpl, {
18621             singleSelect:true,
18622             store: this.store,
18623             selectedClass: this.selectedClass
18624         });
18625
18626         this.view.on('click', this.onViewClick, this);
18627
18628         this.store.on('beforeload', this.onBeforeLoad, this);
18629         this.store.on('load', this.onLoad, this);
18630         this.store.on('loadexception', this.onLoadException, this);
18631
18632         if(this.resizable){
18633             this.resizer = new Roo.Resizable(this.list,  {
18634                pinned:true, handles:'se'
18635             });
18636             this.resizer.on('resize', function(r, w, h){
18637                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18638                 this.listWidth = w;
18639                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18640                 this.restrictHeight();
18641             }, this);
18642             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18643         }
18644         if(!this.editable){
18645             this.editable = true;
18646             this.setEditable(false);
18647         }  
18648         
18649         
18650         if (typeof(this.events.add.listeners) != 'undefined') {
18651             
18652             this.addicon = this.wrap.createChild(
18653                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18654        
18655             this.addicon.on('click', function(e) {
18656                 this.fireEvent('add', this);
18657             }, this);
18658         }
18659         if (typeof(this.events.edit.listeners) != 'undefined') {
18660             
18661             this.editicon = this.wrap.createChild(
18662                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18663             if (this.addicon) {
18664                 this.editicon.setStyle('margin-left', '40px');
18665             }
18666             this.editicon.on('click', function(e) {
18667                 
18668                 // we fire even  if inothing is selected..
18669                 this.fireEvent('edit', this, this.lastData );
18670                 
18671             }, this);
18672         }
18673         
18674         
18675         
18676     },
18677
18678     // private
18679     initEvents : function(){
18680         Roo.form.ComboBox.superclass.initEvents.call(this);
18681
18682         this.keyNav = new Roo.KeyNav(this.el, {
18683             "up" : function(e){
18684                 this.inKeyMode = true;
18685                 this.selectPrev();
18686             },
18687
18688             "down" : function(e){
18689                 if(!this.isExpanded()){
18690                     this.onTriggerClick();
18691                 }else{
18692                     this.inKeyMode = true;
18693                     this.selectNext();
18694                 }
18695             },
18696
18697             "enter" : function(e){
18698                 this.onViewClick();
18699                 //return true;
18700             },
18701
18702             "esc" : function(e){
18703                 this.collapse();
18704             },
18705
18706             "tab" : function(e){
18707                 this.onViewClick(false);
18708                 this.fireEvent("specialkey", this, e);
18709                 return true;
18710             },
18711
18712             scope : this,
18713
18714             doRelay : function(foo, bar, hname){
18715                 if(hname == 'down' || this.scope.isExpanded()){
18716                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18717                 }
18718                 return true;
18719             },
18720
18721             forceKeyDown: true
18722         });
18723         this.queryDelay = Math.max(this.queryDelay || 10,
18724                 this.mode == 'local' ? 10 : 250);
18725         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18726         if(this.typeAhead){
18727             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18728         }
18729         if(this.editable !== false){
18730             this.el.on("keyup", this.onKeyUp, this);
18731         }
18732         if(this.forceSelection){
18733             this.on('blur', this.doForce, this);
18734         }
18735     },
18736
18737     onDestroy : function(){
18738         if(this.view){
18739             this.view.setStore(null);
18740             this.view.el.removeAllListeners();
18741             this.view.el.remove();
18742             this.view.purgeListeners();
18743         }
18744         if(this.list){
18745             this.list.destroy();
18746         }
18747         if(this.store){
18748             this.store.un('beforeload', this.onBeforeLoad, this);
18749             this.store.un('load', this.onLoad, this);
18750             this.store.un('loadexception', this.onLoadException, this);
18751         }
18752         Roo.form.ComboBox.superclass.onDestroy.call(this);
18753     },
18754
18755     // private
18756     fireKey : function(e){
18757         if(e.isNavKeyPress() && !this.list.isVisible()){
18758             this.fireEvent("specialkey", this, e);
18759         }
18760     },
18761
18762     // private
18763     onResize: function(w, h){
18764         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18765         
18766         if(typeof w != 'number'){
18767             // we do not handle it!?!?
18768             return;
18769         }
18770         var tw = this.trigger.getWidth();
18771         tw += this.addicon ? this.addicon.getWidth() : 0;
18772         tw += this.editicon ? this.editicon.getWidth() : 0;
18773         var x = w - tw;
18774         this.el.setWidth( this.adjustWidth('input', x));
18775             
18776         this.trigger.setStyle('left', x+'px');
18777         
18778         if(this.list && this.listWidth === undefined){
18779             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18780             this.list.setWidth(lw);
18781             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18782         }
18783         
18784     
18785         
18786     },
18787
18788     /**
18789      * Allow or prevent the user from directly editing the field text.  If false is passed,
18790      * the user will only be able to select from the items defined in the dropdown list.  This method
18791      * is the runtime equivalent of setting the 'editable' config option at config time.
18792      * @param {Boolean} value True to allow the user to directly edit the field text
18793      */
18794     setEditable : function(value){
18795         if(value == this.editable){
18796             return;
18797         }
18798         this.editable = value;
18799         if(!value){
18800             this.el.dom.setAttribute('readOnly', true);
18801             this.el.on('mousedown', this.onTriggerClick,  this);
18802             this.el.addClass('x-combo-noedit');
18803         }else{
18804             this.el.dom.setAttribute('readOnly', false);
18805             this.el.un('mousedown', this.onTriggerClick,  this);
18806             this.el.removeClass('x-combo-noedit');
18807         }
18808     },
18809
18810     // private
18811     onBeforeLoad : function(){
18812         if(!this.hasFocus){
18813             return;
18814         }
18815         this.innerList.update(this.loadingText ?
18816                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18817         this.restrictHeight();
18818         this.selectedIndex = -1;
18819     },
18820
18821     // private
18822     onLoad : function(){
18823         if(!this.hasFocus){
18824             return;
18825         }
18826         if(this.store.getCount() > 0){
18827             this.expand();
18828             this.restrictHeight();
18829             if(this.lastQuery == this.allQuery){
18830                 if(this.editable){
18831                     this.el.dom.select();
18832                 }
18833                 if(!this.selectByValue(this.value, true)){
18834                     this.select(0, true);
18835                 }
18836             }else{
18837                 this.selectNext();
18838                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18839                     this.taTask.delay(this.typeAheadDelay);
18840                 }
18841             }
18842         }else{
18843             this.onEmptyResults();
18844         }
18845         //this.el.focus();
18846     },
18847     // private
18848     onLoadException : function()
18849     {
18850         this.collapse();
18851         Roo.log(this.store.reader.jsonData);
18852         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18853             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18854         }
18855         
18856         
18857     },
18858     // private
18859     onTypeAhead : function(){
18860         if(this.store.getCount() > 0){
18861             var r = this.store.getAt(0);
18862             var newValue = r.data[this.displayField];
18863             var len = newValue.length;
18864             var selStart = this.getRawValue().length;
18865             if(selStart != len){
18866                 this.setRawValue(newValue);
18867                 this.selectText(selStart, newValue.length);
18868             }
18869         }
18870     },
18871
18872     // private
18873     onSelect : function(record, index){
18874         if(this.fireEvent('beforeselect', this, record, index) !== false){
18875             this.setFromData(index > -1 ? record.data : false);
18876             this.collapse();
18877             this.fireEvent('select', this, record, index);
18878         }
18879     },
18880
18881     /**
18882      * Returns the currently selected field value or empty string if no value is set.
18883      * @return {String} value The selected value
18884      */
18885     getValue : function(){
18886         if(this.valueField){
18887             return typeof this.value != 'undefined' ? this.value : '';
18888         }
18889         return Roo.form.ComboBox.superclass.getValue.call(this);
18890     },
18891
18892     /**
18893      * Clears any text/value currently set in the field
18894      */
18895     clearValue : function(){
18896         if(this.hiddenField){
18897             this.hiddenField.value = '';
18898         }
18899         this.value = '';
18900         this.setRawValue('');
18901         this.lastSelectionText = '';
18902         
18903     },
18904
18905     /**
18906      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18907      * will be displayed in the field.  If the value does not match the data value of an existing item,
18908      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18909      * Otherwise the field will be blank (although the value will still be set).
18910      * @param {String} value The value to match
18911      */
18912     setValue : function(v){
18913         var text = v;
18914         if(this.valueField){
18915             var r = this.findRecord(this.valueField, v);
18916             if(r){
18917                 text = r.data[this.displayField];
18918             }else if(this.valueNotFoundText !== undefined){
18919                 text = this.valueNotFoundText;
18920             }
18921         }
18922         this.lastSelectionText = text;
18923         if(this.hiddenField){
18924             this.hiddenField.value = v;
18925         }
18926         Roo.form.ComboBox.superclass.setValue.call(this, text);
18927         this.value = v;
18928     },
18929     /**
18930      * @property {Object} the last set data for the element
18931      */
18932     
18933     lastData : false,
18934     /**
18935      * Sets the value of the field based on a object which is related to the record format for the store.
18936      * @param {Object} value the value to set as. or false on reset?
18937      */
18938     setFromData : function(o){
18939         var dv = ''; // display value
18940         var vv = ''; // value value..
18941         this.lastData = o;
18942         if (this.displayField) {
18943             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18944         } else {
18945             // this is an error condition!!!
18946             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18947         }
18948         
18949         if(this.valueField){
18950             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18951         }
18952         if(this.hiddenField){
18953             this.hiddenField.value = vv;
18954             
18955             this.lastSelectionText = dv;
18956             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18957             this.value = vv;
18958             return;
18959         }
18960         // no hidden field.. - we store the value in 'value', but still display
18961         // display field!!!!
18962         this.lastSelectionText = dv;
18963         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18964         this.value = vv;
18965         
18966         
18967     },
18968     // private
18969     reset : function(){
18970         // overridden so that last data is reset..
18971         this.setValue(this.resetValue);
18972         this.originalValue = this.getValue();
18973         this.clearInvalid();
18974         this.lastData = false;
18975         if (this.view) {
18976             this.view.clearSelections();
18977         }
18978     },
18979     // private
18980     findRecord : function(prop, value){
18981         var record;
18982         if(this.store.getCount() > 0){
18983             this.store.each(function(r){
18984                 if(r.data[prop] == value){
18985                     record = r;
18986                     return false;
18987                 }
18988                 return true;
18989             });
18990         }
18991         return record;
18992     },
18993     
18994     getName: function()
18995     {
18996         // returns hidden if it's set..
18997         if (!this.rendered) {return ''};
18998         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18999         
19000     },
19001     // private
19002     onViewMove : function(e, t){
19003         this.inKeyMode = false;
19004     },
19005
19006     // private
19007     onViewOver : function(e, t){
19008         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19009             return;
19010         }
19011         var item = this.view.findItemFromChild(t);
19012         if(item){
19013             var index = this.view.indexOf(item);
19014             this.select(index, false);
19015         }
19016     },
19017
19018     // private
19019     onViewClick : function(doFocus)
19020     {
19021         var index = this.view.getSelectedIndexes()[0];
19022         var r = this.store.getAt(index);
19023         if(r){
19024             this.onSelect(r, index);
19025         }
19026         if(doFocus !== false && !this.blockFocus){
19027             this.el.focus();
19028         }
19029     },
19030
19031     // private
19032     restrictHeight : function(){
19033         this.innerList.dom.style.height = '';
19034         var inner = this.innerList.dom;
19035         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19036         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19037         this.list.beginUpdate();
19038         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19039         this.list.alignTo(this.el, this.listAlign);
19040         this.list.endUpdate();
19041     },
19042
19043     // private
19044     onEmptyResults : function(){
19045         this.collapse();
19046     },
19047
19048     /**
19049      * Returns true if the dropdown list is expanded, else false.
19050      */
19051     isExpanded : function(){
19052         return this.list.isVisible();
19053     },
19054
19055     /**
19056      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19057      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19058      * @param {String} value The data value of the item to select
19059      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19060      * selected item if it is not currently in view (defaults to true)
19061      * @return {Boolean} True if the value matched an item in the list, else false
19062      */
19063     selectByValue : function(v, scrollIntoView){
19064         if(v !== undefined && v !== null){
19065             var r = this.findRecord(this.valueField || this.displayField, v);
19066             if(r){
19067                 this.select(this.store.indexOf(r), scrollIntoView);
19068                 return true;
19069             }
19070         }
19071         return false;
19072     },
19073
19074     /**
19075      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19076      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19077      * @param {Number} index The zero-based index of the list item to select
19078      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19079      * selected item if it is not currently in view (defaults to true)
19080      */
19081     select : function(index, scrollIntoView){
19082         this.selectedIndex = index;
19083         this.view.select(index);
19084         if(scrollIntoView !== false){
19085             var el = this.view.getNode(index);
19086             if(el){
19087                 this.innerList.scrollChildIntoView(el, false);
19088             }
19089         }
19090     },
19091
19092     // private
19093     selectNext : function(){
19094         var ct = this.store.getCount();
19095         if(ct > 0){
19096             if(this.selectedIndex == -1){
19097                 this.select(0);
19098             }else if(this.selectedIndex < ct-1){
19099                 this.select(this.selectedIndex+1);
19100             }
19101         }
19102     },
19103
19104     // private
19105     selectPrev : function(){
19106         var ct = this.store.getCount();
19107         if(ct > 0){
19108             if(this.selectedIndex == -1){
19109                 this.select(0);
19110             }else if(this.selectedIndex != 0){
19111                 this.select(this.selectedIndex-1);
19112             }
19113         }
19114     },
19115
19116     // private
19117     onKeyUp : function(e){
19118         if(this.editable !== false && !e.isSpecialKey()){
19119             this.lastKey = e.getKey();
19120             this.dqTask.delay(this.queryDelay);
19121         }
19122     },
19123
19124     // private
19125     validateBlur : function(){
19126         return !this.list || !this.list.isVisible();   
19127     },
19128
19129     // private
19130     initQuery : function(){
19131         this.doQuery(this.getRawValue());
19132     },
19133
19134     // private
19135     doForce : function(){
19136         if(this.el.dom.value.length > 0){
19137             this.el.dom.value =
19138                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19139              
19140         }
19141     },
19142
19143     /**
19144      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19145      * query allowing the query action to be canceled if needed.
19146      * @param {String} query The SQL query to execute
19147      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19148      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19149      * saved in the current store (defaults to false)
19150      */
19151     doQuery : function(q, forceAll){
19152         if(q === undefined || q === null){
19153             q = '';
19154         }
19155         var qe = {
19156             query: q,
19157             forceAll: forceAll,
19158             combo: this,
19159             cancel:false
19160         };
19161         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19162             return false;
19163         }
19164         q = qe.query;
19165         forceAll = qe.forceAll;
19166         if(forceAll === true || (q.length >= this.minChars)){
19167             if(this.lastQuery != q || this.alwaysQuery){
19168                 this.lastQuery = q;
19169                 if(this.mode == 'local'){
19170                     this.selectedIndex = -1;
19171                     if(forceAll){
19172                         this.store.clearFilter();
19173                     }else{
19174                         this.store.filter(this.displayField, q);
19175                     }
19176                     this.onLoad();
19177                 }else{
19178                     this.store.baseParams[this.queryParam] = q;
19179                     this.store.load({
19180                         params: this.getParams(q)
19181                     });
19182                     this.expand();
19183                 }
19184             }else{
19185                 this.selectedIndex = -1;
19186                 this.onLoad();   
19187             }
19188         }
19189     },
19190
19191     // private
19192     getParams : function(q){
19193         var p = {};
19194         //p[this.queryParam] = q;
19195         if(this.pageSize){
19196             p.start = 0;
19197             p.limit = this.pageSize;
19198         }
19199         return p;
19200     },
19201
19202     /**
19203      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19204      */
19205     collapse : function(){
19206         if(!this.isExpanded()){
19207             return;
19208         }
19209         this.list.hide();
19210         Roo.get(document).un('mousedown', this.collapseIf, this);
19211         Roo.get(document).un('mousewheel', this.collapseIf, this);
19212         if (!this.editable) {
19213             Roo.get(document).un('keydown', this.listKeyPress, this);
19214         }
19215         this.fireEvent('collapse', this);
19216     },
19217
19218     // private
19219     collapseIf : function(e){
19220         if(!e.within(this.wrap) && !e.within(this.list)){
19221             this.collapse();
19222         }
19223     },
19224
19225     /**
19226      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19227      */
19228     expand : function(){
19229         if(this.isExpanded() || !this.hasFocus){
19230             return;
19231         }
19232         this.list.alignTo(this.el, this.listAlign);
19233         this.list.show();
19234         Roo.get(document).on('mousedown', this.collapseIf, this);
19235         Roo.get(document).on('mousewheel', this.collapseIf, this);
19236         if (!this.editable) {
19237             Roo.get(document).on('keydown', this.listKeyPress, this);
19238         }
19239         
19240         this.fireEvent('expand', this);
19241     },
19242
19243     // private
19244     // Implements the default empty TriggerField.onTriggerClick function
19245     onTriggerClick : function(){
19246         if(this.disabled){
19247             return;
19248         }
19249         if(this.isExpanded()){
19250             this.collapse();
19251             if (!this.blockFocus) {
19252                 this.el.focus();
19253             }
19254             
19255         }else {
19256             this.hasFocus = true;
19257             if(this.triggerAction == 'all') {
19258                 this.doQuery(this.allQuery, true);
19259             } else {
19260                 this.doQuery(this.getRawValue());
19261             }
19262             if (!this.blockFocus) {
19263                 this.el.focus();
19264             }
19265         }
19266     },
19267     listKeyPress : function(e)
19268     {
19269         //Roo.log('listkeypress');
19270         // scroll to first matching element based on key pres..
19271         if (e.isSpecialKey()) {
19272             return false;
19273         }
19274         var k = String.fromCharCode(e.getKey()).toUpperCase();
19275         //Roo.log(k);
19276         var match  = false;
19277         var csel = this.view.getSelectedNodes();
19278         var cselitem = false;
19279         if (csel.length) {
19280             var ix = this.view.indexOf(csel[0]);
19281             cselitem  = this.store.getAt(ix);
19282             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19283                 cselitem = false;
19284             }
19285             
19286         }
19287         
19288         this.store.each(function(v) { 
19289             if (cselitem) {
19290                 // start at existing selection.
19291                 if (cselitem.id == v.id) {
19292                     cselitem = false;
19293                 }
19294                 return;
19295             }
19296                 
19297             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19298                 match = this.store.indexOf(v);
19299                 return false;
19300             }
19301         }, this);
19302         
19303         if (match === false) {
19304             return true; // no more action?
19305         }
19306         // scroll to?
19307         this.view.select(match);
19308         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19309         sn.scrollIntoView(sn.dom.parentNode, false);
19310     } 
19311
19312     /** 
19313     * @cfg {Boolean} grow 
19314     * @hide 
19315     */
19316     /** 
19317     * @cfg {Number} growMin 
19318     * @hide 
19319     */
19320     /** 
19321     * @cfg {Number} growMax 
19322     * @hide 
19323     */
19324     /**
19325      * @hide
19326      * @method autoSize
19327      */
19328 });/*
19329  * Copyright(c) 2010-2012, Roo J Solutions Limited
19330  *
19331  * Licence LGPL
19332  *
19333  */
19334
19335 /**
19336  * @class Roo.form.ComboBoxArray
19337  * @extends Roo.form.TextField
19338  * A facebook style adder... for lists of email / people / countries  etc...
19339  * pick multiple items from a combo box, and shows each one.
19340  *
19341  *  Fred [x]  Brian [x]  [Pick another |v]
19342  *
19343  *
19344  *  For this to work: it needs various extra information
19345  *    - normal combo problay has
19346  *      name, hiddenName
19347  *    + displayField, valueField
19348  *
19349  *    For our purpose...
19350  *
19351  *
19352  *   If we change from 'extends' to wrapping...
19353  *   
19354  *  
19355  *
19356  
19357  
19358  * @constructor
19359  * Create a new ComboBoxArray.
19360  * @param {Object} config Configuration options
19361  */
19362  
19363
19364 Roo.form.ComboBoxArray = function(config)
19365 {
19366     this.addEvents({
19367         /**
19368          * @event beforeremove
19369          * Fires before remove the value from the list
19370              * @param {Roo.form.ComboBoxArray} _self This combo box array
19371              * @param {Roo.form.ComboBoxArray.Item} item removed item
19372              */
19373         'beforeremove' : true,
19374         /**
19375          * @event remove
19376          * Fires when remove the value from the list
19377              * @param {Roo.form.ComboBoxArray} _self This combo box array
19378              * @param {Roo.form.ComboBoxArray.Item} item removed item
19379              */
19380         'remove' : true
19381         
19382         
19383     });
19384     
19385     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19386     
19387     this.items = new Roo.util.MixedCollection(false);
19388     
19389     // construct the child combo...
19390     
19391     
19392     
19393     
19394    
19395     
19396 }
19397
19398  
19399 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19400
19401     /**
19402      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19403      */
19404     
19405     lastData : false,
19406     
19407     // behavies liek a hiddne field
19408     inputType:      'hidden',
19409     /**
19410      * @cfg {Number} width The width of the box that displays the selected element
19411      */ 
19412     width:          300,
19413
19414     
19415     
19416     /**
19417      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19418      */
19419     name : false,
19420     /**
19421      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19422      */
19423     hiddenName : false,
19424       /**
19425      * @cfg {String} seperator    The value seperator normally ',' 
19426      */
19427     seperator : ',',
19428     
19429     // private the array of items that are displayed..
19430     items  : false,
19431     // private - the hidden field el.
19432     hiddenEl : false,
19433     // private - the filed el..
19434     el : false,
19435     
19436     //validateValue : function() { return true; }, // all values are ok!
19437     //onAddClick: function() { },
19438     
19439     onRender : function(ct, position) 
19440     {
19441         
19442         // create the standard hidden element
19443         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19444         
19445         
19446         // give fake names to child combo;
19447         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19448         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19449         
19450         this.combo = Roo.factory(this.combo, Roo.form);
19451         this.combo.onRender(ct, position);
19452         if (typeof(this.combo.width) != 'undefined') {
19453             this.combo.onResize(this.combo.width,0);
19454         }
19455         
19456         this.combo.initEvents();
19457         
19458         // assigned so form know we need to do this..
19459         this.store          = this.combo.store;
19460         this.valueField     = this.combo.valueField;
19461         this.displayField   = this.combo.displayField ;
19462         
19463         
19464         this.combo.wrap.addClass('x-cbarray-grp');
19465         
19466         var cbwrap = this.combo.wrap.createChild(
19467             {tag: 'div', cls: 'x-cbarray-cb'},
19468             this.combo.el.dom
19469         );
19470         
19471              
19472         this.hiddenEl = this.combo.wrap.createChild({
19473             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19474         });
19475         this.el = this.combo.wrap.createChild({
19476             tag: 'input',  type:'hidden' , name: this.name, value : ''
19477         });
19478          //   this.el.dom.removeAttribute("name");
19479         
19480         
19481         this.outerWrap = this.combo.wrap;
19482         this.wrap = cbwrap;
19483         
19484         this.outerWrap.setWidth(this.width);
19485         this.outerWrap.dom.removeChild(this.el.dom);
19486         
19487         this.wrap.dom.appendChild(this.el.dom);
19488         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19489         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19490         
19491         this.combo.trigger.setStyle('position','relative');
19492         this.combo.trigger.setStyle('left', '0px');
19493         this.combo.trigger.setStyle('top', '2px');
19494         
19495         this.combo.el.setStyle('vertical-align', 'text-bottom');
19496         
19497         //this.trigger.setStyle('vertical-align', 'top');
19498         
19499         // this should use the code from combo really... on('add' ....)
19500         if (this.adder) {
19501             
19502         
19503             this.adder = this.outerWrap.createChild(
19504                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19505             var _t = this;
19506             this.adder.on('click', function(e) {
19507                 _t.fireEvent('adderclick', this, e);
19508             }, _t);
19509         }
19510         //var _t = this;
19511         //this.adder.on('click', this.onAddClick, _t);
19512         
19513         
19514         this.combo.on('select', function(cb, rec, ix) {
19515             this.addItem(rec.data);
19516             
19517             cb.setValue('');
19518             cb.el.dom.value = '';
19519             //cb.lastData = rec.data;
19520             // add to list
19521             
19522         }, this);
19523         
19524         
19525     },
19526     
19527     
19528     getName: function()
19529     {
19530         // returns hidden if it's set..
19531         if (!this.rendered) {return ''};
19532         return  this.hiddenName ? this.hiddenName : this.name;
19533         
19534     },
19535     
19536     
19537     onResize: function(w, h){
19538         
19539         return;
19540         // not sure if this is needed..
19541         //this.combo.onResize(w,h);
19542         
19543         if(typeof w != 'number'){
19544             // we do not handle it!?!?
19545             return;
19546         }
19547         var tw = this.combo.trigger.getWidth();
19548         tw += this.addicon ? this.addicon.getWidth() : 0;
19549         tw += this.editicon ? this.editicon.getWidth() : 0;
19550         var x = w - tw;
19551         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19552             
19553         this.combo.trigger.setStyle('left', '0px');
19554         
19555         if(this.list && this.listWidth === undefined){
19556             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19557             this.list.setWidth(lw);
19558             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19559         }
19560         
19561     
19562         
19563     },
19564     
19565     addItem: function(rec)
19566     {
19567         var valueField = this.combo.valueField;
19568         var displayField = this.combo.displayField;
19569         
19570         if (this.items.indexOfKey(rec[valueField]) > -1) {
19571             //console.log("GOT " + rec.data.id);
19572             return;
19573         }
19574         
19575         var x = new Roo.form.ComboBoxArray.Item({
19576             //id : rec[this.idField],
19577             data : rec,
19578             displayField : displayField ,
19579             tipField : displayField ,
19580             cb : this
19581         });
19582         // use the 
19583         this.items.add(rec[valueField],x);
19584         // add it before the element..
19585         this.updateHiddenEl();
19586         x.render(this.outerWrap, this.wrap.dom);
19587         // add the image handler..
19588     },
19589     
19590     updateHiddenEl : function()
19591     {
19592         this.validate();
19593         if (!this.hiddenEl) {
19594             return;
19595         }
19596         var ar = [];
19597         var idField = this.combo.valueField;
19598         
19599         this.items.each(function(f) {
19600             ar.push(f.data[idField]);
19601         });
19602         this.hiddenEl.dom.value = ar.join(this.seperator);
19603         this.validate();
19604     },
19605     
19606     reset : function()
19607     {
19608         this.items.clear();
19609         
19610         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19611            el.remove();
19612         });
19613         
19614         this.el.dom.value = '';
19615         if (this.hiddenEl) {
19616             this.hiddenEl.dom.value = '';
19617         }
19618         
19619     },
19620     getValue: function()
19621     {
19622         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19623     },
19624     setValue: function(v) // not a valid action - must use addItems..
19625     {
19626         
19627         this.reset();
19628          
19629         if (this.store.isLocal && (typeof(v) == 'string')) {
19630             // then we can use the store to find the values..
19631             // comma seperated at present.. this needs to allow JSON based encoding..
19632             this.hiddenEl.value  = v;
19633             var v_ar = [];
19634             Roo.each(v.split(this.seperator), function(k) {
19635                 Roo.log("CHECK " + this.valueField + ',' + k);
19636                 var li = this.store.query(this.valueField, k);
19637                 if (!li.length) {
19638                     return;
19639                 }
19640                 var add = {};
19641                 add[this.valueField] = k;
19642                 add[this.displayField] = li.item(0).data[this.displayField];
19643                 
19644                 this.addItem(add);
19645             }, this) 
19646              
19647         }
19648         if (typeof(v) == 'object' ) {
19649             // then let's assume it's an array of objects..
19650             Roo.each(v, function(l) {
19651                 var add = l;
19652                 if (typeof(l) == 'string') {
19653                     add = {};
19654                     add[this.valueField] = l;
19655                     add[this.displayField] = l
19656                 }
19657                 this.addItem(add);
19658             }, this);
19659              
19660         }
19661         
19662         
19663     },
19664     setFromData: function(v)
19665     {
19666         // this recieves an object, if setValues is called.
19667         this.reset();
19668         this.el.dom.value = v[this.displayField];
19669         this.hiddenEl.dom.value = v[this.valueField];
19670         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19671             return;
19672         }
19673         var kv = v[this.valueField];
19674         var dv = v[this.displayField];
19675         kv = typeof(kv) != 'string' ? '' : kv;
19676         dv = typeof(dv) != 'string' ? '' : dv;
19677         
19678         
19679         var keys = kv.split(this.seperator);
19680         var display = dv.split(this.seperator);
19681         for (var i = 0 ; i < keys.length; i++) {
19682             add = {};
19683             add[this.valueField] = keys[i];
19684             add[this.displayField] = display[i];
19685             this.addItem(add);
19686         }
19687       
19688         
19689     },
19690     
19691     /**
19692      * Validates the combox array value
19693      * @return {Boolean} True if the value is valid, else false
19694      */
19695     validate : function(){
19696         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19697             this.clearInvalid();
19698             return true;
19699         }
19700         return false;
19701     },
19702     
19703     validateValue : function(value){
19704         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19705         
19706     },
19707     
19708     /*@
19709      * overide
19710      * 
19711      */
19712     isDirty : function() {
19713         if(this.disabled) {
19714             return false;
19715         }
19716         
19717         try {
19718             var d = Roo.decode(String(this.originalValue));
19719         } catch (e) {
19720             return String(this.getValue()) !== String(this.originalValue);
19721         }
19722         
19723         var originalValue = [];
19724         
19725         for (var i = 0; i < d.length; i++){
19726             originalValue.push(d[i][this.valueField]);
19727         }
19728         
19729         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19730         
19731     }
19732     
19733 });
19734
19735
19736
19737 /**
19738  * @class Roo.form.ComboBoxArray.Item
19739  * @extends Roo.BoxComponent
19740  * A selected item in the list
19741  *  Fred [x]  Brian [x]  [Pick another |v]
19742  * 
19743  * @constructor
19744  * Create a new item.
19745  * @param {Object} config Configuration options
19746  */
19747  
19748 Roo.form.ComboBoxArray.Item = function(config) {
19749     config.id = Roo.id();
19750     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19751 }
19752
19753 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19754     data : {},
19755     cb: false,
19756     displayField : false,
19757     tipField : false,
19758     
19759     
19760     defaultAutoCreate : {
19761         tag: 'div',
19762         cls: 'x-cbarray-item',
19763         cn : [ 
19764             { tag: 'div' },
19765             {
19766                 tag: 'img',
19767                 width:16,
19768                 height : 16,
19769                 src : Roo.BLANK_IMAGE_URL ,
19770                 align: 'center'
19771             }
19772         ]
19773         
19774     },
19775     
19776  
19777     onRender : function(ct, position)
19778     {
19779         Roo.form.Field.superclass.onRender.call(this, ct, position);
19780         
19781         if(!this.el){
19782             var cfg = this.getAutoCreate();
19783             this.el = ct.createChild(cfg, position);
19784         }
19785         
19786         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19787         
19788         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19789             this.cb.renderer(this.data) :
19790             String.format('{0}',this.data[this.displayField]);
19791         
19792             
19793         this.el.child('div').dom.setAttribute('qtip',
19794                         String.format('{0}',this.data[this.tipField])
19795         );
19796         
19797         this.el.child('img').on('click', this.remove, this);
19798         
19799     },
19800    
19801     remove : function()
19802     {
19803         if(this.cb.disabled){
19804             return;
19805         }
19806         
19807         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19808             this.cb.items.remove(this);
19809             this.el.child('img').un('click', this.remove, this);
19810             this.el.remove();
19811             this.cb.updateHiddenEl();
19812
19813             this.cb.fireEvent('remove', this.cb, this);
19814         }
19815         
19816     }
19817 });/*
19818  * RooJS Library 1.1.1
19819  * Copyright(c) 2008-2011  Alan Knowles
19820  *
19821  * License - LGPL
19822  */
19823  
19824
19825 /**
19826  * @class Roo.form.ComboNested
19827  * @extends Roo.form.ComboBox
19828  * A combobox for that allows selection of nested items in a list,
19829  * eg.
19830  *
19831  *  Book
19832  *    -> red
19833  *    -> green
19834  *  Table
19835  *    -> square
19836  *      ->red
19837  *      ->green
19838  *    -> rectangle
19839  *      ->green
19840  *      
19841  * 
19842  * @constructor
19843  * Create a new ComboNested
19844  * @param {Object} config Configuration options
19845  */
19846 Roo.form.ComboNested = function(config){
19847     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19848     // should verify some data...
19849     // like
19850     // hiddenName = required..
19851     // displayField = required
19852     // valudField == required
19853     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19854     var _t = this;
19855     Roo.each(req, function(e) {
19856         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19857             throw "Roo.form.ComboNested : missing value for: " + e;
19858         }
19859     });
19860      
19861     
19862 };
19863
19864 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19865    
19866     /*
19867      * @config {Number} max Number of columns to show
19868      */
19869     
19870     maxColumns : 3,
19871    
19872     list : null, // the outermost div..
19873     innerLists : null, // the
19874     views : null,
19875     stores : null,
19876     // private
19877     loadingChildren : false,
19878     
19879     onRender : function(ct, position)
19880     {
19881         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19882         
19883         if(this.hiddenName){
19884             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19885                     'before', true);
19886             this.hiddenField.value =
19887                 this.hiddenValue !== undefined ? this.hiddenValue :
19888                 this.value !== undefined ? this.value : '';
19889
19890             // prevent input submission
19891             this.el.dom.removeAttribute('name');
19892              
19893              
19894         }
19895         
19896         if(Roo.isGecko){
19897             this.el.dom.setAttribute('autocomplete', 'off');
19898         }
19899
19900         var cls = 'x-combo-list';
19901
19902         this.list = new Roo.Layer({
19903             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19904         });
19905
19906         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19907         this.list.setWidth(lw);
19908         this.list.swallowEvent('mousewheel');
19909         this.assetHeight = 0;
19910
19911         if(this.title){
19912             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19913             this.assetHeight += this.header.getHeight();
19914         }
19915         this.innerLists = [];
19916         this.views = [];
19917         this.stores = [];
19918         for (var i =0 ; i < this.maxColumns; i++) {
19919             this.onRenderList( cls, i);
19920         }
19921         
19922         // always needs footer, as we are going to have an 'OK' button.
19923         this.footer = this.list.createChild({cls:cls+'-ft'});
19924         this.pageTb = new Roo.Toolbar(this.footer);  
19925         var _this = this;
19926         this.pageTb.add(  {
19927             
19928             text: 'Done',
19929             handler: function()
19930             {
19931                 _this.collapse();
19932             }
19933         });
19934         
19935         if ( this.allowBlank && !this.disableClear) {
19936             
19937             this.pageTb.add(new Roo.Toolbar.Fill(), {
19938                 cls: 'x-btn-icon x-btn-clear',
19939                 text: '&#160;',
19940                 handler: function()
19941                 {
19942                     _this.collapse();
19943                     _this.clearValue();
19944                     _this.onSelect(false, -1);
19945                 }
19946             });
19947         }
19948         if (this.footer) {
19949             this.assetHeight += this.footer.getHeight();
19950         }
19951         
19952     },
19953     onRenderList : function (  cls, i)
19954     {
19955         
19956         var lw = Math.floor(
19957                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19958         );
19959         
19960         this.list.setWidth(lw); // default to '1'
19961
19962         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19963         //il.on('mouseover', this.onViewOver, this, { list:  i });
19964         //il.on('mousemove', this.onViewMove, this, { list:  i });
19965         il.setWidth(lw);
19966         il.setStyle({ 'overflow-x' : 'hidden'});
19967
19968         if(!this.tpl){
19969             this.tpl = new Roo.Template({
19970                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19971                 isEmpty: function (value, allValues) {
19972                     //Roo.log(value);
19973                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19974                     return dl ? 'has-children' : 'no-children'
19975                 }
19976             });
19977         }
19978         
19979         var store  = this.store;
19980         if (i > 0) {
19981             store  = new Roo.data.SimpleStore({
19982                 //fields : this.store.reader.meta.fields,
19983                 reader : this.store.reader,
19984                 data : [ ]
19985             });
19986         }
19987         this.stores[i]  = store;
19988                   
19989         var view = this.views[i] = new Roo.View(
19990             il,
19991             this.tpl,
19992             {
19993                 singleSelect:true,
19994                 store: store,
19995                 selectedClass: this.selectedClass
19996             }
19997         );
19998         view.getEl().setWidth(lw);
19999         view.getEl().setStyle({
20000             position: i < 1 ? 'relative' : 'absolute',
20001             top: 0,
20002             left: (i * lw ) + 'px',
20003             display : i > 0 ? 'none' : 'block'
20004         });
20005         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20006         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20007         //view.on('click', this.onViewClick, this, { list : i });
20008
20009         store.on('beforeload', this.onBeforeLoad, this);
20010         store.on('load',  this.onLoad, this, { list  : i});
20011         store.on('loadexception', this.onLoadException, this);
20012
20013         // hide the other vies..
20014         
20015         
20016         
20017     },
20018       
20019     restrictHeight : function()
20020     {
20021         var mh = 0;
20022         Roo.each(this.innerLists, function(il,i) {
20023             var el = this.views[i].getEl();
20024             el.dom.style.height = '';
20025             var inner = el.dom;
20026             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20027             // only adjust heights on other ones..
20028             mh = Math.max(h, mh);
20029             if (i < 1) {
20030                 
20031                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20032                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20033                
20034             }
20035             
20036             
20037         }, this);
20038         
20039         this.list.beginUpdate();
20040         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20041         this.list.alignTo(this.el, this.listAlign);
20042         this.list.endUpdate();
20043         
20044     },
20045      
20046     
20047     // -- store handlers..
20048     // private
20049     onBeforeLoad : function()
20050     {
20051         if(!this.hasFocus){
20052             return;
20053         }
20054         this.innerLists[0].update(this.loadingText ?
20055                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20056         this.restrictHeight();
20057         this.selectedIndex = -1;
20058     },
20059     // private
20060     onLoad : function(a,b,c,d)
20061     {
20062         if (!this.loadingChildren) {
20063             // then we are loading the top level. - hide the children
20064             for (var i = 1;i < this.views.length; i++) {
20065                 this.views[i].getEl().setStyle({ display : 'none' });
20066             }
20067             var lw = Math.floor(
20068                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20069             );
20070         
20071              this.list.setWidth(lw); // default to '1'
20072
20073             
20074         }
20075         if(!this.hasFocus){
20076             return;
20077         }
20078         
20079         if(this.store.getCount() > 0) {
20080             this.expand();
20081             this.restrictHeight();   
20082         } else {
20083             this.onEmptyResults();
20084         }
20085         
20086         if (!this.loadingChildren) {
20087             this.selectActive();
20088         }
20089         /*
20090         this.stores[1].loadData([]);
20091         this.stores[2].loadData([]);
20092         this.views
20093         */    
20094     
20095         //this.el.focus();
20096     },
20097     
20098     
20099     // private
20100     onLoadException : function()
20101     {
20102         this.collapse();
20103         Roo.log(this.store.reader.jsonData);
20104         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20105             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20106         }
20107         
20108         
20109     },
20110     // no cleaning of leading spaces on blur here.
20111     cleanLeadingSpace : function(e) { },
20112     
20113
20114     onSelectChange : function (view, sels, opts )
20115     {
20116         var ix = view.getSelectedIndexes();
20117          
20118         if (opts.list > this.maxColumns - 2) {
20119             if (view.store.getCount()<  1) {
20120                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20121
20122             } else  {
20123                 if (ix.length) {
20124                     // used to clear ?? but if we are loading unselected 
20125                     this.setFromData(view.store.getAt(ix[0]).data);
20126                 }
20127                 
20128             }
20129             
20130             return;
20131         }
20132         
20133         if (!ix.length) {
20134             // this get's fired when trigger opens..
20135            // this.setFromData({});
20136             var str = this.stores[opts.list+1];
20137             str.data.clear(); // removeall wihtout the fire events..
20138             return;
20139         }
20140         
20141         var rec = view.store.getAt(ix[0]);
20142          
20143         this.setFromData(rec.data);
20144         this.fireEvent('select', this, rec, ix[0]);
20145         
20146         var lw = Math.floor(
20147              (
20148                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20149              ) / this.maxColumns
20150         );
20151         this.loadingChildren = true;
20152         this.stores[opts.list+1].loadDataFromChildren( rec );
20153         this.loadingChildren = false;
20154         var dl = this.stores[opts.list+1]. getTotalCount();
20155         
20156         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20157         
20158         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20159         for (var i = opts.list+2; i < this.views.length;i++) {
20160             this.views[i].getEl().setStyle({ display : 'none' });
20161         }
20162         
20163         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20164         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20165         
20166         if (this.isLoading) {
20167            // this.selectActive(opts.list);
20168         }
20169          
20170     },
20171     
20172     
20173     
20174     
20175     onDoubleClick : function()
20176     {
20177         this.collapse(); //??
20178     },
20179     
20180      
20181     
20182     
20183     
20184     // private
20185     recordToStack : function(store, prop, value, stack)
20186     {
20187         var cstore = new Roo.data.SimpleStore({
20188             //fields : this.store.reader.meta.fields, // we need array reader.. for
20189             reader : this.store.reader,
20190             data : [ ]
20191         });
20192         var _this = this;
20193         var record  = false;
20194         var srec = false;
20195         if(store.getCount() < 1){
20196             return false;
20197         }
20198         store.each(function(r){
20199             if(r.data[prop] == value){
20200                 record = r;
20201             srec = r;
20202                 return false;
20203             }
20204             if (r.data.cn && r.data.cn.length) {
20205                 cstore.loadDataFromChildren( r);
20206                 var cret = _this.recordToStack(cstore, prop, value, stack);
20207                 if (cret !== false) {
20208                     record = cret;
20209                     srec = r;
20210                     return false;
20211                 }
20212             }
20213              
20214             return true;
20215         });
20216         if (record == false) {
20217             return false
20218         }
20219         stack.unshift(srec);
20220         return record;
20221     },
20222     
20223     /*
20224      * find the stack of stores that match our value.
20225      *
20226      * 
20227      */
20228     
20229     selectActive : function ()
20230     {
20231         // if store is not loaded, then we will need to wait for that to happen first.
20232         var stack = [];
20233         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20234         for (var i = 0; i < stack.length; i++ ) {
20235             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20236         }
20237         
20238     }
20239         
20240          
20241     
20242     
20243     
20244     
20245 });/*
20246  * Based on:
20247  * Ext JS Library 1.1.1
20248  * Copyright(c) 2006-2007, Ext JS, LLC.
20249  *
20250  * Originally Released Under LGPL - original licence link has changed is not relivant.
20251  *
20252  * Fork - LGPL
20253  * <script type="text/javascript">
20254  */
20255 /**
20256  * @class Roo.form.Checkbox
20257  * @extends Roo.form.Field
20258  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20259  * @constructor
20260  * Creates a new Checkbox
20261  * @param {Object} config Configuration options
20262  */
20263 Roo.form.Checkbox = function(config){
20264     Roo.form.Checkbox.superclass.constructor.call(this, config);
20265     this.addEvents({
20266         /**
20267          * @event check
20268          * Fires when the checkbox is checked or unchecked.
20269              * @param {Roo.form.Checkbox} this This checkbox
20270              * @param {Boolean} checked The new checked value
20271              */
20272         check : true
20273     });
20274 };
20275
20276 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20277     /**
20278      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20279      */
20280     focusClass : undefined,
20281     /**
20282      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20283      */
20284     fieldClass: "x-form-field",
20285     /**
20286      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20287      */
20288     checked: false,
20289     /**
20290      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20291      * {tag: "input", type: "checkbox", autocomplete: "off"})
20292      */
20293     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20294     /**
20295      * @cfg {String} boxLabel The text that appears beside the checkbox
20296      */
20297     boxLabel : "",
20298     /**
20299      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20300      */  
20301     inputValue : '1',
20302     /**
20303      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20304      */
20305      valueOff: '0', // value when not checked..
20306
20307     actionMode : 'viewEl', 
20308     //
20309     // private
20310     itemCls : 'x-menu-check-item x-form-item',
20311     groupClass : 'x-menu-group-item',
20312     inputType : 'hidden',
20313     
20314     
20315     inSetChecked: false, // check that we are not calling self...
20316     
20317     inputElement: false, // real input element?
20318     basedOn: false, // ????
20319     
20320     isFormField: true, // not sure where this is needed!!!!
20321
20322     onResize : function(){
20323         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20324         if(!this.boxLabel){
20325             this.el.alignTo(this.wrap, 'c-c');
20326         }
20327     },
20328
20329     initEvents : function(){
20330         Roo.form.Checkbox.superclass.initEvents.call(this);
20331         this.el.on("click", this.onClick,  this);
20332         this.el.on("change", this.onClick,  this);
20333     },
20334
20335
20336     getResizeEl : function(){
20337         return this.wrap;
20338     },
20339
20340     getPositionEl : function(){
20341         return this.wrap;
20342     },
20343
20344     // private
20345     onRender : function(ct, position){
20346         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20347         /*
20348         if(this.inputValue !== undefined){
20349             this.el.dom.value = this.inputValue;
20350         }
20351         */
20352         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20353         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20354         var viewEl = this.wrap.createChild({ 
20355             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20356         this.viewEl = viewEl;   
20357         this.wrap.on('click', this.onClick,  this); 
20358         
20359         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20360         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20361         
20362         
20363         
20364         if(this.boxLabel){
20365             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20366         //    viewEl.on('click', this.onClick,  this); 
20367         }
20368         //if(this.checked){
20369             this.setChecked(this.checked);
20370         //}else{
20371             //this.checked = this.el.dom;
20372         //}
20373
20374     },
20375
20376     // private
20377     initValue : Roo.emptyFn,
20378
20379     /**
20380      * Returns the checked state of the checkbox.
20381      * @return {Boolean} True if checked, else false
20382      */
20383     getValue : function(){
20384         if(this.el){
20385             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20386         }
20387         return this.valueOff;
20388         
20389     },
20390
20391         // private
20392     onClick : function(){ 
20393         if (this.disabled) {
20394             return;
20395         }
20396         this.setChecked(!this.checked);
20397
20398         //if(this.el.dom.checked != this.checked){
20399         //    this.setValue(this.el.dom.checked);
20400        // }
20401     },
20402
20403     /**
20404      * Sets the checked state of the checkbox.
20405      * On is always based on a string comparison between inputValue and the param.
20406      * @param {Boolean/String} value - the value to set 
20407      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20408      */
20409     setValue : function(v,suppressEvent){
20410         
20411         
20412         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20413         //if(this.el && this.el.dom){
20414         //    this.el.dom.checked = this.checked;
20415         //    this.el.dom.defaultChecked = this.checked;
20416         //}
20417         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20418         //this.fireEvent("check", this, this.checked);
20419     },
20420     // private..
20421     setChecked : function(state,suppressEvent)
20422     {
20423         if (this.inSetChecked) {
20424             this.checked = state;
20425             return;
20426         }
20427         
20428     
20429         if(this.wrap){
20430             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20431         }
20432         this.checked = state;
20433         if(suppressEvent !== true){
20434             this.fireEvent('check', this, state);
20435         }
20436         this.inSetChecked = true;
20437         this.el.dom.value = state ? this.inputValue : this.valueOff;
20438         this.inSetChecked = false;
20439         
20440     },
20441     // handle setting of hidden value by some other method!!?!?
20442     setFromHidden: function()
20443     {
20444         if(!this.el){
20445             return;
20446         }
20447         //console.log("SET FROM HIDDEN");
20448         //alert('setFrom hidden');
20449         this.setValue(this.el.dom.value);
20450     },
20451     
20452     onDestroy : function()
20453     {
20454         if(this.viewEl){
20455             Roo.get(this.viewEl).remove();
20456         }
20457          
20458         Roo.form.Checkbox.superclass.onDestroy.call(this);
20459     },
20460     
20461     setBoxLabel : function(str)
20462     {
20463         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20464     }
20465
20466 });/*
20467  * Based on:
20468  * Ext JS Library 1.1.1
20469  * Copyright(c) 2006-2007, Ext JS, LLC.
20470  *
20471  * Originally Released Under LGPL - original licence link has changed is not relivant.
20472  *
20473  * Fork - LGPL
20474  * <script type="text/javascript">
20475  */
20476  
20477 /**
20478  * @class Roo.form.Radio
20479  * @extends Roo.form.Checkbox
20480  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20481  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20482  * @constructor
20483  * Creates a new Radio
20484  * @param {Object} config Configuration options
20485  */
20486 Roo.form.Radio = function(){
20487     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20488 };
20489 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20490     inputType: 'radio',
20491
20492     /**
20493      * If this radio is part of a group, it will return the selected value
20494      * @return {String}
20495      */
20496     getGroupValue : function(){
20497         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20498     },
20499     
20500     
20501     onRender : function(ct, position){
20502         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20503         
20504         if(this.inputValue !== undefined){
20505             this.el.dom.value = this.inputValue;
20506         }
20507          
20508         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20509         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20510         //var viewEl = this.wrap.createChild({ 
20511         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20512         //this.viewEl = viewEl;   
20513         //this.wrap.on('click', this.onClick,  this); 
20514         
20515         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20516         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20517         
20518         
20519         
20520         if(this.boxLabel){
20521             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20522         //    viewEl.on('click', this.onClick,  this); 
20523         }
20524          if(this.checked){
20525             this.el.dom.checked =   'checked' ;
20526         }
20527          
20528     } 
20529     
20530     
20531 });//<script type="text/javascript">
20532
20533 /*
20534  * Based  Ext JS Library 1.1.1
20535  * Copyright(c) 2006-2007, Ext JS, LLC.
20536  * LGPL
20537  *
20538  */
20539  
20540 /**
20541  * @class Roo.HtmlEditorCore
20542  * @extends Roo.Component
20543  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20544  *
20545  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20546  */
20547
20548 Roo.HtmlEditorCore = function(config){
20549     
20550     
20551     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20552     
20553     
20554     this.addEvents({
20555         /**
20556          * @event initialize
20557          * Fires when the editor is fully initialized (including the iframe)
20558          * @param {Roo.HtmlEditorCore} this
20559          */
20560         initialize: true,
20561         /**
20562          * @event activate
20563          * Fires when the editor is first receives the focus. Any insertion must wait
20564          * until after this event.
20565          * @param {Roo.HtmlEditorCore} this
20566          */
20567         activate: true,
20568          /**
20569          * @event beforesync
20570          * Fires before the textarea is updated with content from the editor iframe. Return false
20571          * to cancel the sync.
20572          * @param {Roo.HtmlEditorCore} this
20573          * @param {String} html
20574          */
20575         beforesync: true,
20576          /**
20577          * @event beforepush
20578          * Fires before the iframe editor is updated with content from the textarea. Return false
20579          * to cancel the push.
20580          * @param {Roo.HtmlEditorCore} this
20581          * @param {String} html
20582          */
20583         beforepush: true,
20584          /**
20585          * @event sync
20586          * Fires when the textarea is updated with content from the editor iframe.
20587          * @param {Roo.HtmlEditorCore} this
20588          * @param {String} html
20589          */
20590         sync: true,
20591          /**
20592          * @event push
20593          * Fires when the iframe editor is updated with content from the textarea.
20594          * @param {Roo.HtmlEditorCore} this
20595          * @param {String} html
20596          */
20597         push: true,
20598         
20599         /**
20600          * @event editorevent
20601          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20602          * @param {Roo.HtmlEditorCore} this
20603          */
20604         editorevent: true
20605         
20606     });
20607     
20608     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20609     
20610     // defaults : white / black...
20611     this.applyBlacklists();
20612     
20613     
20614     
20615 };
20616
20617
20618 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20619
20620
20621      /**
20622      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20623      */
20624     
20625     owner : false,
20626     
20627      /**
20628      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20629      *                        Roo.resizable.
20630      */
20631     resizable : false,
20632      /**
20633      * @cfg {Number} height (in pixels)
20634      */   
20635     height: 300,
20636    /**
20637      * @cfg {Number} width (in pixels)
20638      */   
20639     width: 500,
20640     
20641     /**
20642      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20643      * 
20644      */
20645     stylesheets: false,
20646     
20647     /**
20648      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20649      */
20650     allowComments: false,
20651     // id of frame..
20652     frameId: false,
20653     
20654     // private properties
20655     validationEvent : false,
20656     deferHeight: true,
20657     initialized : false,
20658     activated : false,
20659     sourceEditMode : false,
20660     onFocus : Roo.emptyFn,
20661     iframePad:3,
20662     hideMode:'offsets',
20663     
20664     clearUp: true,
20665     
20666     // blacklist + whitelisted elements..
20667     black: false,
20668     white: false,
20669      
20670     bodyCls : '',
20671
20672     /**
20673      * Protected method that will not generally be called directly. It
20674      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20675      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20676      */
20677     getDocMarkup : function(){
20678         // body styles..
20679         var st = '';
20680         
20681         // inherit styels from page...?? 
20682         if (this.stylesheets === false) {
20683             
20684             Roo.get(document.head).select('style').each(function(node) {
20685                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20686             });
20687             
20688             Roo.get(document.head).select('link').each(function(node) { 
20689                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20690             });
20691             
20692         } else if (!this.stylesheets.length) {
20693                 // simple..
20694                 st = '<style type="text/css">' +
20695                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20696                    '</style>';
20697         } else {
20698             for (var i in this.stylesheets) { 
20699                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20700             }
20701             
20702         }
20703         
20704         st +=  '<style type="text/css">' +
20705             'IMG { cursor: pointer } ' +
20706         '</style>';
20707
20708         var cls = 'roo-htmleditor-body';
20709         
20710         if(this.bodyCls.length){
20711             cls += ' ' + this.bodyCls;
20712         }
20713         
20714         return '<html><head>' + st  +
20715             //<style type="text/css">' +
20716             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20717             //'</style>' +
20718             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20719     },
20720
20721     // private
20722     onRender : function(ct, position)
20723     {
20724         var _t = this;
20725         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20726         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20727         
20728         
20729         this.el.dom.style.border = '0 none';
20730         this.el.dom.setAttribute('tabIndex', -1);
20731         this.el.addClass('x-hidden hide');
20732         
20733         
20734         
20735         if(Roo.isIE){ // fix IE 1px bogus margin
20736             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20737         }
20738        
20739         
20740         this.frameId = Roo.id();
20741         
20742          
20743         
20744         var iframe = this.owner.wrap.createChild({
20745             tag: 'iframe',
20746             cls: 'form-control', // bootstrap..
20747             id: this.frameId,
20748             name: this.frameId,
20749             frameBorder : 'no',
20750             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20751         }, this.el
20752         );
20753         
20754         
20755         this.iframe = iframe.dom;
20756
20757          this.assignDocWin();
20758         
20759         this.doc.designMode = 'on';
20760        
20761         this.doc.open();
20762         this.doc.write(this.getDocMarkup());
20763         this.doc.close();
20764
20765         
20766         var task = { // must defer to wait for browser to be ready
20767             run : function(){
20768                 //console.log("run task?" + this.doc.readyState);
20769                 this.assignDocWin();
20770                 if(this.doc.body || this.doc.readyState == 'complete'){
20771                     try {
20772                         this.doc.designMode="on";
20773                     } catch (e) {
20774                         return;
20775                     }
20776                     Roo.TaskMgr.stop(task);
20777                     this.initEditor.defer(10, this);
20778                 }
20779             },
20780             interval : 10,
20781             duration: 10000,
20782             scope: this
20783         };
20784         Roo.TaskMgr.start(task);
20785
20786     },
20787
20788     // private
20789     onResize : function(w, h)
20790     {
20791          Roo.log('resize: ' +w + ',' + h );
20792         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20793         if(!this.iframe){
20794             return;
20795         }
20796         if(typeof w == 'number'){
20797             
20798             this.iframe.style.width = w + 'px';
20799         }
20800         if(typeof h == 'number'){
20801             
20802             this.iframe.style.height = h + 'px';
20803             if(this.doc){
20804                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20805             }
20806         }
20807         
20808     },
20809
20810     /**
20811      * Toggles the editor between standard and source edit mode.
20812      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20813      */
20814     toggleSourceEdit : function(sourceEditMode){
20815         
20816         this.sourceEditMode = sourceEditMode === true;
20817         
20818         if(this.sourceEditMode){
20819  
20820             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20821             
20822         }else{
20823             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20824             //this.iframe.className = '';
20825             this.deferFocus();
20826         }
20827         //this.setSize(this.owner.wrap.getSize());
20828         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20829     },
20830
20831     
20832   
20833
20834     /**
20835      * Protected method that will not generally be called directly. If you need/want
20836      * custom HTML cleanup, this is the method you should override.
20837      * @param {String} html The HTML to be cleaned
20838      * return {String} The cleaned HTML
20839      */
20840     cleanHtml : function(html){
20841         html = String(html);
20842         if(html.length > 5){
20843             if(Roo.isSafari){ // strip safari nonsense
20844                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20845             }
20846         }
20847         if(html == '&nbsp;'){
20848             html = '';
20849         }
20850         return html;
20851     },
20852
20853     /**
20854      * HTML Editor -> Textarea
20855      * Protected method that will not generally be called directly. Syncs the contents
20856      * of the editor iframe with the textarea.
20857      */
20858     syncValue : function(){
20859         if(this.initialized){
20860             var bd = (this.doc.body || this.doc.documentElement);
20861             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20862             var html = bd.innerHTML;
20863             if(Roo.isSafari){
20864                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20865                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20866                 if(m && m[1]){
20867                     html = '<div style="'+m[0]+'">' + html + '</div>';
20868                 }
20869             }
20870             html = this.cleanHtml(html);
20871             // fix up the special chars.. normaly like back quotes in word...
20872             // however we do not want to do this with chinese..
20873             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20874                 
20875                 var cc = match.charCodeAt();
20876
20877                 // Get the character value, handling surrogate pairs
20878                 if (match.length == 2) {
20879                     // It's a surrogate pair, calculate the Unicode code point
20880                     var high = match.charCodeAt(0) - 0xD800;
20881                     var low  = match.charCodeAt(1) - 0xDC00;
20882                     cc = (high * 0x400) + low + 0x10000;
20883                 }  else if (
20884                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20885                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20886                     (cc >= 0xf900 && cc < 0xfb00 )
20887                 ) {
20888                         return match;
20889                 }  
20890          
20891                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20892                 return "&#" + cc + ";";
20893                 
20894                 
20895             });
20896             
20897             
20898              
20899             if(this.owner.fireEvent('beforesync', this, html) !== false){
20900                 this.el.dom.value = html;
20901                 this.owner.fireEvent('sync', this, html);
20902             }
20903         }
20904     },
20905
20906     /**
20907      * Protected method that will not generally be called directly. Pushes the value of the textarea
20908      * into the iframe editor.
20909      */
20910     pushValue : function(){
20911         if(this.initialized){
20912             var v = this.el.dom.value.trim();
20913             
20914 //            if(v.length < 1){
20915 //                v = '&#160;';
20916 //            }
20917             
20918             if(this.owner.fireEvent('beforepush', this, v) !== false){
20919                 var d = (this.doc.body || this.doc.documentElement);
20920                 d.innerHTML = v;
20921                 this.cleanUpPaste();
20922                 this.el.dom.value = d.innerHTML;
20923                 this.owner.fireEvent('push', this, v);
20924             }
20925         }
20926     },
20927
20928     // private
20929     deferFocus : function(){
20930         this.focus.defer(10, this);
20931     },
20932
20933     // doc'ed in Field
20934     focus : function(){
20935         if(this.win && !this.sourceEditMode){
20936             this.win.focus();
20937         }else{
20938             this.el.focus();
20939         }
20940     },
20941     
20942     assignDocWin: function()
20943     {
20944         var iframe = this.iframe;
20945         
20946          if(Roo.isIE){
20947             this.doc = iframe.contentWindow.document;
20948             this.win = iframe.contentWindow;
20949         } else {
20950 //            if (!Roo.get(this.frameId)) {
20951 //                return;
20952 //            }
20953 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20954 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20955             
20956             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20957                 return;
20958             }
20959             
20960             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20961             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20962         }
20963     },
20964     
20965     // private
20966     initEditor : function(){
20967         //console.log("INIT EDITOR");
20968         this.assignDocWin();
20969         
20970         
20971         
20972         this.doc.designMode="on";
20973         this.doc.open();
20974         this.doc.write(this.getDocMarkup());
20975         this.doc.close();
20976         
20977         var dbody = (this.doc.body || this.doc.documentElement);
20978         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20979         // this copies styles from the containing element into thsi one..
20980         // not sure why we need all of this..
20981         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20982         
20983         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20984         //ss['background-attachment'] = 'fixed'; // w3c
20985         dbody.bgProperties = 'fixed'; // ie
20986         //Roo.DomHelper.applyStyles(dbody, ss);
20987         Roo.EventManager.on(this.doc, {
20988             //'mousedown': this.onEditorEvent,
20989             'mouseup': this.onEditorEvent,
20990             'dblclick': this.onEditorEvent,
20991             'click': this.onEditorEvent,
20992             'keyup': this.onEditorEvent,
20993             buffer:100,
20994             scope: this
20995         });
20996         if(Roo.isGecko){
20997             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20998         }
20999         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21000             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21001         }
21002         this.initialized = true;
21003
21004         this.owner.fireEvent('initialize', this);
21005         this.pushValue();
21006     },
21007
21008     // private
21009     onDestroy : function(){
21010         
21011         
21012         
21013         if(this.rendered){
21014             
21015             //for (var i =0; i < this.toolbars.length;i++) {
21016             //    // fixme - ask toolbars for heights?
21017             //    this.toolbars[i].onDestroy();
21018            // }
21019             
21020             //this.wrap.dom.innerHTML = '';
21021             //this.wrap.remove();
21022         }
21023     },
21024
21025     // private
21026     onFirstFocus : function(){
21027         
21028         this.assignDocWin();
21029         
21030         
21031         this.activated = true;
21032          
21033     
21034         if(Roo.isGecko){ // prevent silly gecko errors
21035             this.win.focus();
21036             var s = this.win.getSelection();
21037             if(!s.focusNode || s.focusNode.nodeType != 3){
21038                 var r = s.getRangeAt(0);
21039                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21040                 r.collapse(true);
21041                 this.deferFocus();
21042             }
21043             try{
21044                 this.execCmd('useCSS', true);
21045                 this.execCmd('styleWithCSS', false);
21046             }catch(e){}
21047         }
21048         this.owner.fireEvent('activate', this);
21049     },
21050
21051     // private
21052     adjustFont: function(btn){
21053         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21054         //if(Roo.isSafari){ // safari
21055         //    adjust *= 2;
21056        // }
21057         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21058         if(Roo.isSafari){ // safari
21059             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21060             v =  (v < 10) ? 10 : v;
21061             v =  (v > 48) ? 48 : v;
21062             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21063             
21064         }
21065         
21066         
21067         v = Math.max(1, v+adjust);
21068         
21069         this.execCmd('FontSize', v  );
21070     },
21071
21072     onEditorEvent : function(e)
21073     {
21074         this.owner.fireEvent('editorevent', this, e);
21075       //  this.updateToolbar();
21076         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21077     },
21078
21079     insertTag : function(tg)
21080     {
21081         // could be a bit smarter... -> wrap the current selected tRoo..
21082         if (tg.toLowerCase() == 'span' ||
21083             tg.toLowerCase() == 'code' ||
21084             tg.toLowerCase() == 'sup' ||
21085             tg.toLowerCase() == 'sub' 
21086             ) {
21087             
21088             range = this.createRange(this.getSelection());
21089             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21090             wrappingNode.appendChild(range.extractContents());
21091             range.insertNode(wrappingNode);
21092
21093             return;
21094             
21095             
21096             
21097         }
21098         this.execCmd("formatblock",   tg);
21099         
21100     },
21101     
21102     insertText : function(txt)
21103     {
21104         
21105         
21106         var range = this.createRange();
21107         range.deleteContents();
21108                //alert(Sender.getAttribute('label'));
21109                
21110         range.insertNode(this.doc.createTextNode(txt));
21111     } ,
21112     
21113      
21114
21115     /**
21116      * Executes a Midas editor command on the editor document and performs necessary focus and
21117      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21118      * @param {String} cmd The Midas command
21119      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21120      */
21121     relayCmd : function(cmd, value){
21122         this.win.focus();
21123         this.execCmd(cmd, value);
21124         this.owner.fireEvent('editorevent', this);
21125         //this.updateToolbar();
21126         this.owner.deferFocus();
21127     },
21128
21129     /**
21130      * Executes a Midas editor command directly on the editor document.
21131      * For visual commands, you should use {@link #relayCmd} instead.
21132      * <b>This should only be called after the editor is initialized.</b>
21133      * @param {String} cmd The Midas command
21134      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21135      */
21136     execCmd : function(cmd, value){
21137         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21138         this.syncValue();
21139     },
21140  
21141  
21142    
21143     /**
21144      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21145      * to insert tRoo.
21146      * @param {String} text | dom node.. 
21147      */
21148     insertAtCursor : function(text)
21149     {
21150         
21151         if(!this.activated){
21152             return;
21153         }
21154         /*
21155         if(Roo.isIE){
21156             this.win.focus();
21157             var r = this.doc.selection.createRange();
21158             if(r){
21159                 r.collapse(true);
21160                 r.pasteHTML(text);
21161                 this.syncValue();
21162                 this.deferFocus();
21163             
21164             }
21165             return;
21166         }
21167         */
21168         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21169             this.win.focus();
21170             
21171             
21172             // from jquery ui (MIT licenced)
21173             var range, node;
21174             var win = this.win;
21175             
21176             if (win.getSelection && win.getSelection().getRangeAt) {
21177                 range = win.getSelection().getRangeAt(0);
21178                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21179                 range.insertNode(node);
21180             } else if (win.document.selection && win.document.selection.createRange) {
21181                 // no firefox support
21182                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21183                 win.document.selection.createRange().pasteHTML(txt);
21184             } else {
21185                 // no firefox support
21186                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21187                 this.execCmd('InsertHTML', txt);
21188             } 
21189             
21190             this.syncValue();
21191             
21192             this.deferFocus();
21193         }
21194     },
21195  // private
21196     mozKeyPress : function(e){
21197         if(e.ctrlKey){
21198             var c = e.getCharCode(), cmd;
21199           
21200             if(c > 0){
21201                 c = String.fromCharCode(c).toLowerCase();
21202                 switch(c){
21203                     case 'b':
21204                         cmd = 'bold';
21205                         break;
21206                     case 'i':
21207                         cmd = 'italic';
21208                         break;
21209                     
21210                     case 'u':
21211                         cmd = 'underline';
21212                         break;
21213                     
21214                     case 'v':
21215                         this.cleanUpPaste.defer(100, this);
21216                         return;
21217                         
21218                 }
21219                 if(cmd){
21220                     this.win.focus();
21221                     this.execCmd(cmd);
21222                     this.deferFocus();
21223                     e.preventDefault();
21224                 }
21225                 
21226             }
21227         }
21228     },
21229
21230     // private
21231     fixKeys : function(){ // load time branching for fastest keydown performance
21232         if(Roo.isIE){
21233             return function(e){
21234                 var k = e.getKey(), r;
21235                 if(k == e.TAB){
21236                     e.stopEvent();
21237                     r = this.doc.selection.createRange();
21238                     if(r){
21239                         r.collapse(true);
21240                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21241                         this.deferFocus();
21242                     }
21243                     return;
21244                 }
21245                 
21246                 if(k == e.ENTER){
21247                     r = this.doc.selection.createRange();
21248                     if(r){
21249                         var target = r.parentElement();
21250                         if(!target || target.tagName.toLowerCase() != 'li'){
21251                             e.stopEvent();
21252                             r.pasteHTML('<br />');
21253                             r.collapse(false);
21254                             r.select();
21255                         }
21256                     }
21257                 }
21258                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21259                     this.cleanUpPaste.defer(100, this);
21260                     return;
21261                 }
21262                 
21263                 
21264             };
21265         }else if(Roo.isOpera){
21266             return function(e){
21267                 var k = e.getKey();
21268                 if(k == e.TAB){
21269                     e.stopEvent();
21270                     this.win.focus();
21271                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21272                     this.deferFocus();
21273                 }
21274                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21275                     this.cleanUpPaste.defer(100, this);
21276                     return;
21277                 }
21278                 
21279             };
21280         }else if(Roo.isSafari){
21281             return function(e){
21282                 var k = e.getKey();
21283                 
21284                 if(k == e.TAB){
21285                     e.stopEvent();
21286                     this.execCmd('InsertText','\t');
21287                     this.deferFocus();
21288                     return;
21289                 }
21290                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21291                     this.cleanUpPaste.defer(100, this);
21292                     return;
21293                 }
21294                 
21295              };
21296         }
21297     }(),
21298     
21299     getAllAncestors: function()
21300     {
21301         var p = this.getSelectedNode();
21302         var a = [];
21303         if (!p) {
21304             a.push(p); // push blank onto stack..
21305             p = this.getParentElement();
21306         }
21307         
21308         
21309         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21310             a.push(p);
21311             p = p.parentNode;
21312         }
21313         a.push(this.doc.body);
21314         return a;
21315     },
21316     lastSel : false,
21317     lastSelNode : false,
21318     
21319     
21320     getSelection : function() 
21321     {
21322         this.assignDocWin();
21323         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21324     },
21325     
21326     getSelectedNode: function() 
21327     {
21328         // this may only work on Gecko!!!
21329         
21330         // should we cache this!!!!
21331         
21332         
21333         
21334          
21335         var range = this.createRange(this.getSelection()).cloneRange();
21336         
21337         if (Roo.isIE) {
21338             var parent = range.parentElement();
21339             while (true) {
21340                 var testRange = range.duplicate();
21341                 testRange.moveToElementText(parent);
21342                 if (testRange.inRange(range)) {
21343                     break;
21344                 }
21345                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21346                     break;
21347                 }
21348                 parent = parent.parentElement;
21349             }
21350             return parent;
21351         }
21352         
21353         // is ancestor a text element.
21354         var ac =  range.commonAncestorContainer;
21355         if (ac.nodeType == 3) {
21356             ac = ac.parentNode;
21357         }
21358         
21359         var ar = ac.childNodes;
21360          
21361         var nodes = [];
21362         var other_nodes = [];
21363         var has_other_nodes = false;
21364         for (var i=0;i<ar.length;i++) {
21365             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21366                 continue;
21367             }
21368             // fullly contained node.
21369             
21370             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21371                 nodes.push(ar[i]);
21372                 continue;
21373             }
21374             
21375             // probably selected..
21376             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21377                 other_nodes.push(ar[i]);
21378                 continue;
21379             }
21380             // outer..
21381             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21382                 continue;
21383             }
21384             
21385             
21386             has_other_nodes = true;
21387         }
21388         if (!nodes.length && other_nodes.length) {
21389             nodes= other_nodes;
21390         }
21391         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21392             return false;
21393         }
21394         
21395         return nodes[0];
21396     },
21397     createRange: function(sel)
21398     {
21399         // this has strange effects when using with 
21400         // top toolbar - not sure if it's a great idea.
21401         //this.editor.contentWindow.focus();
21402         if (typeof sel != "undefined") {
21403             try {
21404                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21405             } catch(e) {
21406                 return this.doc.createRange();
21407             }
21408         } else {
21409             return this.doc.createRange();
21410         }
21411     },
21412     getParentElement: function()
21413     {
21414         
21415         this.assignDocWin();
21416         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21417         
21418         var range = this.createRange(sel);
21419          
21420         try {
21421             var p = range.commonAncestorContainer;
21422             while (p.nodeType == 3) { // text node
21423                 p = p.parentNode;
21424             }
21425             return p;
21426         } catch (e) {
21427             return null;
21428         }
21429     
21430     },
21431     /***
21432      *
21433      * Range intersection.. the hard stuff...
21434      *  '-1' = before
21435      *  '0' = hits..
21436      *  '1' = after.
21437      *         [ -- selected range --- ]
21438      *   [fail]                        [fail]
21439      *
21440      *    basically..
21441      *      if end is before start or  hits it. fail.
21442      *      if start is after end or hits it fail.
21443      *
21444      *   if either hits (but other is outside. - then it's not 
21445      *   
21446      *    
21447      **/
21448     
21449     
21450     // @see http://www.thismuchiknow.co.uk/?p=64.
21451     rangeIntersectsNode : function(range, node)
21452     {
21453         var nodeRange = node.ownerDocument.createRange();
21454         try {
21455             nodeRange.selectNode(node);
21456         } catch (e) {
21457             nodeRange.selectNodeContents(node);
21458         }
21459     
21460         var rangeStartRange = range.cloneRange();
21461         rangeStartRange.collapse(true);
21462     
21463         var rangeEndRange = range.cloneRange();
21464         rangeEndRange.collapse(false);
21465     
21466         var nodeStartRange = nodeRange.cloneRange();
21467         nodeStartRange.collapse(true);
21468     
21469         var nodeEndRange = nodeRange.cloneRange();
21470         nodeEndRange.collapse(false);
21471     
21472         return rangeStartRange.compareBoundaryPoints(
21473                  Range.START_TO_START, nodeEndRange) == -1 &&
21474                rangeEndRange.compareBoundaryPoints(
21475                  Range.START_TO_START, nodeStartRange) == 1;
21476         
21477          
21478     },
21479     rangeCompareNode : function(range, node)
21480     {
21481         var nodeRange = node.ownerDocument.createRange();
21482         try {
21483             nodeRange.selectNode(node);
21484         } catch (e) {
21485             nodeRange.selectNodeContents(node);
21486         }
21487         
21488         
21489         range.collapse(true);
21490     
21491         nodeRange.collapse(true);
21492      
21493         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21494         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21495          
21496         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21497         
21498         var nodeIsBefore   =  ss == 1;
21499         var nodeIsAfter    = ee == -1;
21500         
21501         if (nodeIsBefore && nodeIsAfter) {
21502             return 0; // outer
21503         }
21504         if (!nodeIsBefore && nodeIsAfter) {
21505             return 1; //right trailed.
21506         }
21507         
21508         if (nodeIsBefore && !nodeIsAfter) {
21509             return 2;  // left trailed.
21510         }
21511         // fully contined.
21512         return 3;
21513     },
21514
21515     // private? - in a new class?
21516     cleanUpPaste :  function()
21517     {
21518         // cleans up the whole document..
21519         Roo.log('cleanuppaste');
21520         
21521         this.cleanUpChildren(this.doc.body);
21522         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21523         if (clean != this.doc.body.innerHTML) {
21524             this.doc.body.innerHTML = clean;
21525         }
21526         
21527     },
21528     
21529     cleanWordChars : function(input) {// change the chars to hex code
21530         var he = Roo.HtmlEditorCore;
21531         
21532         var output = input;
21533         Roo.each(he.swapCodes, function(sw) { 
21534             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21535             
21536             output = output.replace(swapper, sw[1]);
21537         });
21538         
21539         return output;
21540     },
21541     
21542     
21543     cleanUpChildren : function (n)
21544     {
21545         if (!n.childNodes.length) {
21546             return;
21547         }
21548         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21549            this.cleanUpChild(n.childNodes[i]);
21550         }
21551     },
21552     
21553     
21554         
21555     
21556     cleanUpChild : function (node)
21557     {
21558         var ed = this;
21559         //console.log(node);
21560         if (node.nodeName == "#text") {
21561             // clean up silly Windows -- stuff?
21562             return; 
21563         }
21564         if (node.nodeName == "#comment") {
21565             if (!this.allowComments) {
21566                 node.parentNode.removeChild(node);
21567             }
21568             // clean up silly Windows -- stuff?
21569             return; 
21570         }
21571         var lcname = node.tagName.toLowerCase();
21572         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21573         // whitelist of tags..
21574         
21575         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21576             // remove node.
21577             node.parentNode.removeChild(node);
21578             return;
21579             
21580         }
21581         
21582         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21583         
21584         // spans with no attributes - just remove them..
21585         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21586             remove_keep_children = true;
21587         }
21588         
21589         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21590         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21591         
21592         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21593         //    remove_keep_children = true;
21594         //}
21595         
21596         if (remove_keep_children) {
21597             this.cleanUpChildren(node);
21598             // inserts everything just before this node...
21599             while (node.childNodes.length) {
21600                 var cn = node.childNodes[0];
21601                 node.removeChild(cn);
21602                 node.parentNode.insertBefore(cn, node);
21603             }
21604             node.parentNode.removeChild(node);
21605             return;
21606         }
21607         
21608         if (!node.attributes || !node.attributes.length) {
21609             
21610           
21611             
21612             
21613             this.cleanUpChildren(node);
21614             return;
21615         }
21616         
21617         function cleanAttr(n,v)
21618         {
21619             
21620             if (v.match(/^\./) || v.match(/^\//)) {
21621                 return;
21622             }
21623             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21624                 return;
21625             }
21626             if (v.match(/^#/)) {
21627                 return;
21628             }
21629             if (v.match(/^\{/)) { // allow template editing.
21630                 return;
21631             }
21632 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21633             node.removeAttribute(n);
21634             
21635         }
21636         
21637         var cwhite = this.cwhite;
21638         var cblack = this.cblack;
21639             
21640         function cleanStyle(n,v)
21641         {
21642             if (v.match(/expression/)) { //XSS?? should we even bother..
21643                 node.removeAttribute(n);
21644                 return;
21645             }
21646             
21647             var parts = v.split(/;/);
21648             var clean = [];
21649             
21650             Roo.each(parts, function(p) {
21651                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21652                 if (!p.length) {
21653                     return true;
21654                 }
21655                 var l = p.split(':').shift().replace(/\s+/g,'');
21656                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21657                 
21658                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21659 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21660                     //node.removeAttribute(n);
21661                     return true;
21662                 }
21663                 //Roo.log()
21664                 // only allow 'c whitelisted system attributes'
21665                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21666 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21667                     //node.removeAttribute(n);
21668                     return true;
21669                 }
21670                 
21671                 
21672                  
21673                 
21674                 clean.push(p);
21675                 return true;
21676             });
21677             if (clean.length) { 
21678                 node.setAttribute(n, clean.join(';'));
21679             } else {
21680                 node.removeAttribute(n);
21681             }
21682             
21683         }
21684         
21685         
21686         for (var i = node.attributes.length-1; i > -1 ; i--) {
21687             var a = node.attributes[i];
21688             //console.log(a);
21689             
21690             if (a.name.toLowerCase().substr(0,2)=='on')  {
21691                 node.removeAttribute(a.name);
21692                 continue;
21693             }
21694             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21695                 node.removeAttribute(a.name);
21696                 continue;
21697             }
21698             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21699                 cleanAttr(a.name,a.value); // fixme..
21700                 continue;
21701             }
21702             if (a.name == 'style') {
21703                 cleanStyle(a.name,a.value);
21704                 continue;
21705             }
21706             /// clean up MS crap..
21707             // tecnically this should be a list of valid class'es..
21708             
21709             
21710             if (a.name == 'class') {
21711                 if (a.value.match(/^Mso/)) {
21712                     node.removeAttribute('class');
21713                 }
21714                 
21715                 if (a.value.match(/^body$/)) {
21716                     node.removeAttribute('class');
21717                 }
21718                 continue;
21719             }
21720             
21721             // style cleanup!?
21722             // class cleanup?
21723             
21724         }
21725         
21726         
21727         this.cleanUpChildren(node);
21728         
21729         
21730     },
21731     
21732     /**
21733      * Clean up MS wordisms...
21734      */
21735     cleanWord : function(node)
21736     {
21737         if (!node) {
21738             this.cleanWord(this.doc.body);
21739             return;
21740         }
21741         
21742         if(
21743                 node.nodeName == 'SPAN' &&
21744                 !node.hasAttributes() &&
21745                 node.childNodes.length == 1 &&
21746                 node.firstChild.nodeName == "#text"  
21747         ) {
21748             var textNode = node.firstChild;
21749             node.removeChild(textNode);
21750             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21751                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21752             }
21753             node.parentNode.insertBefore(textNode, node);
21754             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21755                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21756             }
21757             node.parentNode.removeChild(node);
21758         }
21759         
21760         if (node.nodeName == "#text") {
21761             // clean up silly Windows -- stuff?
21762             return; 
21763         }
21764         if (node.nodeName == "#comment") {
21765             node.parentNode.removeChild(node);
21766             // clean up silly Windows -- stuff?
21767             return; 
21768         }
21769         
21770         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21771             node.parentNode.removeChild(node);
21772             return;
21773         }
21774         //Roo.log(node.tagName);
21775         // remove - but keep children..
21776         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21777             //Roo.log('-- removed');
21778             while (node.childNodes.length) {
21779                 var cn = node.childNodes[0];
21780                 node.removeChild(cn);
21781                 node.parentNode.insertBefore(cn, node);
21782                 // move node to parent - and clean it..
21783                 this.cleanWord(cn);
21784             }
21785             node.parentNode.removeChild(node);
21786             /// no need to iterate chidlren = it's got none..
21787             //this.iterateChildren(node, this.cleanWord);
21788             return;
21789         }
21790         // clean styles
21791         if (node.className.length) {
21792             
21793             var cn = node.className.split(/\W+/);
21794             var cna = [];
21795             Roo.each(cn, function(cls) {
21796                 if (cls.match(/Mso[a-zA-Z]+/)) {
21797                     return;
21798                 }
21799                 cna.push(cls);
21800             });
21801             node.className = cna.length ? cna.join(' ') : '';
21802             if (!cna.length) {
21803                 node.removeAttribute("class");
21804             }
21805         }
21806         
21807         if (node.hasAttribute("lang")) {
21808             node.removeAttribute("lang");
21809         }
21810         
21811         if (node.hasAttribute("style")) {
21812             
21813             var styles = node.getAttribute("style").split(";");
21814             var nstyle = [];
21815             Roo.each(styles, function(s) {
21816                 if (!s.match(/:/)) {
21817                     return;
21818                 }
21819                 var kv = s.split(":");
21820                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21821                     return;
21822                 }
21823                 // what ever is left... we allow.
21824                 nstyle.push(s);
21825             });
21826             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21827             if (!nstyle.length) {
21828                 node.removeAttribute('style');
21829             }
21830         }
21831         this.iterateChildren(node, this.cleanWord);
21832         
21833         
21834         
21835     },
21836     /**
21837      * iterateChildren of a Node, calling fn each time, using this as the scole..
21838      * @param {DomNode} node node to iterate children of.
21839      * @param {Function} fn method of this class to call on each item.
21840      */
21841     iterateChildren : function(node, fn)
21842     {
21843         if (!node.childNodes.length) {
21844                 return;
21845         }
21846         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21847            fn.call(this, node.childNodes[i])
21848         }
21849     },
21850     
21851     
21852     /**
21853      * cleanTableWidths.
21854      *
21855      * Quite often pasting from word etc.. results in tables with column and widths.
21856      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21857      *
21858      */
21859     cleanTableWidths : function(node)
21860     {
21861          
21862          
21863         if (!node) {
21864             this.cleanTableWidths(this.doc.body);
21865             return;
21866         }
21867         
21868         // ignore list...
21869         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21870             return; 
21871         }
21872         Roo.log(node.tagName);
21873         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21874             this.iterateChildren(node, this.cleanTableWidths);
21875             return;
21876         }
21877         if (node.hasAttribute('width')) {
21878             node.removeAttribute('width');
21879         }
21880         
21881          
21882         if (node.hasAttribute("style")) {
21883             // pretty basic...
21884             
21885             var styles = node.getAttribute("style").split(";");
21886             var nstyle = [];
21887             Roo.each(styles, function(s) {
21888                 if (!s.match(/:/)) {
21889                     return;
21890                 }
21891                 var kv = s.split(":");
21892                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21893                     return;
21894                 }
21895                 // what ever is left... we allow.
21896                 nstyle.push(s);
21897             });
21898             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21899             if (!nstyle.length) {
21900                 node.removeAttribute('style');
21901             }
21902         }
21903         
21904         this.iterateChildren(node, this.cleanTableWidths);
21905         
21906         
21907     },
21908     
21909     
21910     
21911     
21912     domToHTML : function(currentElement, depth, nopadtext) {
21913         
21914         depth = depth || 0;
21915         nopadtext = nopadtext || false;
21916     
21917         if (!currentElement) {
21918             return this.domToHTML(this.doc.body);
21919         }
21920         
21921         //Roo.log(currentElement);
21922         var j;
21923         var allText = false;
21924         var nodeName = currentElement.nodeName;
21925         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21926         
21927         if  (nodeName == '#text') {
21928             
21929             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21930         }
21931         
21932         
21933         var ret = '';
21934         if (nodeName != 'BODY') {
21935              
21936             var i = 0;
21937             // Prints the node tagName, such as <A>, <IMG>, etc
21938             if (tagName) {
21939                 var attr = [];
21940                 for(i = 0; i < currentElement.attributes.length;i++) {
21941                     // quoting?
21942                     var aname = currentElement.attributes.item(i).name;
21943                     if (!currentElement.attributes.item(i).value.length) {
21944                         continue;
21945                     }
21946                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21947                 }
21948                 
21949                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21950             } 
21951             else {
21952                 
21953                 // eack
21954             }
21955         } else {
21956             tagName = false;
21957         }
21958         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21959             return ret;
21960         }
21961         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21962             nopadtext = true;
21963         }
21964         
21965         
21966         // Traverse the tree
21967         i = 0;
21968         var currentElementChild = currentElement.childNodes.item(i);
21969         var allText = true;
21970         var innerHTML  = '';
21971         lastnode = '';
21972         while (currentElementChild) {
21973             // Formatting code (indent the tree so it looks nice on the screen)
21974             var nopad = nopadtext;
21975             if (lastnode == 'SPAN') {
21976                 nopad  = true;
21977             }
21978             // text
21979             if  (currentElementChild.nodeName == '#text') {
21980                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21981                 toadd = nopadtext ? toadd : toadd.trim();
21982                 if (!nopad && toadd.length > 80) {
21983                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21984                 }
21985                 innerHTML  += toadd;
21986                 
21987                 i++;
21988                 currentElementChild = currentElement.childNodes.item(i);
21989                 lastNode = '';
21990                 continue;
21991             }
21992             allText = false;
21993             
21994             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21995                 
21996             // Recursively traverse the tree structure of the child node
21997             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21998             lastnode = currentElementChild.nodeName;
21999             i++;
22000             currentElementChild=currentElement.childNodes.item(i);
22001         }
22002         
22003         ret += innerHTML;
22004         
22005         if (!allText) {
22006                 // The remaining code is mostly for formatting the tree
22007             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22008         }
22009         
22010         
22011         if (tagName) {
22012             ret+= "</"+tagName+">";
22013         }
22014         return ret;
22015         
22016     },
22017         
22018     applyBlacklists : function()
22019     {
22020         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22021         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22022         
22023         this.white = [];
22024         this.black = [];
22025         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22026             if (b.indexOf(tag) > -1) {
22027                 return;
22028             }
22029             this.white.push(tag);
22030             
22031         }, this);
22032         
22033         Roo.each(w, function(tag) {
22034             if (b.indexOf(tag) > -1) {
22035                 return;
22036             }
22037             if (this.white.indexOf(tag) > -1) {
22038                 return;
22039             }
22040             this.white.push(tag);
22041             
22042         }, this);
22043         
22044         
22045         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22046             if (w.indexOf(tag) > -1) {
22047                 return;
22048             }
22049             this.black.push(tag);
22050             
22051         }, this);
22052         
22053         Roo.each(b, function(tag) {
22054             if (w.indexOf(tag) > -1) {
22055                 return;
22056             }
22057             if (this.black.indexOf(tag) > -1) {
22058                 return;
22059             }
22060             this.black.push(tag);
22061             
22062         }, this);
22063         
22064         
22065         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22066         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22067         
22068         this.cwhite = [];
22069         this.cblack = [];
22070         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22071             if (b.indexOf(tag) > -1) {
22072                 return;
22073             }
22074             this.cwhite.push(tag);
22075             
22076         }, this);
22077         
22078         Roo.each(w, function(tag) {
22079             if (b.indexOf(tag) > -1) {
22080                 return;
22081             }
22082             if (this.cwhite.indexOf(tag) > -1) {
22083                 return;
22084             }
22085             this.cwhite.push(tag);
22086             
22087         }, this);
22088         
22089         
22090         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22091             if (w.indexOf(tag) > -1) {
22092                 return;
22093             }
22094             this.cblack.push(tag);
22095             
22096         }, this);
22097         
22098         Roo.each(b, function(tag) {
22099             if (w.indexOf(tag) > -1) {
22100                 return;
22101             }
22102             if (this.cblack.indexOf(tag) > -1) {
22103                 return;
22104             }
22105             this.cblack.push(tag);
22106             
22107         }, this);
22108     },
22109     
22110     setStylesheets : function(stylesheets)
22111     {
22112         if(typeof(stylesheets) == 'string'){
22113             Roo.get(this.iframe.contentDocument.head).createChild({
22114                 tag : 'link',
22115                 rel : 'stylesheet',
22116                 type : 'text/css',
22117                 href : stylesheets
22118             });
22119             
22120             return;
22121         }
22122         var _this = this;
22123      
22124         Roo.each(stylesheets, function(s) {
22125             if(!s.length){
22126                 return;
22127             }
22128             
22129             Roo.get(_this.iframe.contentDocument.head).createChild({
22130                 tag : 'link',
22131                 rel : 'stylesheet',
22132                 type : 'text/css',
22133                 href : s
22134             });
22135         });
22136
22137         
22138     },
22139     
22140     removeStylesheets : function()
22141     {
22142         var _this = this;
22143         
22144         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22145             s.remove();
22146         });
22147     },
22148     
22149     setStyle : function(style)
22150     {
22151         Roo.get(this.iframe.contentDocument.head).createChild({
22152             tag : 'style',
22153             type : 'text/css',
22154             html : style
22155         });
22156
22157         return;
22158     }
22159     
22160     // hide stuff that is not compatible
22161     /**
22162      * @event blur
22163      * @hide
22164      */
22165     /**
22166      * @event change
22167      * @hide
22168      */
22169     /**
22170      * @event focus
22171      * @hide
22172      */
22173     /**
22174      * @event specialkey
22175      * @hide
22176      */
22177     /**
22178      * @cfg {String} fieldClass @hide
22179      */
22180     /**
22181      * @cfg {String} focusClass @hide
22182      */
22183     /**
22184      * @cfg {String} autoCreate @hide
22185      */
22186     /**
22187      * @cfg {String} inputType @hide
22188      */
22189     /**
22190      * @cfg {String} invalidClass @hide
22191      */
22192     /**
22193      * @cfg {String} invalidText @hide
22194      */
22195     /**
22196      * @cfg {String} msgFx @hide
22197      */
22198     /**
22199      * @cfg {String} validateOnBlur @hide
22200      */
22201 });
22202
22203 Roo.HtmlEditorCore.white = [
22204         'area', 'br', 'img', 'input', 'hr', 'wbr',
22205         
22206        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22207        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22208        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22209        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22210        'table',   'ul',         'xmp', 
22211        
22212        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22213       'thead',   'tr', 
22214      
22215       'dir', 'menu', 'ol', 'ul', 'dl',
22216        
22217       'embed',  'object'
22218 ];
22219
22220
22221 Roo.HtmlEditorCore.black = [
22222     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22223         'applet', // 
22224         'base',   'basefont', 'bgsound', 'blink',  'body', 
22225         'frame',  'frameset', 'head',    'html',   'ilayer', 
22226         'iframe', 'layer',  'link',     'meta',    'object',   
22227         'script', 'style' ,'title',  'xml' // clean later..
22228 ];
22229 Roo.HtmlEditorCore.clean = [
22230     'script', 'style', 'title', 'xml'
22231 ];
22232 Roo.HtmlEditorCore.remove = [
22233     'font'
22234 ];
22235 // attributes..
22236
22237 Roo.HtmlEditorCore.ablack = [
22238     'on'
22239 ];
22240     
22241 Roo.HtmlEditorCore.aclean = [ 
22242     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22243 ];
22244
22245 // protocols..
22246 Roo.HtmlEditorCore.pwhite= [
22247         'http',  'https',  'mailto'
22248 ];
22249
22250 // white listed style attributes.
22251 Roo.HtmlEditorCore.cwhite= [
22252       //  'text-align', /// default is to allow most things..
22253       
22254          
22255 //        'font-size'//??
22256 ];
22257
22258 // black listed style attributes.
22259 Roo.HtmlEditorCore.cblack= [
22260       //  'font-size' -- this can be set by the project 
22261 ];
22262
22263
22264 Roo.HtmlEditorCore.swapCodes   =[ 
22265     [    8211, "&#8211;" ], 
22266     [    8212, "&#8212;" ], 
22267     [    8216,  "'" ],  
22268     [    8217, "'" ],  
22269     [    8220, '"' ],  
22270     [    8221, '"' ],  
22271     [    8226, "*" ],  
22272     [    8230, "..." ]
22273 ]; 
22274
22275     //<script type="text/javascript">
22276
22277 /*
22278  * Ext JS Library 1.1.1
22279  * Copyright(c) 2006-2007, Ext JS, LLC.
22280  * Licence LGPL
22281  * 
22282  */
22283  
22284  
22285 Roo.form.HtmlEditor = function(config){
22286     
22287     
22288     
22289     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22290     
22291     if (!this.toolbars) {
22292         this.toolbars = [];
22293     }
22294     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22295     
22296     
22297 };
22298
22299 /**
22300  * @class Roo.form.HtmlEditor
22301  * @extends Roo.form.Field
22302  * Provides a lightweight HTML Editor component.
22303  *
22304  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22305  * 
22306  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22307  * supported by this editor.</b><br/><br/>
22308  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22309  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22310  */
22311 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22312     /**
22313      * @cfg {Boolean} clearUp
22314      */
22315     clearUp : true,
22316       /**
22317      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22318      */
22319     toolbars : false,
22320    
22321      /**
22322      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22323      *                        Roo.resizable.
22324      */
22325     resizable : false,
22326      /**
22327      * @cfg {Number} height (in pixels)
22328      */   
22329     height: 300,
22330    /**
22331      * @cfg {Number} width (in pixels)
22332      */   
22333     width: 500,
22334     
22335     /**
22336      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22337      * 
22338      */
22339     stylesheets: false,
22340     
22341     
22342      /**
22343      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22344      * 
22345      */
22346     cblack: false,
22347     /**
22348      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22349      * 
22350      */
22351     cwhite: false,
22352     
22353      /**
22354      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22355      * 
22356      */
22357     black: false,
22358     /**
22359      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22360      * 
22361      */
22362     white: false,
22363     /**
22364      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22365      */
22366     allowComments: false,
22367     
22368     // id of frame..
22369     frameId: false,
22370     
22371     // private properties
22372     validationEvent : false,
22373     deferHeight: true,
22374     initialized : false,
22375     activated : false,
22376     
22377     onFocus : Roo.emptyFn,
22378     iframePad:3,
22379     hideMode:'offsets',
22380     
22381     actionMode : 'container', // defaults to hiding it...
22382     
22383     defaultAutoCreate : { // modified by initCompnoent..
22384         tag: "textarea",
22385         style:"width:500px;height:300px;",
22386         autocomplete: "new-password"
22387     },
22388
22389     // private
22390     initComponent : function(){
22391         this.addEvents({
22392             /**
22393              * @event initialize
22394              * Fires when the editor is fully initialized (including the iframe)
22395              * @param {HtmlEditor} this
22396              */
22397             initialize: true,
22398             /**
22399              * @event activate
22400              * Fires when the editor is first receives the focus. Any insertion must wait
22401              * until after this event.
22402              * @param {HtmlEditor} this
22403              */
22404             activate: true,
22405              /**
22406              * @event beforesync
22407              * Fires before the textarea is updated with content from the editor iframe. Return false
22408              * to cancel the sync.
22409              * @param {HtmlEditor} this
22410              * @param {String} html
22411              */
22412             beforesync: true,
22413              /**
22414              * @event beforepush
22415              * Fires before the iframe editor is updated with content from the textarea. Return false
22416              * to cancel the push.
22417              * @param {HtmlEditor} this
22418              * @param {String} html
22419              */
22420             beforepush: true,
22421              /**
22422              * @event sync
22423              * Fires when the textarea is updated with content from the editor iframe.
22424              * @param {HtmlEditor} this
22425              * @param {String} html
22426              */
22427             sync: true,
22428              /**
22429              * @event push
22430              * Fires when the iframe editor is updated with content from the textarea.
22431              * @param {HtmlEditor} this
22432              * @param {String} html
22433              */
22434             push: true,
22435              /**
22436              * @event editmodechange
22437              * Fires when the editor switches edit modes
22438              * @param {HtmlEditor} this
22439              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22440              */
22441             editmodechange: true,
22442             /**
22443              * @event editorevent
22444              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22445              * @param {HtmlEditor} this
22446              */
22447             editorevent: true,
22448             /**
22449              * @event firstfocus
22450              * Fires when on first focus - needed by toolbars..
22451              * @param {HtmlEditor} this
22452              */
22453             firstfocus: true,
22454             /**
22455              * @event autosave
22456              * Auto save the htmlEditor value as a file into Events
22457              * @param {HtmlEditor} this
22458              */
22459             autosave: true,
22460             /**
22461              * @event savedpreview
22462              * preview the saved version of htmlEditor
22463              * @param {HtmlEditor} this
22464              */
22465             savedpreview: true,
22466             
22467             /**
22468             * @event stylesheetsclick
22469             * Fires when press the Sytlesheets button
22470             * @param {Roo.HtmlEditorCore} this
22471             */
22472             stylesheetsclick: true
22473         });
22474         this.defaultAutoCreate =  {
22475             tag: "textarea",
22476             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22477             autocomplete: "new-password"
22478         };
22479     },
22480
22481     /**
22482      * Protected method that will not generally be called directly. It
22483      * is called when the editor creates its toolbar. Override this method if you need to
22484      * add custom toolbar buttons.
22485      * @param {HtmlEditor} editor
22486      */
22487     createToolbar : function(editor){
22488         Roo.log("create toolbars");
22489         if (!editor.toolbars || !editor.toolbars.length) {
22490             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22491         }
22492         
22493         for (var i =0 ; i < editor.toolbars.length;i++) {
22494             editor.toolbars[i] = Roo.factory(
22495                     typeof(editor.toolbars[i]) == 'string' ?
22496                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22497                 Roo.form.HtmlEditor);
22498             editor.toolbars[i].init(editor);
22499         }
22500          
22501         
22502     },
22503
22504      
22505     // private
22506     onRender : function(ct, position)
22507     {
22508         var _t = this;
22509         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22510         
22511         this.wrap = this.el.wrap({
22512             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22513         });
22514         
22515         this.editorcore.onRender(ct, position);
22516          
22517         if (this.resizable) {
22518             this.resizeEl = new Roo.Resizable(this.wrap, {
22519                 pinned : true,
22520                 wrap: true,
22521                 dynamic : true,
22522                 minHeight : this.height,
22523                 height: this.height,
22524                 handles : this.resizable,
22525                 width: this.width,
22526                 listeners : {
22527                     resize : function(r, w, h) {
22528                         _t.onResize(w,h); // -something
22529                     }
22530                 }
22531             });
22532             
22533         }
22534         this.createToolbar(this);
22535        
22536         
22537         if(!this.width){
22538             this.setSize(this.wrap.getSize());
22539         }
22540         if (this.resizeEl) {
22541             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22542             // should trigger onReize..
22543         }
22544         
22545         this.keyNav = new Roo.KeyNav(this.el, {
22546             
22547             "tab" : function(e){
22548                 e.preventDefault();
22549                 
22550                 var value = this.getValue();
22551                 
22552                 var start = this.el.dom.selectionStart;
22553                 var end = this.el.dom.selectionEnd;
22554                 
22555                 if(!e.shiftKey){
22556                     
22557                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22558                     this.el.dom.setSelectionRange(end + 1, end + 1);
22559                     return;
22560                 }
22561                 
22562                 var f = value.substring(0, start).split("\t");
22563                 
22564                 if(f.pop().length != 0){
22565                     return;
22566                 }
22567                 
22568                 this.setValue(f.join("\t") + value.substring(end));
22569                 this.el.dom.setSelectionRange(start - 1, start - 1);
22570                 
22571             },
22572             
22573             "home" : function(e){
22574                 e.preventDefault();
22575                 
22576                 var curr = this.el.dom.selectionStart;
22577                 var lines = this.getValue().split("\n");
22578                 
22579                 if(!lines.length){
22580                     return;
22581                 }
22582                 
22583                 if(e.ctrlKey){
22584                     this.el.dom.setSelectionRange(0, 0);
22585                     return;
22586                 }
22587                 
22588                 var pos = 0;
22589                 
22590                 for (var i = 0; i < lines.length;i++) {
22591                     pos += lines[i].length;
22592                     
22593                     if(i != 0){
22594                         pos += 1;
22595                     }
22596                     
22597                     if(pos < curr){
22598                         continue;
22599                     }
22600                     
22601                     pos -= lines[i].length;
22602                     
22603                     break;
22604                 }
22605                 
22606                 if(!e.shiftKey){
22607                     this.el.dom.setSelectionRange(pos, pos);
22608                     return;
22609                 }
22610                 
22611                 this.el.dom.selectionStart = pos;
22612                 this.el.dom.selectionEnd = curr;
22613             },
22614             
22615             "end" : function(e){
22616                 e.preventDefault();
22617                 
22618                 var curr = this.el.dom.selectionStart;
22619                 var lines = this.getValue().split("\n");
22620                 
22621                 if(!lines.length){
22622                     return;
22623                 }
22624                 
22625                 if(e.ctrlKey){
22626                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22627                     return;
22628                 }
22629                 
22630                 var pos = 0;
22631                 
22632                 for (var i = 0; i < lines.length;i++) {
22633                     
22634                     pos += lines[i].length;
22635                     
22636                     if(i != 0){
22637                         pos += 1;
22638                     }
22639                     
22640                     if(pos < curr){
22641                         continue;
22642                     }
22643                     
22644                     break;
22645                 }
22646                 
22647                 if(!e.shiftKey){
22648                     this.el.dom.setSelectionRange(pos, pos);
22649                     return;
22650                 }
22651                 
22652                 this.el.dom.selectionStart = curr;
22653                 this.el.dom.selectionEnd = pos;
22654             },
22655
22656             scope : this,
22657
22658             doRelay : function(foo, bar, hname){
22659                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22660             },
22661
22662             forceKeyDown: true
22663         });
22664         
22665 //        if(this.autosave && this.w){
22666 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22667 //        }
22668     },
22669
22670     // private
22671     onResize : function(w, h)
22672     {
22673         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22674         var ew = false;
22675         var eh = false;
22676         
22677         if(this.el ){
22678             if(typeof w == 'number'){
22679                 var aw = w - this.wrap.getFrameWidth('lr');
22680                 this.el.setWidth(this.adjustWidth('textarea', aw));
22681                 ew = aw;
22682             }
22683             if(typeof h == 'number'){
22684                 var tbh = 0;
22685                 for (var i =0; i < this.toolbars.length;i++) {
22686                     // fixme - ask toolbars for heights?
22687                     tbh += this.toolbars[i].tb.el.getHeight();
22688                     if (this.toolbars[i].footer) {
22689                         tbh += this.toolbars[i].footer.el.getHeight();
22690                     }
22691                 }
22692                 
22693                 
22694                 
22695                 
22696                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22697                 ah -= 5; // knock a few pixes off for look..
22698 //                Roo.log(ah);
22699                 this.el.setHeight(this.adjustWidth('textarea', ah));
22700                 var eh = ah;
22701             }
22702         }
22703         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22704         this.editorcore.onResize(ew,eh);
22705         
22706     },
22707
22708     /**
22709      * Toggles the editor between standard and source edit mode.
22710      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22711      */
22712     toggleSourceEdit : function(sourceEditMode)
22713     {
22714         this.editorcore.toggleSourceEdit(sourceEditMode);
22715         
22716         if(this.editorcore.sourceEditMode){
22717             Roo.log('editor - showing textarea');
22718             
22719 //            Roo.log('in');
22720 //            Roo.log(this.syncValue());
22721             this.editorcore.syncValue();
22722             this.el.removeClass('x-hidden');
22723             this.el.dom.removeAttribute('tabIndex');
22724             this.el.focus();
22725             
22726             for (var i = 0; i < this.toolbars.length; i++) {
22727                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22728                     this.toolbars[i].tb.hide();
22729                     this.toolbars[i].footer.hide();
22730                 }
22731             }
22732             
22733         }else{
22734             Roo.log('editor - hiding textarea');
22735 //            Roo.log('out')
22736 //            Roo.log(this.pushValue()); 
22737             this.editorcore.pushValue();
22738             
22739             this.el.addClass('x-hidden');
22740             this.el.dom.setAttribute('tabIndex', -1);
22741             
22742             for (var i = 0; i < this.toolbars.length; i++) {
22743                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22744                     this.toolbars[i].tb.show();
22745                     this.toolbars[i].footer.show();
22746                 }
22747             }
22748             
22749             //this.deferFocus();
22750         }
22751         
22752         this.setSize(this.wrap.getSize());
22753         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22754         
22755         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22756     },
22757  
22758     // private (for BoxComponent)
22759     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22760
22761     // private (for BoxComponent)
22762     getResizeEl : function(){
22763         return this.wrap;
22764     },
22765
22766     // private (for BoxComponent)
22767     getPositionEl : function(){
22768         return this.wrap;
22769     },
22770
22771     // private
22772     initEvents : function(){
22773         this.originalValue = this.getValue();
22774     },
22775
22776     /**
22777      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22778      * @method
22779      */
22780     markInvalid : Roo.emptyFn,
22781     /**
22782      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22783      * @method
22784      */
22785     clearInvalid : Roo.emptyFn,
22786
22787     setValue : function(v){
22788         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22789         this.editorcore.pushValue();
22790     },
22791
22792      
22793     // private
22794     deferFocus : function(){
22795         this.focus.defer(10, this);
22796     },
22797
22798     // doc'ed in Field
22799     focus : function(){
22800         this.editorcore.focus();
22801         
22802     },
22803       
22804
22805     // private
22806     onDestroy : function(){
22807         
22808         
22809         
22810         if(this.rendered){
22811             
22812             for (var i =0; i < this.toolbars.length;i++) {
22813                 // fixme - ask toolbars for heights?
22814                 this.toolbars[i].onDestroy();
22815             }
22816             
22817             this.wrap.dom.innerHTML = '';
22818             this.wrap.remove();
22819         }
22820     },
22821
22822     // private
22823     onFirstFocus : function(){
22824         //Roo.log("onFirstFocus");
22825         this.editorcore.onFirstFocus();
22826          for (var i =0; i < this.toolbars.length;i++) {
22827             this.toolbars[i].onFirstFocus();
22828         }
22829         
22830     },
22831     
22832     // private
22833     syncValue : function()
22834     {
22835         this.editorcore.syncValue();
22836     },
22837     
22838     pushValue : function()
22839     {
22840         this.editorcore.pushValue();
22841     },
22842     
22843     setStylesheets : function(stylesheets)
22844     {
22845         this.editorcore.setStylesheets(stylesheets);
22846     },
22847     
22848     removeStylesheets : function()
22849     {
22850         this.editorcore.removeStylesheets();
22851     }
22852      
22853     
22854     // hide stuff that is not compatible
22855     /**
22856      * @event blur
22857      * @hide
22858      */
22859     /**
22860      * @event change
22861      * @hide
22862      */
22863     /**
22864      * @event focus
22865      * @hide
22866      */
22867     /**
22868      * @event specialkey
22869      * @hide
22870      */
22871     /**
22872      * @cfg {String} fieldClass @hide
22873      */
22874     /**
22875      * @cfg {String} focusClass @hide
22876      */
22877     /**
22878      * @cfg {String} autoCreate @hide
22879      */
22880     /**
22881      * @cfg {String} inputType @hide
22882      */
22883     /**
22884      * @cfg {String} invalidClass @hide
22885      */
22886     /**
22887      * @cfg {String} invalidText @hide
22888      */
22889     /**
22890      * @cfg {String} msgFx @hide
22891      */
22892     /**
22893      * @cfg {String} validateOnBlur @hide
22894      */
22895 });
22896  
22897     // <script type="text/javascript">
22898 /*
22899  * Based on
22900  * Ext JS Library 1.1.1
22901  * Copyright(c) 2006-2007, Ext JS, LLC.
22902  *  
22903  
22904  */
22905
22906 /**
22907  * @class Roo.form.HtmlEditorToolbar1
22908  * Basic Toolbar
22909  * 
22910  * Usage:
22911  *
22912  new Roo.form.HtmlEditor({
22913     ....
22914     toolbars : [
22915         new Roo.form.HtmlEditorToolbar1({
22916             disable : { fonts: 1 , format: 1, ..., ... , ...],
22917             btns : [ .... ]
22918         })
22919     }
22920      
22921  * 
22922  * @cfg {Object} disable List of elements to disable..
22923  * @cfg {Array} btns List of additional buttons.
22924  * 
22925  * 
22926  * NEEDS Extra CSS? 
22927  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22928  */
22929  
22930 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22931 {
22932     
22933     Roo.apply(this, config);
22934     
22935     // default disabled, based on 'good practice'..
22936     this.disable = this.disable || {};
22937     Roo.applyIf(this.disable, {
22938         fontSize : true,
22939         colors : true,
22940         specialElements : true
22941     });
22942     
22943     
22944     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22945     // dont call parent... till later.
22946 }
22947
22948 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22949     
22950     tb: false,
22951     
22952     rendered: false,
22953     
22954     editor : false,
22955     editorcore : false,
22956     /**
22957      * @cfg {Object} disable  List of toolbar elements to disable
22958          
22959      */
22960     disable : false,
22961     
22962     
22963      /**
22964      * @cfg {String} createLinkText The default text for the create link prompt
22965      */
22966     createLinkText : 'Please enter the URL for the link:',
22967     /**
22968      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22969      */
22970     defaultLinkValue : 'http:/'+'/',
22971    
22972     
22973       /**
22974      * @cfg {Array} fontFamilies An array of available font families
22975      */
22976     fontFamilies : [
22977         'Arial',
22978         'Courier New',
22979         'Tahoma',
22980         'Times New Roman',
22981         'Verdana'
22982     ],
22983     
22984     specialChars : [
22985            "&#169;",
22986           "&#174;",     
22987           "&#8482;",    
22988           "&#163;" ,    
22989          // "&#8212;",    
22990           "&#8230;",    
22991           "&#247;" ,    
22992         //  "&#225;" ,     ?? a acute?
22993            "&#8364;"    , //Euro
22994        //   "&#8220;"    ,
22995         //  "&#8221;"    ,
22996         //  "&#8226;"    ,
22997           "&#176;"  //   , // degrees
22998
22999          // "&#233;"     , // e ecute
23000          // "&#250;"     , // u ecute?
23001     ],
23002     
23003     specialElements : [
23004         {
23005             text: "Insert Table",
23006             xtype: 'MenuItem',
23007             xns : Roo.Menu,
23008             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23009                 
23010         },
23011         {    
23012             text: "Insert Image",
23013             xtype: 'MenuItem',
23014             xns : Roo.Menu,
23015             ihtml : '<img src="about:blank"/>'
23016             
23017         }
23018         
23019          
23020     ],
23021     
23022     
23023     inputElements : [ 
23024             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23025             "input:submit", "input:button", "select", "textarea", "label" ],
23026     formats : [
23027         ["p"] ,  
23028         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23029         ["pre"],[ "code"], 
23030         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23031         ['div'],['span'],
23032         ['sup'],['sub']
23033     ],
23034     
23035     cleanStyles : [
23036         "font-size"
23037     ],
23038      /**
23039      * @cfg {String} defaultFont default font to use.
23040      */
23041     defaultFont: 'tahoma',
23042    
23043     fontSelect : false,
23044     
23045     
23046     formatCombo : false,
23047     
23048     init : function(editor)
23049     {
23050         this.editor = editor;
23051         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23052         var editorcore = this.editorcore;
23053         
23054         var _t = this;
23055         
23056         var fid = editorcore.frameId;
23057         var etb = this;
23058         function btn(id, toggle, handler){
23059             var xid = fid + '-'+ id ;
23060             return {
23061                 id : xid,
23062                 cmd : id,
23063                 cls : 'x-btn-icon x-edit-'+id,
23064                 enableToggle:toggle !== false,
23065                 scope: _t, // was editor...
23066                 handler:handler||_t.relayBtnCmd,
23067                 clickEvent:'mousedown',
23068                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23069                 tabIndex:-1
23070             };
23071         }
23072         
23073         
23074         
23075         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23076         this.tb = tb;
23077          // stop form submits
23078         tb.el.on('click', function(e){
23079             e.preventDefault(); // what does this do?
23080         });
23081
23082         if(!this.disable.font) { // && !Roo.isSafari){
23083             /* why no safari for fonts 
23084             editor.fontSelect = tb.el.createChild({
23085                 tag:'select',
23086                 tabIndex: -1,
23087                 cls:'x-font-select',
23088                 html: this.createFontOptions()
23089             });
23090             
23091             editor.fontSelect.on('change', function(){
23092                 var font = editor.fontSelect.dom.value;
23093                 editor.relayCmd('fontname', font);
23094                 editor.deferFocus();
23095             }, editor);
23096             
23097             tb.add(
23098                 editor.fontSelect.dom,
23099                 '-'
23100             );
23101             */
23102             
23103         };
23104         if(!this.disable.formats){
23105             this.formatCombo = new Roo.form.ComboBox({
23106                 store: new Roo.data.SimpleStore({
23107                     id : 'tag',
23108                     fields: ['tag'],
23109                     data : this.formats // from states.js
23110                 }),
23111                 blockFocus : true,
23112                 name : '',
23113                 //autoCreate : {tag: "div",  size: "20"},
23114                 displayField:'tag',
23115                 typeAhead: false,
23116                 mode: 'local',
23117                 editable : false,
23118                 triggerAction: 'all',
23119                 emptyText:'Add tag',
23120                 selectOnFocus:true,
23121                 width:135,
23122                 listeners : {
23123                     'select': function(c, r, i) {
23124                         editorcore.insertTag(r.get('tag'));
23125                         editor.focus();
23126                     }
23127                 }
23128
23129             });
23130             tb.addField(this.formatCombo);
23131             
23132         }
23133         
23134         if(!this.disable.format){
23135             tb.add(
23136                 btn('bold'),
23137                 btn('italic'),
23138                 btn('underline'),
23139                 btn('strikethrough')
23140             );
23141         };
23142         if(!this.disable.fontSize){
23143             tb.add(
23144                 '-',
23145                 
23146                 
23147                 btn('increasefontsize', false, editorcore.adjustFont),
23148                 btn('decreasefontsize', false, editorcore.adjustFont)
23149             );
23150         };
23151         
23152         
23153         if(!this.disable.colors){
23154             tb.add(
23155                 '-', {
23156                     id:editorcore.frameId +'-forecolor',
23157                     cls:'x-btn-icon x-edit-forecolor',
23158                     clickEvent:'mousedown',
23159                     tooltip: this.buttonTips['forecolor'] || undefined,
23160                     tabIndex:-1,
23161                     menu : new Roo.menu.ColorMenu({
23162                         allowReselect: true,
23163                         focus: Roo.emptyFn,
23164                         value:'000000',
23165                         plain:true,
23166                         selectHandler: function(cp, color){
23167                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23168                             editor.deferFocus();
23169                         },
23170                         scope: editorcore,
23171                         clickEvent:'mousedown'
23172                     })
23173                 }, {
23174                     id:editorcore.frameId +'backcolor',
23175                     cls:'x-btn-icon x-edit-backcolor',
23176                     clickEvent:'mousedown',
23177                     tooltip: this.buttonTips['backcolor'] || undefined,
23178                     tabIndex:-1,
23179                     menu : new Roo.menu.ColorMenu({
23180                         focus: Roo.emptyFn,
23181                         value:'FFFFFF',
23182                         plain:true,
23183                         allowReselect: true,
23184                         selectHandler: function(cp, color){
23185                             if(Roo.isGecko){
23186                                 editorcore.execCmd('useCSS', false);
23187                                 editorcore.execCmd('hilitecolor', color);
23188                                 editorcore.execCmd('useCSS', true);
23189                                 editor.deferFocus();
23190                             }else{
23191                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23192                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23193                                 editor.deferFocus();
23194                             }
23195                         },
23196                         scope:editorcore,
23197                         clickEvent:'mousedown'
23198                     })
23199                 }
23200             );
23201         };
23202         // now add all the items...
23203         
23204
23205         if(!this.disable.alignments){
23206             tb.add(
23207                 '-',
23208                 btn('justifyleft'),
23209                 btn('justifycenter'),
23210                 btn('justifyright')
23211             );
23212         };
23213
23214         //if(!Roo.isSafari){
23215             if(!this.disable.links){
23216                 tb.add(
23217                     '-',
23218                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23219                 );
23220             };
23221
23222             if(!this.disable.lists){
23223                 tb.add(
23224                     '-',
23225                     btn('insertorderedlist'),
23226                     btn('insertunorderedlist')
23227                 );
23228             }
23229             if(!this.disable.sourceEdit){
23230                 tb.add(
23231                     '-',
23232                     btn('sourceedit', true, function(btn){
23233                         this.toggleSourceEdit(btn.pressed);
23234                     })
23235                 );
23236             }
23237         //}
23238         
23239         var smenu = { };
23240         // special menu.. - needs to be tidied up..
23241         if (!this.disable.special) {
23242             smenu = {
23243                 text: "&#169;",
23244                 cls: 'x-edit-none',
23245                 
23246                 menu : {
23247                     items : []
23248                 }
23249             };
23250             for (var i =0; i < this.specialChars.length; i++) {
23251                 smenu.menu.items.push({
23252                     
23253                     html: this.specialChars[i],
23254                     handler: function(a,b) {
23255                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23256                         //editor.insertAtCursor(a.html);
23257                         
23258                     },
23259                     tabIndex:-1
23260                 });
23261             }
23262             
23263             
23264             tb.add(smenu);
23265             
23266             
23267         }
23268         
23269         var cmenu = { };
23270         if (!this.disable.cleanStyles) {
23271             cmenu = {
23272                 cls: 'x-btn-icon x-btn-clear',
23273                 
23274                 menu : {
23275                     items : []
23276                 }
23277             };
23278             for (var i =0; i < this.cleanStyles.length; i++) {
23279                 cmenu.menu.items.push({
23280                     actiontype : this.cleanStyles[i],
23281                     html: 'Remove ' + this.cleanStyles[i],
23282                     handler: function(a,b) {
23283 //                        Roo.log(a);
23284 //                        Roo.log(b);
23285                         var c = Roo.get(editorcore.doc.body);
23286                         c.select('[style]').each(function(s) {
23287                             s.dom.style.removeProperty(a.actiontype);
23288                         });
23289                         editorcore.syncValue();
23290                     },
23291                     tabIndex:-1
23292                 });
23293             }
23294              cmenu.menu.items.push({
23295                 actiontype : 'tablewidths',
23296                 html: 'Remove Table Widths',
23297                 handler: function(a,b) {
23298                     editorcore.cleanTableWidths();
23299                     editorcore.syncValue();
23300                 },
23301                 tabIndex:-1
23302             });
23303             cmenu.menu.items.push({
23304                 actiontype : 'word',
23305                 html: 'Remove MS Word Formating',
23306                 handler: function(a,b) {
23307                     editorcore.cleanWord();
23308                     editorcore.syncValue();
23309                 },
23310                 tabIndex:-1
23311             });
23312             
23313             cmenu.menu.items.push({
23314                 actiontype : 'all',
23315                 html: 'Remove All Styles',
23316                 handler: function(a,b) {
23317                     
23318                     var c = Roo.get(editorcore.doc.body);
23319                     c.select('[style]').each(function(s) {
23320                         s.dom.removeAttribute('style');
23321                     });
23322                     editorcore.syncValue();
23323                 },
23324                 tabIndex:-1
23325             });
23326             
23327             cmenu.menu.items.push({
23328                 actiontype : 'all',
23329                 html: 'Remove All CSS Classes',
23330                 handler: function(a,b) {
23331                     
23332                     var c = Roo.get(editorcore.doc.body);
23333                     c.select('[class]').each(function(s) {
23334                         s.dom.removeAttribute('class');
23335                     });
23336                     editorcore.cleanWord();
23337                     editorcore.syncValue();
23338                 },
23339                 tabIndex:-1
23340             });
23341             
23342              cmenu.menu.items.push({
23343                 actiontype : 'tidy',
23344                 html: 'Tidy HTML Source',
23345                 handler: function(a,b) {
23346                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23347                     editorcore.syncValue();
23348                 },
23349                 tabIndex:-1
23350             });
23351             
23352             
23353             tb.add(cmenu);
23354         }
23355          
23356         if (!this.disable.specialElements) {
23357             var semenu = {
23358                 text: "Other;",
23359                 cls: 'x-edit-none',
23360                 menu : {
23361                     items : []
23362                 }
23363             };
23364             for (var i =0; i < this.specialElements.length; i++) {
23365                 semenu.menu.items.push(
23366                     Roo.apply({ 
23367                         handler: function(a,b) {
23368                             editor.insertAtCursor(this.ihtml);
23369                         }
23370                     }, this.specialElements[i])
23371                 );
23372                     
23373             }
23374             
23375             tb.add(semenu);
23376             
23377             
23378         }
23379          
23380         
23381         if (this.btns) {
23382             for(var i =0; i< this.btns.length;i++) {
23383                 var b = Roo.factory(this.btns[i],Roo.form);
23384                 b.cls =  'x-edit-none';
23385                 
23386                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23387                     b.cls += ' x-init-enable';
23388                 }
23389                 
23390                 b.scope = editorcore;
23391                 tb.add(b);
23392             }
23393         
23394         }
23395         
23396         
23397         
23398         // disable everything...
23399         
23400         this.tb.items.each(function(item){
23401             
23402            if(
23403                 item.id != editorcore.frameId+ '-sourceedit' && 
23404                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23405             ){
23406                 
23407                 item.disable();
23408             }
23409         });
23410         this.rendered = true;
23411         
23412         // the all the btns;
23413         editor.on('editorevent', this.updateToolbar, this);
23414         // other toolbars need to implement this..
23415         //editor.on('editmodechange', this.updateToolbar, this);
23416     },
23417     
23418     
23419     relayBtnCmd : function(btn) {
23420         this.editorcore.relayCmd(btn.cmd);
23421     },
23422     // private used internally
23423     createLink : function(){
23424         Roo.log("create link?");
23425         var url = prompt(this.createLinkText, this.defaultLinkValue);
23426         if(url && url != 'http:/'+'/'){
23427             this.editorcore.relayCmd('createlink', url);
23428         }
23429     },
23430
23431     
23432     /**
23433      * Protected method that will not generally be called directly. It triggers
23434      * a toolbar update by reading the markup state of the current selection in the editor.
23435      */
23436     updateToolbar: function(){
23437
23438         if(!this.editorcore.activated){
23439             this.editor.onFirstFocus();
23440             return;
23441         }
23442
23443         var btns = this.tb.items.map, 
23444             doc = this.editorcore.doc,
23445             frameId = this.editorcore.frameId;
23446
23447         if(!this.disable.font && !Roo.isSafari){
23448             /*
23449             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23450             if(name != this.fontSelect.dom.value){
23451                 this.fontSelect.dom.value = name;
23452             }
23453             */
23454         }
23455         if(!this.disable.format){
23456             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23457             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23458             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23459             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23460         }
23461         if(!this.disable.alignments){
23462             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23463             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23464             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23465         }
23466         if(!Roo.isSafari && !this.disable.lists){
23467             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23468             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23469         }
23470         
23471         var ans = this.editorcore.getAllAncestors();
23472         if (this.formatCombo) {
23473             
23474             
23475             var store = this.formatCombo.store;
23476             this.formatCombo.setValue("");
23477             for (var i =0; i < ans.length;i++) {
23478                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23479                     // select it..
23480                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23481                     break;
23482                 }
23483             }
23484         }
23485         
23486         
23487         
23488         // hides menus... - so this cant be on a menu...
23489         Roo.menu.MenuMgr.hideAll();
23490
23491         //this.editorsyncValue();
23492     },
23493    
23494     
23495     createFontOptions : function(){
23496         var buf = [], fs = this.fontFamilies, ff, lc;
23497         
23498         
23499         
23500         for(var i = 0, len = fs.length; i< len; i++){
23501             ff = fs[i];
23502             lc = ff.toLowerCase();
23503             buf.push(
23504                 '<option value="',lc,'" style="font-family:',ff,';"',
23505                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23506                     ff,
23507                 '</option>'
23508             );
23509         }
23510         return buf.join('');
23511     },
23512     
23513     toggleSourceEdit : function(sourceEditMode){
23514         
23515         Roo.log("toolbar toogle");
23516         if(sourceEditMode === undefined){
23517             sourceEditMode = !this.sourceEditMode;
23518         }
23519         this.sourceEditMode = sourceEditMode === true;
23520         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23521         // just toggle the button?
23522         if(btn.pressed !== this.sourceEditMode){
23523             btn.toggle(this.sourceEditMode);
23524             return;
23525         }
23526         
23527         if(sourceEditMode){
23528             Roo.log("disabling buttons");
23529             this.tb.items.each(function(item){
23530                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23531                     item.disable();
23532                 }
23533             });
23534           
23535         }else{
23536             Roo.log("enabling buttons");
23537             if(this.editorcore.initialized){
23538                 this.tb.items.each(function(item){
23539                     item.enable();
23540                 });
23541             }
23542             
23543         }
23544         Roo.log("calling toggole on editor");
23545         // tell the editor that it's been pressed..
23546         this.editor.toggleSourceEdit(sourceEditMode);
23547        
23548     },
23549      /**
23550      * Object collection of toolbar tooltips for the buttons in the editor. The key
23551      * is the command id associated with that button and the value is a valid QuickTips object.
23552      * For example:
23553 <pre><code>
23554 {
23555     bold : {
23556         title: 'Bold (Ctrl+B)',
23557         text: 'Make the selected text bold.',
23558         cls: 'x-html-editor-tip'
23559     },
23560     italic : {
23561         title: 'Italic (Ctrl+I)',
23562         text: 'Make the selected text italic.',
23563         cls: 'x-html-editor-tip'
23564     },
23565     ...
23566 </code></pre>
23567     * @type Object
23568      */
23569     buttonTips : {
23570         bold : {
23571             title: 'Bold (Ctrl+B)',
23572             text: 'Make the selected text bold.',
23573             cls: 'x-html-editor-tip'
23574         },
23575         italic : {
23576             title: 'Italic (Ctrl+I)',
23577             text: 'Make the selected text italic.',
23578             cls: 'x-html-editor-tip'
23579         },
23580         underline : {
23581             title: 'Underline (Ctrl+U)',
23582             text: 'Underline the selected text.',
23583             cls: 'x-html-editor-tip'
23584         },
23585         strikethrough : {
23586             title: 'Strikethrough',
23587             text: 'Strikethrough the selected text.',
23588             cls: 'x-html-editor-tip'
23589         },
23590         increasefontsize : {
23591             title: 'Grow Text',
23592             text: 'Increase the font size.',
23593             cls: 'x-html-editor-tip'
23594         },
23595         decreasefontsize : {
23596             title: 'Shrink Text',
23597             text: 'Decrease the font size.',
23598             cls: 'x-html-editor-tip'
23599         },
23600         backcolor : {
23601             title: 'Text Highlight Color',
23602             text: 'Change the background color of the selected text.',
23603             cls: 'x-html-editor-tip'
23604         },
23605         forecolor : {
23606             title: 'Font Color',
23607             text: 'Change the color of the selected text.',
23608             cls: 'x-html-editor-tip'
23609         },
23610         justifyleft : {
23611             title: 'Align Text Left',
23612             text: 'Align text to the left.',
23613             cls: 'x-html-editor-tip'
23614         },
23615         justifycenter : {
23616             title: 'Center Text',
23617             text: 'Center text in the editor.',
23618             cls: 'x-html-editor-tip'
23619         },
23620         justifyright : {
23621             title: 'Align Text Right',
23622             text: 'Align text to the right.',
23623             cls: 'x-html-editor-tip'
23624         },
23625         insertunorderedlist : {
23626             title: 'Bullet List',
23627             text: 'Start a bulleted list.',
23628             cls: 'x-html-editor-tip'
23629         },
23630         insertorderedlist : {
23631             title: 'Numbered List',
23632             text: 'Start a numbered list.',
23633             cls: 'x-html-editor-tip'
23634         },
23635         createlink : {
23636             title: 'Hyperlink',
23637             text: 'Make the selected text a hyperlink.',
23638             cls: 'x-html-editor-tip'
23639         },
23640         sourceedit : {
23641             title: 'Source Edit',
23642             text: 'Switch to source editing mode.',
23643             cls: 'x-html-editor-tip'
23644         }
23645     },
23646     // private
23647     onDestroy : function(){
23648         if(this.rendered){
23649             
23650             this.tb.items.each(function(item){
23651                 if(item.menu){
23652                     item.menu.removeAll();
23653                     if(item.menu.el){
23654                         item.menu.el.destroy();
23655                     }
23656                 }
23657                 item.destroy();
23658             });
23659              
23660         }
23661     },
23662     onFirstFocus: function() {
23663         this.tb.items.each(function(item){
23664            item.enable();
23665         });
23666     }
23667 });
23668
23669
23670
23671
23672 // <script type="text/javascript">
23673 /*
23674  * Based on
23675  * Ext JS Library 1.1.1
23676  * Copyright(c) 2006-2007, Ext JS, LLC.
23677  *  
23678  
23679  */
23680
23681  
23682 /**
23683  * @class Roo.form.HtmlEditor.ToolbarContext
23684  * Context Toolbar
23685  * 
23686  * Usage:
23687  *
23688  new Roo.form.HtmlEditor({
23689     ....
23690     toolbars : [
23691         { xtype: 'ToolbarStandard', styles : {} }
23692         { xtype: 'ToolbarContext', disable : {} }
23693     ]
23694 })
23695
23696      
23697  * 
23698  * @config : {Object} disable List of elements to disable.. (not done yet.)
23699  * @config : {Object} styles  Map of styles available.
23700  * 
23701  */
23702
23703 Roo.form.HtmlEditor.ToolbarContext = function(config)
23704 {
23705     
23706     Roo.apply(this, config);
23707     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23708     // dont call parent... till later.
23709     this.styles = this.styles || {};
23710 }
23711
23712  
23713
23714 Roo.form.HtmlEditor.ToolbarContext.types = {
23715     'IMG' : {
23716         width : {
23717             title: "Width",
23718             width: 40
23719         },
23720         height:  {
23721             title: "Height",
23722             width: 40
23723         },
23724         align: {
23725             title: "Align",
23726             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23727             width : 80
23728             
23729         },
23730         border: {
23731             title: "Border",
23732             width: 40
23733         },
23734         alt: {
23735             title: "Alt",
23736             width: 120
23737         },
23738         src : {
23739             title: "Src",
23740             width: 220
23741         }
23742         
23743     },
23744     'A' : {
23745         name : {
23746             title: "Name",
23747             width: 50
23748         },
23749         target:  {
23750             title: "Target",
23751             width: 120
23752         },
23753         href:  {
23754             title: "Href",
23755             width: 220
23756         } // border?
23757         
23758     },
23759     'TABLE' : {
23760         rows : {
23761             title: "Rows",
23762             width: 20
23763         },
23764         cols : {
23765             title: "Cols",
23766             width: 20
23767         },
23768         width : {
23769             title: "Width",
23770             width: 40
23771         },
23772         height : {
23773             title: "Height",
23774             width: 40
23775         },
23776         border : {
23777             title: "Border",
23778             width: 20
23779         }
23780     },
23781     'TD' : {
23782         width : {
23783             title: "Width",
23784             width: 40
23785         },
23786         height : {
23787             title: "Height",
23788             width: 40
23789         },   
23790         align: {
23791             title: "Align",
23792             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23793             width: 80
23794         },
23795         valign: {
23796             title: "Valign",
23797             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23798             width: 80
23799         },
23800         colspan: {
23801             title: "Colspan",
23802             width: 20
23803             
23804         },
23805          'font-family'  : {
23806             title : "Font",
23807             style : 'fontFamily',
23808             displayField: 'display',
23809             optname : 'font-family',
23810             width: 140
23811         }
23812     },
23813     'INPUT' : {
23814         name : {
23815             title: "name",
23816             width: 120
23817         },
23818         value : {
23819             title: "Value",
23820             width: 120
23821         },
23822         width : {
23823             title: "Width",
23824             width: 40
23825         }
23826     },
23827     'LABEL' : {
23828         'for' : {
23829             title: "For",
23830             width: 120
23831         }
23832     },
23833     'TEXTAREA' : {
23834           name : {
23835             title: "name",
23836             width: 120
23837         },
23838         rows : {
23839             title: "Rows",
23840             width: 20
23841         },
23842         cols : {
23843             title: "Cols",
23844             width: 20
23845         }
23846     },
23847     'SELECT' : {
23848         name : {
23849             title: "name",
23850             width: 120
23851         },
23852         selectoptions : {
23853             title: "Options",
23854             width: 200
23855         }
23856     },
23857     
23858     // should we really allow this??
23859     // should this just be 
23860     'BODY' : {
23861         title : {
23862             title: "Title",
23863             width: 200,
23864             disabled : true
23865         }
23866     },
23867     'SPAN' : {
23868         'font-family'  : {
23869             title : "Font",
23870             style : 'fontFamily',
23871             displayField: 'display',
23872             optname : 'font-family',
23873             width: 140
23874         }
23875     },
23876     'DIV' : {
23877         'font-family'  : {
23878             title : "Font",
23879             style : 'fontFamily',
23880             displayField: 'display',
23881             optname : 'font-family',
23882             width: 140
23883         }
23884     },
23885      'P' : {
23886         'font-family'  : {
23887             title : "Font",
23888             style : 'fontFamily',
23889             displayField: 'display',
23890             optname : 'font-family',
23891             width: 140
23892         }
23893     },
23894     
23895     '*' : {
23896         // empty..
23897     }
23898
23899 };
23900
23901 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23902 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23903
23904 Roo.form.HtmlEditor.ToolbarContext.options = {
23905         'font-family'  : [ 
23906                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23907                 [ 'Courier New', 'Courier New'],
23908                 [ 'Tahoma', 'Tahoma'],
23909                 [ 'Times New Roman,serif', 'Times'],
23910                 [ 'Verdana','Verdana' ]
23911         ]
23912 };
23913
23914 // fixme - these need to be configurable..
23915  
23916
23917 //Roo.form.HtmlEditor.ToolbarContext.types
23918
23919
23920 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23921     
23922     tb: false,
23923     
23924     rendered: false,
23925     
23926     editor : false,
23927     editorcore : false,
23928     /**
23929      * @cfg {Object} disable  List of toolbar elements to disable
23930          
23931      */
23932     disable : false,
23933     /**
23934      * @cfg {Object} styles List of styles 
23935      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23936      *
23937      * These must be defined in the page, so they get rendered correctly..
23938      * .headline { }
23939      * TD.underline { }
23940      * 
23941      */
23942     styles : false,
23943     
23944     options: false,
23945     
23946     toolbars : false,
23947     
23948     init : function(editor)
23949     {
23950         this.editor = editor;
23951         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23952         var editorcore = this.editorcore;
23953         
23954         var fid = editorcore.frameId;
23955         var etb = this;
23956         function btn(id, toggle, handler){
23957             var xid = fid + '-'+ id ;
23958             return {
23959                 id : xid,
23960                 cmd : id,
23961                 cls : 'x-btn-icon x-edit-'+id,
23962                 enableToggle:toggle !== false,
23963                 scope: editorcore, // was editor...
23964                 handler:handler||editorcore.relayBtnCmd,
23965                 clickEvent:'mousedown',
23966                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23967                 tabIndex:-1
23968             };
23969         }
23970         // create a new element.
23971         var wdiv = editor.wrap.createChild({
23972                 tag: 'div'
23973             }, editor.wrap.dom.firstChild.nextSibling, true);
23974         
23975         // can we do this more than once??
23976         
23977          // stop form submits
23978       
23979  
23980         // disable everything...
23981         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23982         this.toolbars = {};
23983            
23984         for (var i in  ty) {
23985           
23986             this.toolbars[i] = this.buildToolbar(ty[i],i);
23987         }
23988         this.tb = this.toolbars.BODY;
23989         this.tb.el.show();
23990         this.buildFooter();
23991         this.footer.show();
23992         editor.on('hide', function( ) { this.footer.hide() }, this);
23993         editor.on('show', function( ) { this.footer.show() }, this);
23994         
23995          
23996         this.rendered = true;
23997         
23998         // the all the btns;
23999         editor.on('editorevent', this.updateToolbar, this);
24000         // other toolbars need to implement this..
24001         //editor.on('editmodechange', this.updateToolbar, this);
24002     },
24003     
24004     
24005     
24006     /**
24007      * Protected method that will not generally be called directly. It triggers
24008      * a toolbar update by reading the markup state of the current selection in the editor.
24009      *
24010      * Note you can force an update by calling on('editorevent', scope, false)
24011      */
24012     updateToolbar: function(editor,ev,sel){
24013
24014         //Roo.log(ev);
24015         // capture mouse up - this is handy for selecting images..
24016         // perhaps should go somewhere else...
24017         if(!this.editorcore.activated){
24018              this.editor.onFirstFocus();
24019             return;
24020         }
24021         
24022         
24023         
24024         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24025         // selectNode - might want to handle IE?
24026         if (ev &&
24027             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24028             ev.target && ev.target.tagName == 'IMG') {
24029             // they have click on an image...
24030             // let's see if we can change the selection...
24031             sel = ev.target;
24032          
24033               var nodeRange = sel.ownerDocument.createRange();
24034             try {
24035                 nodeRange.selectNode(sel);
24036             } catch (e) {
24037                 nodeRange.selectNodeContents(sel);
24038             }
24039             //nodeRange.collapse(true);
24040             var s = this.editorcore.win.getSelection();
24041             s.removeAllRanges();
24042             s.addRange(nodeRange);
24043         }  
24044         
24045       
24046         var updateFooter = sel ? false : true;
24047         
24048         
24049         var ans = this.editorcore.getAllAncestors();
24050         
24051         // pick
24052         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24053         
24054         if (!sel) { 
24055             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24056             sel = sel ? sel : this.editorcore.doc.body;
24057             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24058             
24059         }
24060         // pick a menu that exists..
24061         var tn = sel.tagName.toUpperCase();
24062         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24063         
24064         tn = sel.tagName.toUpperCase();
24065         
24066         var lastSel = this.tb.selectedNode;
24067         
24068         this.tb.selectedNode = sel;
24069         
24070         // if current menu does not match..
24071         
24072         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24073                 
24074             this.tb.el.hide();
24075             ///console.log("show: " + tn);
24076             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24077             this.tb.el.show();
24078             // update name
24079             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24080             
24081             
24082             // update attributes
24083             if (this.tb.fields) {
24084                 this.tb.fields.each(function(e) {
24085                     if (e.stylename) {
24086                         e.setValue(sel.style[e.stylename]);
24087                         return;
24088                     } 
24089                    e.setValue(sel.getAttribute(e.attrname));
24090                 });
24091             }
24092             
24093             var hasStyles = false;
24094             for(var i in this.styles) {
24095                 hasStyles = true;
24096                 break;
24097             }
24098             
24099             // update styles
24100             if (hasStyles) { 
24101                 var st = this.tb.fields.item(0);
24102                 
24103                 st.store.removeAll();
24104                
24105                 
24106                 var cn = sel.className.split(/\s+/);
24107                 
24108                 var avs = [];
24109                 if (this.styles['*']) {
24110                     
24111                     Roo.each(this.styles['*'], function(v) {
24112                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24113                     });
24114                 }
24115                 if (this.styles[tn]) { 
24116                     Roo.each(this.styles[tn], function(v) {
24117                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24118                     });
24119                 }
24120                 
24121                 st.store.loadData(avs);
24122                 st.collapse();
24123                 st.setValue(cn);
24124             }
24125             // flag our selected Node.
24126             this.tb.selectedNode = sel;
24127            
24128            
24129             Roo.menu.MenuMgr.hideAll();
24130
24131         }
24132         
24133         if (!updateFooter) {
24134             //this.footDisp.dom.innerHTML = ''; 
24135             return;
24136         }
24137         // update the footer
24138         //
24139         var html = '';
24140         
24141         this.footerEls = ans.reverse();
24142         Roo.each(this.footerEls, function(a,i) {
24143             if (!a) { return; }
24144             html += html.length ? ' &gt; '  :  '';
24145             
24146             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24147             
24148         });
24149        
24150         // 
24151         var sz = this.footDisp.up('td').getSize();
24152         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24153         this.footDisp.dom.style.marginLeft = '5px';
24154         
24155         this.footDisp.dom.style.overflow = 'hidden';
24156         
24157         this.footDisp.dom.innerHTML = html;
24158             
24159         //this.editorsyncValue();
24160     },
24161      
24162     
24163    
24164        
24165     // private
24166     onDestroy : function(){
24167         if(this.rendered){
24168             
24169             this.tb.items.each(function(item){
24170                 if(item.menu){
24171                     item.menu.removeAll();
24172                     if(item.menu.el){
24173                         item.menu.el.destroy();
24174                     }
24175                 }
24176                 item.destroy();
24177             });
24178              
24179         }
24180     },
24181     onFirstFocus: function() {
24182         // need to do this for all the toolbars..
24183         this.tb.items.each(function(item){
24184            item.enable();
24185         });
24186     },
24187     buildToolbar: function(tlist, nm)
24188     {
24189         var editor = this.editor;
24190         var editorcore = this.editorcore;
24191          // create a new element.
24192         var wdiv = editor.wrap.createChild({
24193                 tag: 'div'
24194             }, editor.wrap.dom.firstChild.nextSibling, true);
24195         
24196        
24197         var tb = new Roo.Toolbar(wdiv);
24198         // add the name..
24199         
24200         tb.add(nm+ ":&nbsp;");
24201         
24202         var styles = [];
24203         for(var i in this.styles) {
24204             styles.push(i);
24205         }
24206         
24207         // styles...
24208         if (styles && styles.length) {
24209             
24210             // this needs a multi-select checkbox...
24211             tb.addField( new Roo.form.ComboBox({
24212                 store: new Roo.data.SimpleStore({
24213                     id : 'val',
24214                     fields: ['val', 'selected'],
24215                     data : [] 
24216                 }),
24217                 name : '-roo-edit-className',
24218                 attrname : 'className',
24219                 displayField: 'val',
24220                 typeAhead: false,
24221                 mode: 'local',
24222                 editable : false,
24223                 triggerAction: 'all',
24224                 emptyText:'Select Style',
24225                 selectOnFocus:true,
24226                 width: 130,
24227                 listeners : {
24228                     'select': function(c, r, i) {
24229                         // initial support only for on class per el..
24230                         tb.selectedNode.className =  r ? r.get('val') : '';
24231                         editorcore.syncValue();
24232                     }
24233                 }
24234     
24235             }));
24236         }
24237         
24238         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24239         var tbops = tbc.options;
24240         
24241         for (var i in tlist) {
24242             
24243             var item = tlist[i];
24244             tb.add(item.title + ":&nbsp;");
24245             
24246             
24247             //optname == used so you can configure the options available..
24248             var opts = item.opts ? item.opts : false;
24249             if (item.optname) {
24250                 opts = tbops[item.optname];
24251            
24252             }
24253             
24254             if (opts) {
24255                 // opts == pulldown..
24256                 tb.addField( new Roo.form.ComboBox({
24257                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24258                         id : 'val',
24259                         fields: ['val', 'display'],
24260                         data : opts  
24261                     }),
24262                     name : '-roo-edit-' + i,
24263                     attrname : i,
24264                     stylename : item.style ? item.style : false,
24265                     displayField: item.displayField ? item.displayField : 'val',
24266                     valueField :  'val',
24267                     typeAhead: false,
24268                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24269                     editable : false,
24270                     triggerAction: 'all',
24271                     emptyText:'Select',
24272                     selectOnFocus:true,
24273                     width: item.width ? item.width  : 130,
24274                     listeners : {
24275                         'select': function(c, r, i) {
24276                             if (c.stylename) {
24277                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24278                                 return;
24279                             }
24280                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24281                         }
24282                     }
24283
24284                 }));
24285                 continue;
24286                     
24287                  
24288                 
24289                 tb.addField( new Roo.form.TextField({
24290                     name: i,
24291                     width: 100,
24292                     //allowBlank:false,
24293                     value: ''
24294                 }));
24295                 continue;
24296             }
24297             tb.addField( new Roo.form.TextField({
24298                 name: '-roo-edit-' + i,
24299                 attrname : i,
24300                 
24301                 width: item.width,
24302                 //allowBlank:true,
24303                 value: '',
24304                 listeners: {
24305                     'change' : function(f, nv, ov) {
24306                         tb.selectedNode.setAttribute(f.attrname, nv);
24307                         editorcore.syncValue();
24308                     }
24309                 }
24310             }));
24311              
24312         }
24313         
24314         var _this = this;
24315         
24316         if(nm == 'BODY'){
24317             tb.addSeparator();
24318         
24319             tb.addButton( {
24320                 text: 'Stylesheets',
24321
24322                 listeners : {
24323                     click : function ()
24324                     {
24325                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24326                     }
24327                 }
24328             });
24329         }
24330         
24331         tb.addFill();
24332         tb.addButton( {
24333             text: 'Remove Tag',
24334     
24335             listeners : {
24336                 click : function ()
24337                 {
24338                     // remove
24339                     // undo does not work.
24340                      
24341                     var sn = tb.selectedNode;
24342                     
24343                     var pn = sn.parentNode;
24344                     
24345                     var stn =  sn.childNodes[0];
24346                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24347                     while (sn.childNodes.length) {
24348                         var node = sn.childNodes[0];
24349                         sn.removeChild(node);
24350                         //Roo.log(node);
24351                         pn.insertBefore(node, sn);
24352                         
24353                     }
24354                     pn.removeChild(sn);
24355                     var range = editorcore.createRange();
24356         
24357                     range.setStart(stn,0);
24358                     range.setEnd(en,0); //????
24359                     //range.selectNode(sel);
24360                     
24361                     
24362                     var selection = editorcore.getSelection();
24363                     selection.removeAllRanges();
24364                     selection.addRange(range);
24365                     
24366                     
24367                     
24368                     //_this.updateToolbar(null, null, pn);
24369                     _this.updateToolbar(null, null, null);
24370                     _this.footDisp.dom.innerHTML = ''; 
24371                 }
24372             }
24373             
24374                     
24375                 
24376             
24377         });
24378         
24379         
24380         tb.el.on('click', function(e){
24381             e.preventDefault(); // what does this do?
24382         });
24383         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24384         tb.el.hide();
24385         tb.name = nm;
24386         // dont need to disable them... as they will get hidden
24387         return tb;
24388          
24389         
24390     },
24391     buildFooter : function()
24392     {
24393         
24394         var fel = this.editor.wrap.createChild();
24395         this.footer = new Roo.Toolbar(fel);
24396         // toolbar has scrolly on left / right?
24397         var footDisp= new Roo.Toolbar.Fill();
24398         var _t = this;
24399         this.footer.add(
24400             {
24401                 text : '&lt;',
24402                 xtype: 'Button',
24403                 handler : function() {
24404                     _t.footDisp.scrollTo('left',0,true)
24405                 }
24406             }
24407         );
24408         this.footer.add( footDisp );
24409         this.footer.add( 
24410             {
24411                 text : '&gt;',
24412                 xtype: 'Button',
24413                 handler : function() {
24414                     // no animation..
24415                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24416                 }
24417             }
24418         );
24419         var fel = Roo.get(footDisp.el);
24420         fel.addClass('x-editor-context');
24421         this.footDispWrap = fel; 
24422         this.footDispWrap.overflow  = 'hidden';
24423         
24424         this.footDisp = fel.createChild();
24425         this.footDispWrap.on('click', this.onContextClick, this)
24426         
24427         
24428     },
24429     onContextClick : function (ev,dom)
24430     {
24431         ev.preventDefault();
24432         var  cn = dom.className;
24433         //Roo.log(cn);
24434         if (!cn.match(/x-ed-loc-/)) {
24435             return;
24436         }
24437         var n = cn.split('-').pop();
24438         var ans = this.footerEls;
24439         var sel = ans[n];
24440         
24441          // pick
24442         var range = this.editorcore.createRange();
24443         
24444         range.selectNodeContents(sel);
24445         //range.selectNode(sel);
24446         
24447         
24448         var selection = this.editorcore.getSelection();
24449         selection.removeAllRanges();
24450         selection.addRange(range);
24451         
24452         
24453         
24454         this.updateToolbar(null, null, sel);
24455         
24456         
24457     }
24458     
24459     
24460     
24461     
24462     
24463 });
24464
24465
24466
24467
24468
24469 /*
24470  * Based on:
24471  * Ext JS Library 1.1.1
24472  * Copyright(c) 2006-2007, Ext JS, LLC.
24473  *
24474  * Originally Released Under LGPL - original licence link has changed is not relivant.
24475  *
24476  * Fork - LGPL
24477  * <script type="text/javascript">
24478  */
24479  
24480 /**
24481  * @class Roo.form.BasicForm
24482  * @extends Roo.util.Observable
24483  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24484  * @constructor
24485  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24486  * @param {Object} config Configuration options
24487  */
24488 Roo.form.BasicForm = function(el, config){
24489     this.allItems = [];
24490     this.childForms = [];
24491     Roo.apply(this, config);
24492     /*
24493      * The Roo.form.Field items in this form.
24494      * @type MixedCollection
24495      */
24496      
24497      
24498     this.items = new Roo.util.MixedCollection(false, function(o){
24499         return o.id || (o.id = Roo.id());
24500     });
24501     this.addEvents({
24502         /**
24503          * @event beforeaction
24504          * Fires before any action is performed. Return false to cancel the action.
24505          * @param {Form} this
24506          * @param {Action} action The action to be performed
24507          */
24508         beforeaction: true,
24509         /**
24510          * @event actionfailed
24511          * Fires when an action fails.
24512          * @param {Form} this
24513          * @param {Action} action The action that failed
24514          */
24515         actionfailed : true,
24516         /**
24517          * @event actioncomplete
24518          * Fires when an action is completed.
24519          * @param {Form} this
24520          * @param {Action} action The action that completed
24521          */
24522         actioncomplete : true
24523     });
24524     if(el){
24525         this.initEl(el);
24526     }
24527     Roo.form.BasicForm.superclass.constructor.call(this);
24528     
24529     Roo.form.BasicForm.popover.apply();
24530 };
24531
24532 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24533     /**
24534      * @cfg {String} method
24535      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24536      */
24537     /**
24538      * @cfg {DataReader} reader
24539      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24540      * This is optional as there is built-in support for processing JSON.
24541      */
24542     /**
24543      * @cfg {DataReader} errorReader
24544      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24545      * This is completely optional as there is built-in support for processing JSON.
24546      */
24547     /**
24548      * @cfg {String} url
24549      * The URL to use for form actions if one isn't supplied in the action options.
24550      */
24551     /**
24552      * @cfg {Boolean} fileUpload
24553      * Set to true if this form is a file upload.
24554      */
24555      
24556     /**
24557      * @cfg {Object} baseParams
24558      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24559      */
24560      /**
24561      
24562     /**
24563      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24564      */
24565     timeout: 30,
24566
24567     // private
24568     activeAction : null,
24569
24570     /**
24571      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24572      * or setValues() data instead of when the form was first created.
24573      */
24574     trackResetOnLoad : false,
24575     
24576     
24577     /**
24578      * childForms - used for multi-tab forms
24579      * @type {Array}
24580      */
24581     childForms : false,
24582     
24583     /**
24584      * allItems - full list of fields.
24585      * @type {Array}
24586      */
24587     allItems : false,
24588     
24589     /**
24590      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24591      * element by passing it or its id or mask the form itself by passing in true.
24592      * @type Mixed
24593      */
24594     waitMsgTarget : false,
24595     
24596     /**
24597      * @type Boolean
24598      */
24599     disableMask : false,
24600     
24601     /**
24602      * @cfg {Boolean} errorMask (true|false) default false
24603      */
24604     errorMask : false,
24605     
24606     /**
24607      * @cfg {Number} maskOffset Default 100
24608      */
24609     maskOffset : 100,
24610
24611     // private
24612     initEl : function(el){
24613         this.el = Roo.get(el);
24614         this.id = this.el.id || Roo.id();
24615         this.el.on('submit', this.onSubmit, this);
24616         this.el.addClass('x-form');
24617     },
24618
24619     // private
24620     onSubmit : function(e){
24621         e.stopEvent();
24622     },
24623
24624     /**
24625      * Returns true if client-side validation on the form is successful.
24626      * @return Boolean
24627      */
24628     isValid : function(){
24629         var valid = true;
24630         var target = false;
24631         this.items.each(function(f){
24632             if(f.validate()){
24633                 return;
24634             }
24635             
24636             valid = false;
24637                 
24638             if(!target && f.el.isVisible(true)){
24639                 target = f;
24640             }
24641         });
24642         
24643         if(this.errorMask && !valid){
24644             Roo.form.BasicForm.popover.mask(this, target);
24645         }
24646         
24647         return valid;
24648     },
24649     /**
24650      * Returns array of invalid form fields.
24651      * @return Array
24652      */
24653     
24654     invalidFields : function()
24655     {
24656         var ret = [];
24657         this.items.each(function(f){
24658             if(f.validate()){
24659                 return;
24660             }
24661             ret.push(f);
24662             
24663         });
24664         
24665         return ret;
24666     },
24667     
24668     
24669     /**
24670      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24671      * @return Boolean
24672      */
24673     isDirty : function(){
24674         var dirty = false;
24675         this.items.each(function(f){
24676            if(f.isDirty()){
24677                dirty = true;
24678                return false;
24679            }
24680         });
24681         return dirty;
24682     },
24683     
24684     /**
24685      * Returns true if any fields in this form have changed since their original load. (New version)
24686      * @return Boolean
24687      */
24688     
24689     hasChanged : function()
24690     {
24691         var dirty = false;
24692         this.items.each(function(f){
24693            if(f.hasChanged()){
24694                dirty = true;
24695                return false;
24696            }
24697         });
24698         return dirty;
24699         
24700     },
24701     /**
24702      * Resets all hasChanged to 'false' -
24703      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24704      * So hasChanged storage is only to be used for this purpose
24705      * @return Boolean
24706      */
24707     resetHasChanged : function()
24708     {
24709         this.items.each(function(f){
24710            f.resetHasChanged();
24711         });
24712         
24713     },
24714     
24715     
24716     /**
24717      * Performs a predefined action (submit or load) or custom actions you define on this form.
24718      * @param {String} actionName The name of the action type
24719      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24720      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24721      * accept other config options):
24722      * <pre>
24723 Property          Type             Description
24724 ----------------  ---------------  ----------------------------------------------------------------------------------
24725 url               String           The url for the action (defaults to the form's url)
24726 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24727 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24728 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24729                                    validate the form on the client (defaults to false)
24730      * </pre>
24731      * @return {BasicForm} this
24732      */
24733     doAction : function(action, options){
24734         if(typeof action == 'string'){
24735             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24736         }
24737         if(this.fireEvent('beforeaction', this, action) !== false){
24738             this.beforeAction(action);
24739             action.run.defer(100, action);
24740         }
24741         return this;
24742     },
24743
24744     /**
24745      * Shortcut to do a submit action.
24746      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24747      * @return {BasicForm} this
24748      */
24749     submit : function(options){
24750         this.doAction('submit', options);
24751         return this;
24752     },
24753
24754     /**
24755      * Shortcut to do a load action.
24756      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24757      * @return {BasicForm} this
24758      */
24759     load : function(options){
24760         this.doAction('load', options);
24761         return this;
24762     },
24763
24764     /**
24765      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24766      * @param {Record} record The record to edit
24767      * @return {BasicForm} this
24768      */
24769     updateRecord : function(record){
24770         record.beginEdit();
24771         var fs = record.fields;
24772         fs.each(function(f){
24773             var field = this.findField(f.name);
24774             if(field){
24775                 record.set(f.name, field.getValue());
24776             }
24777         }, this);
24778         record.endEdit();
24779         return this;
24780     },
24781
24782     /**
24783      * Loads an Roo.data.Record into this form.
24784      * @param {Record} record The record to load
24785      * @return {BasicForm} this
24786      */
24787     loadRecord : function(record){
24788         this.setValues(record.data);
24789         return this;
24790     },
24791
24792     // private
24793     beforeAction : function(action){
24794         var o = action.options;
24795         
24796         if(!this.disableMask) {
24797             if(this.waitMsgTarget === true){
24798                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24799             }else if(this.waitMsgTarget){
24800                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24801                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24802             }else {
24803                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24804             }
24805         }
24806         
24807          
24808     },
24809
24810     // private
24811     afterAction : function(action, success){
24812         this.activeAction = null;
24813         var o = action.options;
24814         
24815         if(!this.disableMask) {
24816             if(this.waitMsgTarget === true){
24817                 this.el.unmask();
24818             }else if(this.waitMsgTarget){
24819                 this.waitMsgTarget.unmask();
24820             }else{
24821                 Roo.MessageBox.updateProgress(1);
24822                 Roo.MessageBox.hide();
24823             }
24824         }
24825         
24826         if(success){
24827             if(o.reset){
24828                 this.reset();
24829             }
24830             Roo.callback(o.success, o.scope, [this, action]);
24831             this.fireEvent('actioncomplete', this, action);
24832             
24833         }else{
24834             
24835             // failure condition..
24836             // we have a scenario where updates need confirming.
24837             // eg. if a locking scenario exists..
24838             // we look for { errors : { needs_confirm : true }} in the response.
24839             if (
24840                 (typeof(action.result) != 'undefined')  &&
24841                 (typeof(action.result.errors) != 'undefined')  &&
24842                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24843            ){
24844                 var _t = this;
24845                 Roo.MessageBox.confirm(
24846                     "Change requires confirmation",
24847                     action.result.errorMsg,
24848                     function(r) {
24849                         if (r != 'yes') {
24850                             return;
24851                         }
24852                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24853                     }
24854                     
24855                 );
24856                 
24857                 
24858                 
24859                 return;
24860             }
24861             
24862             Roo.callback(o.failure, o.scope, [this, action]);
24863             // show an error message if no failed handler is set..
24864             if (!this.hasListener('actionfailed')) {
24865                 Roo.MessageBox.alert("Error",
24866                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24867                         action.result.errorMsg :
24868                         "Saving Failed, please check your entries or try again"
24869                 );
24870             }
24871             
24872             this.fireEvent('actionfailed', this, action);
24873         }
24874         
24875     },
24876
24877     /**
24878      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24879      * @param {String} id The value to search for
24880      * @return Field
24881      */
24882     findField : function(id){
24883         var field = this.items.get(id);
24884         if(!field){
24885             this.items.each(function(f){
24886                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24887                     field = f;
24888                     return false;
24889                 }
24890             });
24891         }
24892         return field || null;
24893     },
24894
24895     /**
24896      * Add a secondary form to this one, 
24897      * Used to provide tabbed forms. One form is primary, with hidden values 
24898      * which mirror the elements from the other forms.
24899      * 
24900      * @param {Roo.form.Form} form to add.
24901      * 
24902      */
24903     addForm : function(form)
24904     {
24905        
24906         if (this.childForms.indexOf(form) > -1) {
24907             // already added..
24908             return;
24909         }
24910         this.childForms.push(form);
24911         var n = '';
24912         Roo.each(form.allItems, function (fe) {
24913             
24914             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24915             if (this.findField(n)) { // already added..
24916                 return;
24917             }
24918             var add = new Roo.form.Hidden({
24919                 name : n
24920             });
24921             add.render(this.el);
24922             
24923             this.add( add );
24924         }, this);
24925         
24926     },
24927     /**
24928      * Mark fields in this form invalid in bulk.
24929      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24930      * @return {BasicForm} this
24931      */
24932     markInvalid : function(errors){
24933         if(errors instanceof Array){
24934             for(var i = 0, len = errors.length; i < len; i++){
24935                 var fieldError = errors[i];
24936                 var f = this.findField(fieldError.id);
24937                 if(f){
24938                     f.markInvalid(fieldError.msg);
24939                 }
24940             }
24941         }else{
24942             var field, id;
24943             for(id in errors){
24944                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24945                     field.markInvalid(errors[id]);
24946                 }
24947             }
24948         }
24949         Roo.each(this.childForms || [], function (f) {
24950             f.markInvalid(errors);
24951         });
24952         
24953         return this;
24954     },
24955
24956     /**
24957      * Set values for fields in this form in bulk.
24958      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24959      * @return {BasicForm} this
24960      */
24961     setValues : function(values){
24962         if(values instanceof Array){ // array of objects
24963             for(var i = 0, len = values.length; i < len; i++){
24964                 var v = values[i];
24965                 var f = this.findField(v.id);
24966                 if(f){
24967                     f.setValue(v.value);
24968                     if(this.trackResetOnLoad){
24969                         f.originalValue = f.getValue();
24970                     }
24971                 }
24972             }
24973         }else{ // object hash
24974             var field, id;
24975             for(id in values){
24976                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24977                     
24978                     if (field.setFromData && 
24979                         field.valueField && 
24980                         field.displayField &&
24981                         // combos' with local stores can 
24982                         // be queried via setValue()
24983                         // to set their value..
24984                         (field.store && !field.store.isLocal)
24985                         ) {
24986                         // it's a combo
24987                         var sd = { };
24988                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24989                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24990                         field.setFromData(sd);
24991                         
24992                     } else {
24993                         field.setValue(values[id]);
24994                     }
24995                     
24996                     
24997                     if(this.trackResetOnLoad){
24998                         field.originalValue = field.getValue();
24999                     }
25000                 }
25001             }
25002         }
25003         this.resetHasChanged();
25004         
25005         
25006         Roo.each(this.childForms || [], function (f) {
25007             f.setValues(values);
25008             f.resetHasChanged();
25009         });
25010                 
25011         return this;
25012     },
25013  
25014     /**
25015      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25016      * they are returned as an array.
25017      * @param {Boolean} asString
25018      * @return {Object}
25019      */
25020     getValues : function(asString){
25021         if (this.childForms) {
25022             // copy values from the child forms
25023             Roo.each(this.childForms, function (f) {
25024                 this.setValues(f.getValues());
25025             }, this);
25026         }
25027         
25028         // use formdata
25029         if (typeof(FormData) != 'undefined' && asString !== true) {
25030             // this relies on a 'recent' version of chrome apparently...
25031             try {
25032                 var fd = (new FormData(this.el.dom)).entries();
25033                 var ret = {};
25034                 var ent = fd.next();
25035                 while (!ent.done) {
25036                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25037                     ent = fd.next();
25038                 };
25039                 return ret;
25040             } catch(e) {
25041                 
25042             }
25043             
25044         }
25045         
25046         
25047         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25048         if(asString === true){
25049             return fs;
25050         }
25051         return Roo.urlDecode(fs);
25052     },
25053     
25054     /**
25055      * Returns the fields in this form as an object with key/value pairs. 
25056      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25057      * @return {Object}
25058      */
25059     getFieldValues : function(with_hidden)
25060     {
25061         if (this.childForms) {
25062             // copy values from the child forms
25063             // should this call getFieldValues - probably not as we do not currently copy
25064             // hidden fields when we generate..
25065             Roo.each(this.childForms, function (f) {
25066                 this.setValues(f.getValues());
25067             }, this);
25068         }
25069         
25070         var ret = {};
25071         this.items.each(function(f){
25072             if (!f.getName()) {
25073                 return;
25074             }
25075             var v = f.getValue();
25076             if (f.inputType =='radio') {
25077                 if (typeof(ret[f.getName()]) == 'undefined') {
25078                     ret[f.getName()] = ''; // empty..
25079                 }
25080                 
25081                 if (!f.el.dom.checked) {
25082                     return;
25083                     
25084                 }
25085                 v = f.el.dom.value;
25086                 
25087             }
25088             
25089             // not sure if this supported any more..
25090             if ((typeof(v) == 'object') && f.getRawValue) {
25091                 v = f.getRawValue() ; // dates..
25092             }
25093             // combo boxes where name != hiddenName...
25094             if (f.name != f.getName()) {
25095                 ret[f.name] = f.getRawValue();
25096             }
25097             ret[f.getName()] = v;
25098         });
25099         
25100         return ret;
25101     },
25102
25103     /**
25104      * Clears all invalid messages in this form.
25105      * @return {BasicForm} this
25106      */
25107     clearInvalid : function(){
25108         this.items.each(function(f){
25109            f.clearInvalid();
25110         });
25111         
25112         Roo.each(this.childForms || [], function (f) {
25113             f.clearInvalid();
25114         });
25115         
25116         
25117         return this;
25118     },
25119
25120     /**
25121      * Resets this form.
25122      * @return {BasicForm} this
25123      */
25124     reset : function(){
25125         this.items.each(function(f){
25126             f.reset();
25127         });
25128         
25129         Roo.each(this.childForms || [], function (f) {
25130             f.reset();
25131         });
25132         this.resetHasChanged();
25133         
25134         return this;
25135     },
25136
25137     /**
25138      * Add Roo.form components to this form.
25139      * @param {Field} field1
25140      * @param {Field} field2 (optional)
25141      * @param {Field} etc (optional)
25142      * @return {BasicForm} this
25143      */
25144     add : function(){
25145         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25146         return this;
25147     },
25148
25149
25150     /**
25151      * Removes a field from the items collection (does NOT remove its markup).
25152      * @param {Field} field
25153      * @return {BasicForm} this
25154      */
25155     remove : function(field){
25156         this.items.remove(field);
25157         return this;
25158     },
25159
25160     /**
25161      * Looks at the fields in this form, checks them for an id attribute,
25162      * and calls applyTo on the existing dom element with that id.
25163      * @return {BasicForm} this
25164      */
25165     render : function(){
25166         this.items.each(function(f){
25167             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25168                 f.applyTo(f.id);
25169             }
25170         });
25171         return this;
25172     },
25173
25174     /**
25175      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25176      * @param {Object} values
25177      * @return {BasicForm} this
25178      */
25179     applyToFields : function(o){
25180         this.items.each(function(f){
25181            Roo.apply(f, o);
25182         });
25183         return this;
25184     },
25185
25186     /**
25187      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25188      * @param {Object} values
25189      * @return {BasicForm} this
25190      */
25191     applyIfToFields : function(o){
25192         this.items.each(function(f){
25193            Roo.applyIf(f, o);
25194         });
25195         return this;
25196     }
25197 });
25198
25199 // back compat
25200 Roo.BasicForm = Roo.form.BasicForm;
25201
25202 Roo.apply(Roo.form.BasicForm, {
25203     
25204     popover : {
25205         
25206         padding : 5,
25207         
25208         isApplied : false,
25209         
25210         isMasked : false,
25211         
25212         form : false,
25213         
25214         target : false,
25215         
25216         intervalID : false,
25217         
25218         maskEl : false,
25219         
25220         apply : function()
25221         {
25222             if(this.isApplied){
25223                 return;
25224             }
25225             
25226             this.maskEl = {
25227                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25228                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25229                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25230                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25231             };
25232             
25233             this.maskEl.top.enableDisplayMode("block");
25234             this.maskEl.left.enableDisplayMode("block");
25235             this.maskEl.bottom.enableDisplayMode("block");
25236             this.maskEl.right.enableDisplayMode("block");
25237             
25238             Roo.get(document.body).on('click', function(){
25239                 this.unmask();
25240             }, this);
25241             
25242             Roo.get(document.body).on('touchstart', function(){
25243                 this.unmask();
25244             }, this);
25245             
25246             this.isApplied = true
25247         },
25248         
25249         mask : function(form, target)
25250         {
25251             this.form = form;
25252             
25253             this.target = target;
25254             
25255             if(!this.form.errorMask || !target.el){
25256                 return;
25257             }
25258             
25259             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25260             
25261             var ot = this.target.el.calcOffsetsTo(scrollable);
25262             
25263             var scrollTo = ot[1] - this.form.maskOffset;
25264             
25265             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25266             
25267             scrollable.scrollTo('top', scrollTo);
25268             
25269             var el = this.target.wrap || this.target.el;
25270             
25271             var box = el.getBox();
25272             
25273             this.maskEl.top.setStyle('position', 'absolute');
25274             this.maskEl.top.setStyle('z-index', 10000);
25275             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25276             this.maskEl.top.setLeft(0);
25277             this.maskEl.top.setTop(0);
25278             this.maskEl.top.show();
25279             
25280             this.maskEl.left.setStyle('position', 'absolute');
25281             this.maskEl.left.setStyle('z-index', 10000);
25282             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25283             this.maskEl.left.setLeft(0);
25284             this.maskEl.left.setTop(box.y - this.padding);
25285             this.maskEl.left.show();
25286
25287             this.maskEl.bottom.setStyle('position', 'absolute');
25288             this.maskEl.bottom.setStyle('z-index', 10000);
25289             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25290             this.maskEl.bottom.setLeft(0);
25291             this.maskEl.bottom.setTop(box.bottom + this.padding);
25292             this.maskEl.bottom.show();
25293
25294             this.maskEl.right.setStyle('position', 'absolute');
25295             this.maskEl.right.setStyle('z-index', 10000);
25296             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25297             this.maskEl.right.setLeft(box.right + this.padding);
25298             this.maskEl.right.setTop(box.y - this.padding);
25299             this.maskEl.right.show();
25300
25301             this.intervalID = window.setInterval(function() {
25302                 Roo.form.BasicForm.popover.unmask();
25303             }, 10000);
25304
25305             window.onwheel = function(){ return false;};
25306             
25307             (function(){ this.isMasked = true; }).defer(500, this);
25308             
25309         },
25310         
25311         unmask : function()
25312         {
25313             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25314                 return;
25315             }
25316             
25317             this.maskEl.top.setStyle('position', 'absolute');
25318             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25319             this.maskEl.top.hide();
25320
25321             this.maskEl.left.setStyle('position', 'absolute');
25322             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25323             this.maskEl.left.hide();
25324
25325             this.maskEl.bottom.setStyle('position', 'absolute');
25326             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25327             this.maskEl.bottom.hide();
25328
25329             this.maskEl.right.setStyle('position', 'absolute');
25330             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25331             this.maskEl.right.hide();
25332             
25333             window.onwheel = function(){ return true;};
25334             
25335             if(this.intervalID){
25336                 window.clearInterval(this.intervalID);
25337                 this.intervalID = false;
25338             }
25339             
25340             this.isMasked = false;
25341             
25342         }
25343         
25344     }
25345     
25346 });/*
25347  * Based on:
25348  * Ext JS Library 1.1.1
25349  * Copyright(c) 2006-2007, Ext JS, LLC.
25350  *
25351  * Originally Released Under LGPL - original licence link has changed is not relivant.
25352  *
25353  * Fork - LGPL
25354  * <script type="text/javascript">
25355  */
25356
25357 /**
25358  * @class Roo.form.Form
25359  * @extends Roo.form.BasicForm
25360  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25361  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25362  * @constructor
25363  * @param {Object} config Configuration options
25364  */
25365 Roo.form.Form = function(config){
25366     var xitems =  [];
25367     if (config.items) {
25368         xitems = config.items;
25369         delete config.items;
25370     }
25371    
25372     
25373     Roo.form.Form.superclass.constructor.call(this, null, config);
25374     this.url = this.url || this.action;
25375     if(!this.root){
25376         this.root = new Roo.form.Layout(Roo.applyIf({
25377             id: Roo.id()
25378         }, config));
25379     }
25380     this.active = this.root;
25381     /**
25382      * Array of all the buttons that have been added to this form via {@link addButton}
25383      * @type Array
25384      */
25385     this.buttons = [];
25386     this.allItems = [];
25387     this.addEvents({
25388         /**
25389          * @event clientvalidation
25390          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25391          * @param {Form} this
25392          * @param {Boolean} valid true if the form has passed client-side validation
25393          */
25394         clientvalidation: true,
25395         /**
25396          * @event rendered
25397          * Fires when the form is rendered
25398          * @param {Roo.form.Form} form
25399          */
25400         rendered : true
25401     });
25402     
25403     if (this.progressUrl) {
25404             // push a hidden field onto the list of fields..
25405             this.addxtype( {
25406                     xns: Roo.form, 
25407                     xtype : 'Hidden', 
25408                     name : 'UPLOAD_IDENTIFIER' 
25409             });
25410         }
25411         
25412     
25413     Roo.each(xitems, this.addxtype, this);
25414     
25415 };
25416
25417 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25418      /**
25419      * @cfg {Roo.Button} buttons[] buttons at bottom of form
25420      */
25421     
25422     /**
25423      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25424      */
25425     /**
25426      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25427      */
25428     /**
25429      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25430      */
25431     buttonAlign:'center',
25432
25433     /**
25434      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25435      */
25436     minButtonWidth:75,
25437
25438     /**
25439      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25440      * This property cascades to child containers if not set.
25441      */
25442     labelAlign:'left',
25443
25444     /**
25445      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25446      * fires a looping event with that state. This is required to bind buttons to the valid
25447      * state using the config value formBind:true on the button.
25448      */
25449     monitorValid : false,
25450
25451     /**
25452      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25453      */
25454     monitorPoll : 200,
25455     
25456     /**
25457      * @cfg {String} progressUrl - Url to return progress data 
25458      */
25459     
25460     progressUrl : false,
25461     /**
25462      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25463      * sending a formdata with extra parameters - eg uploaded elements.
25464      */
25465     
25466     formData : false,
25467     
25468     /**
25469      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25470      * fields are added and the column is closed. If no fields are passed the column remains open
25471      * until end() is called.
25472      * @param {Object} config The config to pass to the column
25473      * @param {Field} field1 (optional)
25474      * @param {Field} field2 (optional)
25475      * @param {Field} etc (optional)
25476      * @return Column The column container object
25477      */
25478     column : function(c){
25479         var col = new Roo.form.Column(c);
25480         this.start(col);
25481         if(arguments.length > 1){ // duplicate code required because of Opera
25482             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25483             this.end();
25484         }
25485         return col;
25486     },
25487
25488     /**
25489      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25490      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25491      * until end() is called.
25492      * @param {Object} config The config to pass to the fieldset
25493      * @param {Field} field1 (optional)
25494      * @param {Field} field2 (optional)
25495      * @param {Field} etc (optional)
25496      * @return FieldSet The fieldset container object
25497      */
25498     fieldset : function(c){
25499         var fs = new Roo.form.FieldSet(c);
25500         this.start(fs);
25501         if(arguments.length > 1){ // duplicate code required because of Opera
25502             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25503             this.end();
25504         }
25505         return fs;
25506     },
25507
25508     /**
25509      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25510      * fields are added and the container is closed. If no fields are passed the container remains open
25511      * until end() is called.
25512      * @param {Object} config The config to pass to the Layout
25513      * @param {Field} field1 (optional)
25514      * @param {Field} field2 (optional)
25515      * @param {Field} etc (optional)
25516      * @return Layout The container object
25517      */
25518     container : function(c){
25519         var l = new Roo.form.Layout(c);
25520         this.start(l);
25521         if(arguments.length > 1){ // duplicate code required because of Opera
25522             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25523             this.end();
25524         }
25525         return l;
25526     },
25527
25528     /**
25529      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25530      * @param {Object} container A Roo.form.Layout or subclass of Layout
25531      * @return {Form} this
25532      */
25533     start : function(c){
25534         // cascade label info
25535         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25536         this.active.stack.push(c);
25537         c.ownerCt = this.active;
25538         this.active = c;
25539         return this;
25540     },
25541
25542     /**
25543      * Closes the current open container
25544      * @return {Form} this
25545      */
25546     end : function(){
25547         if(this.active == this.root){
25548             return this;
25549         }
25550         this.active = this.active.ownerCt;
25551         return this;
25552     },
25553
25554     /**
25555      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25556      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25557      * as the label of the field.
25558      * @param {Field} field1
25559      * @param {Field} field2 (optional)
25560      * @param {Field} etc. (optional)
25561      * @return {Form} this
25562      */
25563     add : function(){
25564         this.active.stack.push.apply(this.active.stack, arguments);
25565         this.allItems.push.apply(this.allItems,arguments);
25566         var r = [];
25567         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25568             if(a[i].isFormField){
25569                 r.push(a[i]);
25570             }
25571         }
25572         if(r.length > 0){
25573             Roo.form.Form.superclass.add.apply(this, r);
25574         }
25575         return this;
25576     },
25577     
25578
25579     
25580     
25581     
25582      /**
25583      * Find any element that has been added to a form, using it's ID or name
25584      * This can include framesets, columns etc. along with regular fields..
25585      * @param {String} id - id or name to find.
25586      
25587      * @return {Element} e - or false if nothing found.
25588      */
25589     findbyId : function(id)
25590     {
25591         var ret = false;
25592         if (!id) {
25593             return ret;
25594         }
25595         Roo.each(this.allItems, function(f){
25596             if (f.id == id || f.name == id ){
25597                 ret = f;
25598                 return false;
25599             }
25600         });
25601         return ret;
25602     },
25603
25604     
25605     
25606     /**
25607      * Render this form into the passed container. This should only be called once!
25608      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25609      * @return {Form} this
25610      */
25611     render : function(ct)
25612     {
25613         
25614         
25615         
25616         ct = Roo.get(ct);
25617         var o = this.autoCreate || {
25618             tag: 'form',
25619             method : this.method || 'POST',
25620             id : this.id || Roo.id()
25621         };
25622         this.initEl(ct.createChild(o));
25623
25624         this.root.render(this.el);
25625         
25626        
25627              
25628         this.items.each(function(f){
25629             f.render('x-form-el-'+f.id);
25630         });
25631
25632         if(this.buttons.length > 0){
25633             // tables are required to maintain order and for correct IE layout
25634             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25635                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25636                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25637             }}, null, true);
25638             var tr = tb.getElementsByTagName('tr')[0];
25639             for(var i = 0, len = this.buttons.length; i < len; i++) {
25640                 var b = this.buttons[i];
25641                 var td = document.createElement('td');
25642                 td.className = 'x-form-btn-td';
25643                 b.render(tr.appendChild(td));
25644             }
25645         }
25646         if(this.monitorValid){ // initialize after render
25647             this.startMonitoring();
25648         }
25649         this.fireEvent('rendered', this);
25650         return this;
25651     },
25652
25653     /**
25654      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25655      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25656      * object or a valid Roo.DomHelper element config
25657      * @param {Function} handler The function called when the button is clicked
25658      * @param {Object} scope (optional) The scope of the handler function
25659      * @return {Roo.Button}
25660      */
25661     addButton : function(config, handler, scope){
25662         var bc = {
25663             handler: handler,
25664             scope: scope,
25665             minWidth: this.minButtonWidth,
25666             hideParent:true
25667         };
25668         if(typeof config == "string"){
25669             bc.text = config;
25670         }else{
25671             Roo.apply(bc, config);
25672         }
25673         var btn = new Roo.Button(null, bc);
25674         this.buttons.push(btn);
25675         return btn;
25676     },
25677
25678      /**
25679      * Adds a series of form elements (using the xtype property as the factory method.
25680      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25681      * @param {Object} config 
25682      */
25683     
25684     addxtype : function()
25685     {
25686         var ar = Array.prototype.slice.call(arguments, 0);
25687         var ret = false;
25688         for(var i = 0; i < ar.length; i++) {
25689             if (!ar[i]) {
25690                 continue; // skip -- if this happends something invalid got sent, we 
25691                 // should ignore it, as basically that interface element will not show up
25692                 // and that should be pretty obvious!!
25693             }
25694             
25695             if (Roo.form[ar[i].xtype]) {
25696                 ar[i].form = this;
25697                 var fe = Roo.factory(ar[i], Roo.form);
25698                 if (!ret) {
25699                     ret = fe;
25700                 }
25701                 fe.form = this;
25702                 if (fe.store) {
25703                     fe.store.form = this;
25704                 }
25705                 if (fe.isLayout) {  
25706                          
25707                     this.start(fe);
25708                     this.allItems.push(fe);
25709                     if (fe.items && fe.addxtype) {
25710                         fe.addxtype.apply(fe, fe.items);
25711                         delete fe.items;
25712                     }
25713                      this.end();
25714                     continue;
25715                 }
25716                 
25717                 
25718                  
25719                 this.add(fe);
25720               //  console.log('adding ' + ar[i].xtype);
25721             }
25722             if (ar[i].xtype == 'Button') {  
25723                 //console.log('adding button');
25724                 //console.log(ar[i]);
25725                 this.addButton(ar[i]);
25726                 this.allItems.push(fe);
25727                 continue;
25728             }
25729             
25730             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25731                 alert('end is not supported on xtype any more, use items');
25732             //    this.end();
25733             //    //console.log('adding end');
25734             }
25735             
25736         }
25737         return ret;
25738     },
25739     
25740     /**
25741      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25742      * option "monitorValid"
25743      */
25744     startMonitoring : function(){
25745         if(!this.bound){
25746             this.bound = true;
25747             Roo.TaskMgr.start({
25748                 run : this.bindHandler,
25749                 interval : this.monitorPoll || 200,
25750                 scope: this
25751             });
25752         }
25753     },
25754
25755     /**
25756      * Stops monitoring of the valid state of this form
25757      */
25758     stopMonitoring : function(){
25759         this.bound = false;
25760     },
25761
25762     // private
25763     bindHandler : function(){
25764         if(!this.bound){
25765             return false; // stops binding
25766         }
25767         var valid = true;
25768         this.items.each(function(f){
25769             if(!f.isValid(true)){
25770                 valid = false;
25771                 return false;
25772             }
25773         });
25774         for(var i = 0, len = this.buttons.length; i < len; i++){
25775             var btn = this.buttons[i];
25776             if(btn.formBind === true && btn.disabled === valid){
25777                 btn.setDisabled(!valid);
25778             }
25779         }
25780         this.fireEvent('clientvalidation', this, valid);
25781     }
25782     
25783     
25784     
25785     
25786     
25787     
25788     
25789     
25790 });
25791
25792
25793 // back compat
25794 Roo.Form = Roo.form.Form;
25795 /*
25796  * Based on:
25797  * Ext JS Library 1.1.1
25798  * Copyright(c) 2006-2007, Ext JS, LLC.
25799  *
25800  * Originally Released Under LGPL - original licence link has changed is not relivant.
25801  *
25802  * Fork - LGPL
25803  * <script type="text/javascript">
25804  */
25805
25806 // as we use this in bootstrap.
25807 Roo.namespace('Roo.form');
25808  /**
25809  * @class Roo.form.Action
25810  * Internal Class used to handle form actions
25811  * @constructor
25812  * @param {Roo.form.BasicForm} el The form element or its id
25813  * @param {Object} config Configuration options
25814  */
25815
25816  
25817  
25818 // define the action interface
25819 Roo.form.Action = function(form, options){
25820     this.form = form;
25821     this.options = options || {};
25822 };
25823 /**
25824  * Client Validation Failed
25825  * @const 
25826  */
25827 Roo.form.Action.CLIENT_INVALID = 'client';
25828 /**
25829  * Server Validation Failed
25830  * @const 
25831  */
25832 Roo.form.Action.SERVER_INVALID = 'server';
25833  /**
25834  * Connect to Server Failed
25835  * @const 
25836  */
25837 Roo.form.Action.CONNECT_FAILURE = 'connect';
25838 /**
25839  * Reading Data from Server Failed
25840  * @const 
25841  */
25842 Roo.form.Action.LOAD_FAILURE = 'load';
25843
25844 Roo.form.Action.prototype = {
25845     type : 'default',
25846     failureType : undefined,
25847     response : undefined,
25848     result : undefined,
25849
25850     // interface method
25851     run : function(options){
25852
25853     },
25854
25855     // interface method
25856     success : function(response){
25857
25858     },
25859
25860     // interface method
25861     handleResponse : function(response){
25862
25863     },
25864
25865     // default connection failure
25866     failure : function(response){
25867         
25868         this.response = response;
25869         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25870         this.form.afterAction(this, false);
25871     },
25872
25873     processResponse : function(response){
25874         this.response = response;
25875         if(!response.responseText){
25876             return true;
25877         }
25878         this.result = this.handleResponse(response);
25879         return this.result;
25880     },
25881
25882     // utility functions used internally
25883     getUrl : function(appendParams){
25884         var url = this.options.url || this.form.url || this.form.el.dom.action;
25885         if(appendParams){
25886             var p = this.getParams();
25887             if(p){
25888                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25889             }
25890         }
25891         return url;
25892     },
25893
25894     getMethod : function(){
25895         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25896     },
25897
25898     getParams : function(){
25899         var bp = this.form.baseParams;
25900         var p = this.options.params;
25901         if(p){
25902             if(typeof p == "object"){
25903                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25904             }else if(typeof p == 'string' && bp){
25905                 p += '&' + Roo.urlEncode(bp);
25906             }
25907         }else if(bp){
25908             p = Roo.urlEncode(bp);
25909         }
25910         return p;
25911     },
25912
25913     createCallback : function(){
25914         return {
25915             success: this.success,
25916             failure: this.failure,
25917             scope: this,
25918             timeout: (this.form.timeout*1000),
25919             upload: this.form.fileUpload ? this.success : undefined
25920         };
25921     }
25922 };
25923
25924 Roo.form.Action.Submit = function(form, options){
25925     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25926 };
25927
25928 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25929     type : 'submit',
25930
25931     haveProgress : false,
25932     uploadComplete : false,
25933     
25934     // uploadProgress indicator.
25935     uploadProgress : function()
25936     {
25937         if (!this.form.progressUrl) {
25938             return;
25939         }
25940         
25941         if (!this.haveProgress) {
25942             Roo.MessageBox.progress("Uploading", "Uploading");
25943         }
25944         if (this.uploadComplete) {
25945            Roo.MessageBox.hide();
25946            return;
25947         }
25948         
25949         this.haveProgress = true;
25950    
25951         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25952         
25953         var c = new Roo.data.Connection();
25954         c.request({
25955             url : this.form.progressUrl,
25956             params: {
25957                 id : uid
25958             },
25959             method: 'GET',
25960             success : function(req){
25961                //console.log(data);
25962                 var rdata = false;
25963                 var edata;
25964                 try  {
25965                    rdata = Roo.decode(req.responseText)
25966                 } catch (e) {
25967                     Roo.log("Invalid data from server..");
25968                     Roo.log(edata);
25969                     return;
25970                 }
25971                 if (!rdata || !rdata.success) {
25972                     Roo.log(rdata);
25973                     Roo.MessageBox.alert(Roo.encode(rdata));
25974                     return;
25975                 }
25976                 var data = rdata.data;
25977                 
25978                 if (this.uploadComplete) {
25979                    Roo.MessageBox.hide();
25980                    return;
25981                 }
25982                    
25983                 if (data){
25984                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25985                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25986                     );
25987                 }
25988                 this.uploadProgress.defer(2000,this);
25989             },
25990        
25991             failure: function(data) {
25992                 Roo.log('progress url failed ');
25993                 Roo.log(data);
25994             },
25995             scope : this
25996         });
25997            
25998     },
25999     
26000     
26001     run : function()
26002     {
26003         // run get Values on the form, so it syncs any secondary forms.
26004         this.form.getValues();
26005         
26006         var o = this.options;
26007         var method = this.getMethod();
26008         var isPost = method == 'POST';
26009         if(o.clientValidation === false || this.form.isValid()){
26010             
26011             if (this.form.progressUrl) {
26012                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26013                     (new Date() * 1) + '' + Math.random());
26014                     
26015             } 
26016             
26017             
26018             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26019                 form:this.form.el.dom,
26020                 url:this.getUrl(!isPost),
26021                 method: method,
26022                 params:isPost ? this.getParams() : null,
26023                 isUpload: this.form.fileUpload,
26024                 formData : this.form.formData
26025             }));
26026             
26027             this.uploadProgress();
26028
26029         }else if (o.clientValidation !== false){ // client validation failed
26030             this.failureType = Roo.form.Action.CLIENT_INVALID;
26031             this.form.afterAction(this, false);
26032         }
26033     },
26034
26035     success : function(response)
26036     {
26037         this.uploadComplete= true;
26038         if (this.haveProgress) {
26039             Roo.MessageBox.hide();
26040         }
26041         
26042         
26043         var result = this.processResponse(response);
26044         if(result === true || result.success){
26045             this.form.afterAction(this, true);
26046             return;
26047         }
26048         if(result.errors){
26049             this.form.markInvalid(result.errors);
26050             this.failureType = Roo.form.Action.SERVER_INVALID;
26051         }
26052         this.form.afterAction(this, false);
26053     },
26054     failure : function(response)
26055     {
26056         this.uploadComplete= true;
26057         if (this.haveProgress) {
26058             Roo.MessageBox.hide();
26059         }
26060         
26061         this.response = response;
26062         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26063         this.form.afterAction(this, false);
26064     },
26065     
26066     handleResponse : function(response){
26067         if(this.form.errorReader){
26068             var rs = this.form.errorReader.read(response);
26069             var errors = [];
26070             if(rs.records){
26071                 for(var i = 0, len = rs.records.length; i < len; i++) {
26072                     var r = rs.records[i];
26073                     errors[i] = r.data;
26074                 }
26075             }
26076             if(errors.length < 1){
26077                 errors = null;
26078             }
26079             return {
26080                 success : rs.success,
26081                 errors : errors
26082             };
26083         }
26084         var ret = false;
26085         try {
26086             ret = Roo.decode(response.responseText);
26087         } catch (e) {
26088             ret = {
26089                 success: false,
26090                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26091                 errors : []
26092             };
26093         }
26094         return ret;
26095         
26096     }
26097 });
26098
26099
26100 Roo.form.Action.Load = function(form, options){
26101     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26102     this.reader = this.form.reader;
26103 };
26104
26105 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26106     type : 'load',
26107
26108     run : function(){
26109         
26110         Roo.Ajax.request(Roo.apply(
26111                 this.createCallback(), {
26112                     method:this.getMethod(),
26113                     url:this.getUrl(false),
26114                     params:this.getParams()
26115         }));
26116     },
26117
26118     success : function(response){
26119         
26120         var result = this.processResponse(response);
26121         if(result === true || !result.success || !result.data){
26122             this.failureType = Roo.form.Action.LOAD_FAILURE;
26123             this.form.afterAction(this, false);
26124             return;
26125         }
26126         this.form.clearInvalid();
26127         this.form.setValues(result.data);
26128         this.form.afterAction(this, true);
26129     },
26130
26131     handleResponse : function(response){
26132         if(this.form.reader){
26133             var rs = this.form.reader.read(response);
26134             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26135             return {
26136                 success : rs.success,
26137                 data : data
26138             };
26139         }
26140         return Roo.decode(response.responseText);
26141     }
26142 });
26143
26144 Roo.form.Action.ACTION_TYPES = {
26145     'load' : Roo.form.Action.Load,
26146     'submit' : Roo.form.Action.Submit
26147 };/*
26148  * Based on:
26149  * Ext JS Library 1.1.1
26150  * Copyright(c) 2006-2007, Ext JS, LLC.
26151  *
26152  * Originally Released Under LGPL - original licence link has changed is not relivant.
26153  *
26154  * Fork - LGPL
26155  * <script type="text/javascript">
26156  */
26157  
26158 /**
26159  * @class Roo.form.Layout
26160  * @extends Roo.Component
26161  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26162  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26163  * @constructor
26164  * @param {Object} config Configuration options
26165  */
26166 Roo.form.Layout = function(config){
26167     var xitems = [];
26168     if (config.items) {
26169         xitems = config.items;
26170         delete config.items;
26171     }
26172     Roo.form.Layout.superclass.constructor.call(this, config);
26173     this.stack = [];
26174     Roo.each(xitems, this.addxtype, this);
26175      
26176 };
26177
26178 Roo.extend(Roo.form.Layout, Roo.Component, {
26179     /**
26180      * @cfg {String/Object} autoCreate
26181      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26182      */
26183     /**
26184      * @cfg {String/Object/Function} style
26185      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26186      * a function which returns such a specification.
26187      */
26188     /**
26189      * @cfg {String} labelAlign
26190      * Valid values are "left," "top" and "right" (defaults to "left")
26191      */
26192     /**
26193      * @cfg {Number} labelWidth
26194      * Fixed width in pixels of all field labels (defaults to undefined)
26195      */
26196     /**
26197      * @cfg {Boolean} clear
26198      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26199      */
26200     clear : true,
26201     /**
26202      * @cfg {String} labelSeparator
26203      * The separator to use after field labels (defaults to ':')
26204      */
26205     labelSeparator : ':',
26206     /**
26207      * @cfg {Boolean} hideLabels
26208      * True to suppress the display of field labels in this layout (defaults to false)
26209      */
26210     hideLabels : false,
26211
26212     // private
26213     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26214     
26215     isLayout : true,
26216     
26217     // private
26218     onRender : function(ct, position){
26219         if(this.el){ // from markup
26220             this.el = Roo.get(this.el);
26221         }else {  // generate
26222             var cfg = this.getAutoCreate();
26223             this.el = ct.createChild(cfg, position);
26224         }
26225         if(this.style){
26226             this.el.applyStyles(this.style);
26227         }
26228         if(this.labelAlign){
26229             this.el.addClass('x-form-label-'+this.labelAlign);
26230         }
26231         if(this.hideLabels){
26232             this.labelStyle = "display:none";
26233             this.elementStyle = "padding-left:0;";
26234         }else{
26235             if(typeof this.labelWidth == 'number'){
26236                 this.labelStyle = "width:"+this.labelWidth+"px;";
26237                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26238             }
26239             if(this.labelAlign == 'top'){
26240                 this.labelStyle = "width:auto;";
26241                 this.elementStyle = "padding-left:0;";
26242             }
26243         }
26244         var stack = this.stack;
26245         var slen = stack.length;
26246         if(slen > 0){
26247             if(!this.fieldTpl){
26248                 var t = new Roo.Template(
26249                     '<div class="x-form-item {5}">',
26250                         '<label for="{0}" style="{2}">{1}{4}</label>',
26251                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26252                         '</div>',
26253                     '</div><div class="x-form-clear-left"></div>'
26254                 );
26255                 t.disableFormats = true;
26256                 t.compile();
26257                 Roo.form.Layout.prototype.fieldTpl = t;
26258             }
26259             for(var i = 0; i < slen; i++) {
26260                 if(stack[i].isFormField){
26261                     this.renderField(stack[i]);
26262                 }else{
26263                     this.renderComponent(stack[i]);
26264                 }
26265             }
26266         }
26267         if(this.clear){
26268             this.el.createChild({cls:'x-form-clear'});
26269         }
26270     },
26271
26272     // private
26273     renderField : function(f){
26274         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26275                f.id, //0
26276                f.fieldLabel, //1
26277                f.labelStyle||this.labelStyle||'', //2
26278                this.elementStyle||'', //3
26279                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26280                f.itemCls||this.itemCls||''  //5
26281        ], true).getPrevSibling());
26282     },
26283
26284     // private
26285     renderComponent : function(c){
26286         c.render(c.isLayout ? this.el : this.el.createChild());    
26287     },
26288     /**
26289      * Adds a object form elements (using the xtype property as the factory method.)
26290      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26291      * @param {Object} config 
26292      */
26293     addxtype : function(o)
26294     {
26295         // create the lement.
26296         o.form = this.form;
26297         var fe = Roo.factory(o, Roo.form);
26298         this.form.allItems.push(fe);
26299         this.stack.push(fe);
26300         
26301         if (fe.isFormField) {
26302             this.form.items.add(fe);
26303         }
26304          
26305         return fe;
26306     }
26307 });
26308
26309 /**
26310  * @class Roo.form.Column
26311  * @extends Roo.form.Layout
26312  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26313  * @constructor
26314  * @param {Object} config Configuration options
26315  */
26316 Roo.form.Column = function(config){
26317     Roo.form.Column.superclass.constructor.call(this, config);
26318 };
26319
26320 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26321     /**
26322      * @cfg {Number/String} width
26323      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26324      */
26325     /**
26326      * @cfg {String/Object} autoCreate
26327      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26328      */
26329
26330     // private
26331     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26332
26333     // private
26334     onRender : function(ct, position){
26335         Roo.form.Column.superclass.onRender.call(this, ct, position);
26336         if(this.width){
26337             this.el.setWidth(this.width);
26338         }
26339     }
26340 });
26341
26342
26343 /**
26344  * @class Roo.form.Row
26345  * @extends Roo.form.Layout
26346  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26347  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26348  * @constructor
26349  * @param {Object} config Configuration options
26350  */
26351
26352  
26353 Roo.form.Row = function(config){
26354     Roo.form.Row.superclass.constructor.call(this, config);
26355 };
26356  
26357 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26358       /**
26359      * @cfg {Number/String} width
26360      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26361      */
26362     /**
26363      * @cfg {Number/String} height
26364      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26365      */
26366     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26367     
26368     padWidth : 20,
26369     // private
26370     onRender : function(ct, position){
26371         //console.log('row render');
26372         if(!this.rowTpl){
26373             var t = new Roo.Template(
26374                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26375                     '<label for="{0}" style="{2}">{1}{4}</label>',
26376                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26377                     '</div>',
26378                 '</div>'
26379             );
26380             t.disableFormats = true;
26381             t.compile();
26382             Roo.form.Layout.prototype.rowTpl = t;
26383         }
26384         this.fieldTpl = this.rowTpl;
26385         
26386         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26387         var labelWidth = 100;
26388         
26389         if ((this.labelAlign != 'top')) {
26390             if (typeof this.labelWidth == 'number') {
26391                 labelWidth = this.labelWidth
26392             }
26393             this.padWidth =  20 + labelWidth;
26394             
26395         }
26396         
26397         Roo.form.Column.superclass.onRender.call(this, ct, position);
26398         if(this.width){
26399             this.el.setWidth(this.width);
26400         }
26401         if(this.height){
26402             this.el.setHeight(this.height);
26403         }
26404     },
26405     
26406     // private
26407     renderField : function(f){
26408         f.fieldEl = this.fieldTpl.append(this.el, [
26409                f.id, f.fieldLabel,
26410                f.labelStyle||this.labelStyle||'',
26411                this.elementStyle||'',
26412                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26413                f.itemCls||this.itemCls||'',
26414                f.width ? f.width + this.padWidth : 160 + this.padWidth
26415        ],true);
26416     }
26417 });
26418  
26419
26420 /**
26421  * @class Roo.form.FieldSet
26422  * @extends Roo.form.Layout
26423  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26424  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26425  * @constructor
26426  * @param {Object} config Configuration options
26427  */
26428 Roo.form.FieldSet = function(config){
26429     Roo.form.FieldSet.superclass.constructor.call(this, config);
26430 };
26431
26432 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26433     /**
26434      * @cfg {String} legend
26435      * The text to display as the legend for the FieldSet (defaults to '')
26436      */
26437     /**
26438      * @cfg {String/Object} autoCreate
26439      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26440      */
26441
26442     // private
26443     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26444
26445     // private
26446     onRender : function(ct, position){
26447         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26448         if(this.legend){
26449             this.setLegend(this.legend);
26450         }
26451     },
26452
26453     // private
26454     setLegend : function(text){
26455         if(this.rendered){
26456             this.el.child('legend').update(text);
26457         }
26458     }
26459 });/*
26460  * Based on:
26461  * Ext JS Library 1.1.1
26462  * Copyright(c) 2006-2007, Ext JS, LLC.
26463  *
26464  * Originally Released Under LGPL - original licence link has changed is not relivant.
26465  *
26466  * Fork - LGPL
26467  * <script type="text/javascript">
26468  */
26469 /**
26470  * @class Roo.form.VTypes
26471  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26472  * @singleton
26473  */
26474 Roo.form.VTypes = function(){
26475     // closure these in so they are only created once.
26476     var alpha = /^[a-zA-Z_]+$/;
26477     var alphanum = /^[a-zA-Z0-9_]+$/;
26478     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26479     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26480
26481     // All these messages and functions are configurable
26482     return {
26483         /**
26484          * The function used to validate email addresses
26485          * @param {String} value The email address
26486          */
26487         'email' : function(v){
26488             return email.test(v);
26489         },
26490         /**
26491          * The error text to display when the email validation function returns false
26492          * @type String
26493          */
26494         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26495         /**
26496          * The keystroke filter mask to be applied on email input
26497          * @type RegExp
26498          */
26499         'emailMask' : /[a-z0-9_\.\-@]/i,
26500
26501         /**
26502          * The function used to validate URLs
26503          * @param {String} value The URL
26504          */
26505         'url' : function(v){
26506             return url.test(v);
26507         },
26508         /**
26509          * The error text to display when the url validation function returns false
26510          * @type String
26511          */
26512         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26513         
26514         /**
26515          * The function used to validate alpha values
26516          * @param {String} value The value
26517          */
26518         'alpha' : function(v){
26519             return alpha.test(v);
26520         },
26521         /**
26522          * The error text to display when the alpha validation function returns false
26523          * @type String
26524          */
26525         'alphaText' : 'This field should only contain letters and _',
26526         /**
26527          * The keystroke filter mask to be applied on alpha input
26528          * @type RegExp
26529          */
26530         'alphaMask' : /[a-z_]/i,
26531
26532         /**
26533          * The function used to validate alphanumeric values
26534          * @param {String} value The value
26535          */
26536         'alphanum' : function(v){
26537             return alphanum.test(v);
26538         },
26539         /**
26540          * The error text to display when the alphanumeric validation function returns false
26541          * @type String
26542          */
26543         'alphanumText' : 'This field should only contain letters, numbers and _',
26544         /**
26545          * The keystroke filter mask to be applied on alphanumeric input
26546          * @type RegExp
26547          */
26548         'alphanumMask' : /[a-z0-9_]/i
26549     };
26550 }();//<script type="text/javascript">
26551
26552 /**
26553  * @class Roo.form.FCKeditor
26554  * @extends Roo.form.TextArea
26555  * Wrapper around the FCKEditor http://www.fckeditor.net
26556  * @constructor
26557  * Creates a new FCKeditor
26558  * @param {Object} config Configuration options
26559  */
26560 Roo.form.FCKeditor = function(config){
26561     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26562     this.addEvents({
26563          /**
26564          * @event editorinit
26565          * Fired when the editor is initialized - you can add extra handlers here..
26566          * @param {FCKeditor} this
26567          * @param {Object} the FCK object.
26568          */
26569         editorinit : true
26570     });
26571     
26572     
26573 };
26574 Roo.form.FCKeditor.editors = { };
26575 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26576 {
26577     //defaultAutoCreate : {
26578     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26579     //},
26580     // private
26581     /**
26582      * @cfg {Object} fck options - see fck manual for details.
26583      */
26584     fckconfig : false,
26585     
26586     /**
26587      * @cfg {Object} fck toolbar set (Basic or Default)
26588      */
26589     toolbarSet : 'Basic',
26590     /**
26591      * @cfg {Object} fck BasePath
26592      */ 
26593     basePath : '/fckeditor/',
26594     
26595     
26596     frame : false,
26597     
26598     value : '',
26599     
26600    
26601     onRender : function(ct, position)
26602     {
26603         if(!this.el){
26604             this.defaultAutoCreate = {
26605                 tag: "textarea",
26606                 style:"width:300px;height:60px;",
26607                 autocomplete: "new-password"
26608             };
26609         }
26610         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26611         /*
26612         if(this.grow){
26613             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26614             if(this.preventScrollbars){
26615                 this.el.setStyle("overflow", "hidden");
26616             }
26617             this.el.setHeight(this.growMin);
26618         }
26619         */
26620         //console.log('onrender' + this.getId() );
26621         Roo.form.FCKeditor.editors[this.getId()] = this;
26622          
26623
26624         this.replaceTextarea() ;
26625         
26626     },
26627     
26628     getEditor : function() {
26629         return this.fckEditor;
26630     },
26631     /**
26632      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26633      * @param {Mixed} value The value to set
26634      */
26635     
26636     
26637     setValue : function(value)
26638     {
26639         //console.log('setValue: ' + value);
26640         
26641         if(typeof(value) == 'undefined') { // not sure why this is happending...
26642             return;
26643         }
26644         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26645         
26646         //if(!this.el || !this.getEditor()) {
26647         //    this.value = value;
26648             //this.setValue.defer(100,this,[value]);    
26649         //    return;
26650         //} 
26651         
26652         if(!this.getEditor()) {
26653             return;
26654         }
26655         
26656         this.getEditor().SetData(value);
26657         
26658         //
26659
26660     },
26661
26662     /**
26663      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26664      * @return {Mixed} value The field value
26665      */
26666     getValue : function()
26667     {
26668         
26669         if (this.frame && this.frame.dom.style.display == 'none') {
26670             return Roo.form.FCKeditor.superclass.getValue.call(this);
26671         }
26672         
26673         if(!this.el || !this.getEditor()) {
26674            
26675            // this.getValue.defer(100,this); 
26676             return this.value;
26677         }
26678        
26679         
26680         var value=this.getEditor().GetData();
26681         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26682         return Roo.form.FCKeditor.superclass.getValue.call(this);
26683         
26684
26685     },
26686
26687     /**
26688      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26689      * @return {Mixed} value The field value
26690      */
26691     getRawValue : function()
26692     {
26693         if (this.frame && this.frame.dom.style.display == 'none') {
26694             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26695         }
26696         
26697         if(!this.el || !this.getEditor()) {
26698             //this.getRawValue.defer(100,this); 
26699             return this.value;
26700             return;
26701         }
26702         
26703         
26704         
26705         var value=this.getEditor().GetData();
26706         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26707         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26708          
26709     },
26710     
26711     setSize : function(w,h) {
26712         
26713         
26714         
26715         //if (this.frame && this.frame.dom.style.display == 'none') {
26716         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26717         //    return;
26718         //}
26719         //if(!this.el || !this.getEditor()) {
26720         //    this.setSize.defer(100,this, [w,h]); 
26721         //    return;
26722         //}
26723         
26724         
26725         
26726         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26727         
26728         this.frame.dom.setAttribute('width', w);
26729         this.frame.dom.setAttribute('height', h);
26730         this.frame.setSize(w,h);
26731         
26732     },
26733     
26734     toggleSourceEdit : function(value) {
26735         
26736       
26737          
26738         this.el.dom.style.display = value ? '' : 'none';
26739         this.frame.dom.style.display = value ?  'none' : '';
26740         
26741     },
26742     
26743     
26744     focus: function(tag)
26745     {
26746         if (this.frame.dom.style.display == 'none') {
26747             return Roo.form.FCKeditor.superclass.focus.call(this);
26748         }
26749         if(!this.el || !this.getEditor()) {
26750             this.focus.defer(100,this, [tag]); 
26751             return;
26752         }
26753         
26754         
26755         
26756         
26757         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26758         this.getEditor().Focus();
26759         if (tgs.length) {
26760             if (!this.getEditor().Selection.GetSelection()) {
26761                 this.focus.defer(100,this, [tag]); 
26762                 return;
26763             }
26764             
26765             
26766             var r = this.getEditor().EditorDocument.createRange();
26767             r.setStart(tgs[0],0);
26768             r.setEnd(tgs[0],0);
26769             this.getEditor().Selection.GetSelection().removeAllRanges();
26770             this.getEditor().Selection.GetSelection().addRange(r);
26771             this.getEditor().Focus();
26772         }
26773         
26774     },
26775     
26776     
26777     
26778     replaceTextarea : function()
26779     {
26780         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26781             return ;
26782         }
26783         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26784         //{
26785             // We must check the elements firstly using the Id and then the name.
26786         var oTextarea = document.getElementById( this.getId() );
26787         
26788         var colElementsByName = document.getElementsByName( this.getId() ) ;
26789          
26790         oTextarea.style.display = 'none' ;
26791
26792         if ( oTextarea.tabIndex ) {            
26793             this.TabIndex = oTextarea.tabIndex ;
26794         }
26795         
26796         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26797         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26798         this.frame = Roo.get(this.getId() + '___Frame')
26799     },
26800     
26801     _getConfigHtml : function()
26802     {
26803         var sConfig = '' ;
26804
26805         for ( var o in this.fckconfig ) {
26806             sConfig += sConfig.length > 0  ? '&amp;' : '';
26807             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26808         }
26809
26810         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26811     },
26812     
26813     
26814     _getIFrameHtml : function()
26815     {
26816         var sFile = 'fckeditor.html' ;
26817         /* no idea what this is about..
26818         try
26819         {
26820             if ( (/fcksource=true/i).test( window.top.location.search ) )
26821                 sFile = 'fckeditor.original.html' ;
26822         }
26823         catch (e) { 
26824         */
26825
26826         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26827         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26828         
26829         
26830         var html = '<iframe id="' + this.getId() +
26831             '___Frame" src="' + sLink +
26832             '" width="' + this.width +
26833             '" height="' + this.height + '"' +
26834             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26835             ' frameborder="0" scrolling="no"></iframe>' ;
26836
26837         return html ;
26838     },
26839     
26840     _insertHtmlBefore : function( html, element )
26841     {
26842         if ( element.insertAdjacentHTML )       {
26843             // IE
26844             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26845         } else { // Gecko
26846             var oRange = document.createRange() ;
26847             oRange.setStartBefore( element ) ;
26848             var oFragment = oRange.createContextualFragment( html );
26849             element.parentNode.insertBefore( oFragment, element ) ;
26850         }
26851     }
26852     
26853     
26854   
26855     
26856     
26857     
26858     
26859
26860 });
26861
26862 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26863
26864 function FCKeditor_OnComplete(editorInstance){
26865     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26866     f.fckEditor = editorInstance;
26867     //console.log("loaded");
26868     f.fireEvent('editorinit', f, editorInstance);
26869
26870   
26871
26872  
26873
26874
26875
26876
26877
26878
26879
26880
26881
26882
26883
26884
26885
26886
26887
26888 //<script type="text/javascript">
26889 /**
26890  * @class Roo.form.GridField
26891  * @extends Roo.form.Field
26892  * Embed a grid (or editable grid into a form)
26893  * STATUS ALPHA
26894  * 
26895  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26896  * it needs 
26897  * xgrid.store = Roo.data.Store
26898  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26899  * xgrid.store.reader = Roo.data.JsonReader 
26900  * 
26901  * 
26902  * @constructor
26903  * Creates a new GridField
26904  * @param {Object} config Configuration options
26905  */
26906 Roo.form.GridField = function(config){
26907     Roo.form.GridField.superclass.constructor.call(this, config);
26908      
26909 };
26910
26911 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26912     /**
26913      * @cfg {Number} width  - used to restrict width of grid..
26914      */
26915     width : 100,
26916     /**
26917      * @cfg {Number} height - used to restrict height of grid..
26918      */
26919     height : 50,
26920      /**
26921      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26922          * 
26923          *}
26924      */
26925     xgrid : false, 
26926     /**
26927      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26928      * {tag: "input", type: "checkbox", autocomplete: "off"})
26929      */
26930    // defaultAutoCreate : { tag: 'div' },
26931     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26932     /**
26933      * @cfg {String} addTitle Text to include for adding a title.
26934      */
26935     addTitle : false,
26936     //
26937     onResize : function(){
26938         Roo.form.Field.superclass.onResize.apply(this, arguments);
26939     },
26940
26941     initEvents : function(){
26942         // Roo.form.Checkbox.superclass.initEvents.call(this);
26943         // has no events...
26944        
26945     },
26946
26947
26948     getResizeEl : function(){
26949         return this.wrap;
26950     },
26951
26952     getPositionEl : function(){
26953         return this.wrap;
26954     },
26955
26956     // private
26957     onRender : function(ct, position){
26958         
26959         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26960         var style = this.style;
26961         delete this.style;
26962         
26963         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26964         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26965         this.viewEl = this.wrap.createChild({ tag: 'div' });
26966         if (style) {
26967             this.viewEl.applyStyles(style);
26968         }
26969         if (this.width) {
26970             this.viewEl.setWidth(this.width);
26971         }
26972         if (this.height) {
26973             this.viewEl.setHeight(this.height);
26974         }
26975         //if(this.inputValue !== undefined){
26976         //this.setValue(this.value);
26977         
26978         
26979         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26980         
26981         
26982         this.grid.render();
26983         this.grid.getDataSource().on('remove', this.refreshValue, this);
26984         this.grid.getDataSource().on('update', this.refreshValue, this);
26985         this.grid.on('afteredit', this.refreshValue, this);
26986  
26987     },
26988      
26989     
26990     /**
26991      * Sets the value of the item. 
26992      * @param {String} either an object  or a string..
26993      */
26994     setValue : function(v){
26995         //this.value = v;
26996         v = v || []; // empty set..
26997         // this does not seem smart - it really only affects memoryproxy grids..
26998         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26999             var ds = this.grid.getDataSource();
27000             // assumes a json reader..
27001             var data = {}
27002             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27003             ds.loadData( data);
27004         }
27005         // clear selection so it does not get stale.
27006         if (this.grid.sm) { 
27007             this.grid.sm.clearSelections();
27008         }
27009         
27010         Roo.form.GridField.superclass.setValue.call(this, v);
27011         this.refreshValue();
27012         // should load data in the grid really....
27013     },
27014     
27015     // private
27016     refreshValue: function() {
27017          var val = [];
27018         this.grid.getDataSource().each(function(r) {
27019             val.push(r.data);
27020         });
27021         this.el.dom.value = Roo.encode(val);
27022     }
27023     
27024      
27025     
27026     
27027 });/*
27028  * Based on:
27029  * Ext JS Library 1.1.1
27030  * Copyright(c) 2006-2007, Ext JS, LLC.
27031  *
27032  * Originally Released Under LGPL - original licence link has changed is not relivant.
27033  *
27034  * Fork - LGPL
27035  * <script type="text/javascript">
27036  */
27037 /**
27038  * @class Roo.form.DisplayField
27039  * @extends Roo.form.Field
27040  * A generic Field to display non-editable data.
27041  * @cfg {Boolean} closable (true|false) default false
27042  * @constructor
27043  * Creates a new Display Field item.
27044  * @param {Object} config Configuration options
27045  */
27046 Roo.form.DisplayField = function(config){
27047     Roo.form.DisplayField.superclass.constructor.call(this, config);
27048     
27049     this.addEvents({
27050         /**
27051          * @event close
27052          * Fires after the click the close btn
27053              * @param {Roo.form.DisplayField} this
27054              */
27055         close : true
27056     });
27057 };
27058
27059 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27060     inputType:      'hidden',
27061     allowBlank:     true,
27062     readOnly:         true,
27063     
27064  
27065     /**
27066      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27067      */
27068     focusClass : undefined,
27069     /**
27070      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27071      */
27072     fieldClass: 'x-form-field',
27073     
27074      /**
27075      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27076      */
27077     valueRenderer: undefined,
27078     
27079     width: 100,
27080     /**
27081      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27082      * {tag: "input", type: "checkbox", autocomplete: "off"})
27083      */
27084      
27085  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27086  
27087     closable : false,
27088     
27089     onResize : function(){
27090         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27091         
27092     },
27093
27094     initEvents : function(){
27095         // Roo.form.Checkbox.superclass.initEvents.call(this);
27096         // has no events...
27097         
27098         if(this.closable){
27099             this.closeEl.on('click', this.onClose, this);
27100         }
27101        
27102     },
27103
27104
27105     getResizeEl : function(){
27106         return this.wrap;
27107     },
27108
27109     getPositionEl : function(){
27110         return this.wrap;
27111     },
27112
27113     // private
27114     onRender : function(ct, position){
27115         
27116         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27117         //if(this.inputValue !== undefined){
27118         this.wrap = this.el.wrap();
27119         
27120         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27121         
27122         if(this.closable){
27123             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27124         }
27125         
27126         if (this.bodyStyle) {
27127             this.viewEl.applyStyles(this.bodyStyle);
27128         }
27129         //this.viewEl.setStyle('padding', '2px');
27130         
27131         this.setValue(this.value);
27132         
27133     },
27134 /*
27135     // private
27136     initValue : Roo.emptyFn,
27137
27138   */
27139
27140         // private
27141     onClick : function(){
27142         
27143     },
27144
27145     /**
27146      * Sets the checked state of the checkbox.
27147      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27148      */
27149     setValue : function(v){
27150         this.value = v;
27151         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27152         // this might be called before we have a dom element..
27153         if (!this.viewEl) {
27154             return;
27155         }
27156         this.viewEl.dom.innerHTML = html;
27157         Roo.form.DisplayField.superclass.setValue.call(this, v);
27158
27159     },
27160     
27161     onClose : function(e)
27162     {
27163         e.preventDefault();
27164         
27165         this.fireEvent('close', this);
27166     }
27167 });/*
27168  * 
27169  * Licence- LGPL
27170  * 
27171  */
27172
27173 /**
27174  * @class Roo.form.DayPicker
27175  * @extends Roo.form.Field
27176  * A Day picker show [M] [T] [W] ....
27177  * @constructor
27178  * Creates a new Day Picker
27179  * @param {Object} config Configuration options
27180  */
27181 Roo.form.DayPicker= function(config){
27182     Roo.form.DayPicker.superclass.constructor.call(this, config);
27183      
27184 };
27185
27186 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27187     /**
27188      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27189      */
27190     focusClass : undefined,
27191     /**
27192      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27193      */
27194     fieldClass: "x-form-field",
27195    
27196     /**
27197      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27198      * {tag: "input", type: "checkbox", autocomplete: "off"})
27199      */
27200     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27201     
27202    
27203     actionMode : 'viewEl', 
27204     //
27205     // private
27206  
27207     inputType : 'hidden',
27208     
27209      
27210     inputElement: false, // real input element?
27211     basedOn: false, // ????
27212     
27213     isFormField: true, // not sure where this is needed!!!!
27214
27215     onResize : function(){
27216         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27217         if(!this.boxLabel){
27218             this.el.alignTo(this.wrap, 'c-c');
27219         }
27220     },
27221
27222     initEvents : function(){
27223         Roo.form.Checkbox.superclass.initEvents.call(this);
27224         this.el.on("click", this.onClick,  this);
27225         this.el.on("change", this.onClick,  this);
27226     },
27227
27228
27229     getResizeEl : function(){
27230         return this.wrap;
27231     },
27232
27233     getPositionEl : function(){
27234         return this.wrap;
27235     },
27236
27237     
27238     // private
27239     onRender : function(ct, position){
27240         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27241        
27242         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27243         
27244         var r1 = '<table><tr>';
27245         var r2 = '<tr class="x-form-daypick-icons">';
27246         for (var i=0; i < 7; i++) {
27247             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27248             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27249         }
27250         
27251         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27252         viewEl.select('img').on('click', this.onClick, this);
27253         this.viewEl = viewEl;   
27254         
27255         
27256         // this will not work on Chrome!!!
27257         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27258         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27259         
27260         
27261           
27262
27263     },
27264
27265     // private
27266     initValue : Roo.emptyFn,
27267
27268     /**
27269      * Returns the checked state of the checkbox.
27270      * @return {Boolean} True if checked, else false
27271      */
27272     getValue : function(){
27273         return this.el.dom.value;
27274         
27275     },
27276
27277         // private
27278     onClick : function(e){ 
27279         //this.setChecked(!this.checked);
27280         Roo.get(e.target).toggleClass('x-menu-item-checked');
27281         this.refreshValue();
27282         //if(this.el.dom.checked != this.checked){
27283         //    this.setValue(this.el.dom.checked);
27284        // }
27285     },
27286     
27287     // private
27288     refreshValue : function()
27289     {
27290         var val = '';
27291         this.viewEl.select('img',true).each(function(e,i,n)  {
27292             val += e.is(".x-menu-item-checked") ? String(n) : '';
27293         });
27294         this.setValue(val, true);
27295     },
27296
27297     /**
27298      * Sets the checked state of the checkbox.
27299      * On is always based on a string comparison between inputValue and the param.
27300      * @param {Boolean/String} value - the value to set 
27301      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27302      */
27303     setValue : function(v,suppressEvent){
27304         if (!this.el.dom) {
27305             return;
27306         }
27307         var old = this.el.dom.value ;
27308         this.el.dom.value = v;
27309         if (suppressEvent) {
27310             return ;
27311         }
27312          
27313         // update display..
27314         this.viewEl.select('img',true).each(function(e,i,n)  {
27315             
27316             var on = e.is(".x-menu-item-checked");
27317             var newv = v.indexOf(String(n)) > -1;
27318             if (on != newv) {
27319                 e.toggleClass('x-menu-item-checked');
27320             }
27321             
27322         });
27323         
27324         
27325         this.fireEvent('change', this, v, old);
27326         
27327         
27328     },
27329    
27330     // handle setting of hidden value by some other method!!?!?
27331     setFromHidden: function()
27332     {
27333         if(!this.el){
27334             return;
27335         }
27336         //console.log("SET FROM HIDDEN");
27337         //alert('setFrom hidden');
27338         this.setValue(this.el.dom.value);
27339     },
27340     
27341     onDestroy : function()
27342     {
27343         if(this.viewEl){
27344             Roo.get(this.viewEl).remove();
27345         }
27346          
27347         Roo.form.DayPicker.superclass.onDestroy.call(this);
27348     }
27349
27350 });/*
27351  * RooJS Library 1.1.1
27352  * Copyright(c) 2008-2011  Alan Knowles
27353  *
27354  * License - LGPL
27355  */
27356  
27357
27358 /**
27359  * @class Roo.form.ComboCheck
27360  * @extends Roo.form.ComboBox
27361  * A combobox for multiple select items.
27362  *
27363  * FIXME - could do with a reset button..
27364  * 
27365  * @constructor
27366  * Create a new ComboCheck
27367  * @param {Object} config Configuration options
27368  */
27369 Roo.form.ComboCheck = function(config){
27370     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27371     // should verify some data...
27372     // like
27373     // hiddenName = required..
27374     // displayField = required
27375     // valudField == required
27376     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27377     var _t = this;
27378     Roo.each(req, function(e) {
27379         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27380             throw "Roo.form.ComboCheck : missing value for: " + e;
27381         }
27382     });
27383     
27384     
27385 };
27386
27387 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27388      
27389      
27390     editable : false,
27391      
27392     selectedClass: 'x-menu-item-checked', 
27393     
27394     // private
27395     onRender : function(ct, position){
27396         var _t = this;
27397         
27398         
27399         
27400         if(!this.tpl){
27401             var cls = 'x-combo-list';
27402
27403             
27404             this.tpl =  new Roo.Template({
27405                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27406                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27407                    '<span>{' + this.displayField + '}</span>' +
27408                     '</div>' 
27409                 
27410             });
27411         }
27412  
27413         
27414         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27415         this.view.singleSelect = false;
27416         this.view.multiSelect = true;
27417         this.view.toggleSelect = true;
27418         this.pageTb.add(new Roo.Toolbar.Fill(), {
27419             
27420             text: 'Done',
27421             handler: function()
27422             {
27423                 _t.collapse();
27424             }
27425         });
27426     },
27427     
27428     onViewOver : function(e, t){
27429         // do nothing...
27430         return;
27431         
27432     },
27433     
27434     onViewClick : function(doFocus,index){
27435         return;
27436         
27437     },
27438     select: function () {
27439         //Roo.log("SELECT CALLED");
27440     },
27441      
27442     selectByValue : function(xv, scrollIntoView){
27443         var ar = this.getValueArray();
27444         var sels = [];
27445         
27446         Roo.each(ar, function(v) {
27447             if(v === undefined || v === null){
27448                 return;
27449             }
27450             var r = this.findRecord(this.valueField, v);
27451             if(r){
27452                 sels.push(this.store.indexOf(r))
27453                 
27454             }
27455         },this);
27456         this.view.select(sels);
27457         return false;
27458     },
27459     
27460     
27461     
27462     onSelect : function(record, index){
27463        // Roo.log("onselect Called");
27464        // this is only called by the clear button now..
27465         this.view.clearSelections();
27466         this.setValue('[]');
27467         if (this.value != this.valueBefore) {
27468             this.fireEvent('change', this, this.value, this.valueBefore);
27469             this.valueBefore = this.value;
27470         }
27471     },
27472     getValueArray : function()
27473     {
27474         var ar = [] ;
27475         
27476         try {
27477             //Roo.log(this.value);
27478             if (typeof(this.value) == 'undefined') {
27479                 return [];
27480             }
27481             var ar = Roo.decode(this.value);
27482             return  ar instanceof Array ? ar : []; //?? valid?
27483             
27484         } catch(e) {
27485             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27486             return [];
27487         }
27488          
27489     },
27490     expand : function ()
27491     {
27492         
27493         Roo.form.ComboCheck.superclass.expand.call(this);
27494         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27495         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27496         
27497
27498     },
27499     
27500     collapse : function(){
27501         Roo.form.ComboCheck.superclass.collapse.call(this);
27502         var sl = this.view.getSelectedIndexes();
27503         var st = this.store;
27504         var nv = [];
27505         var tv = [];
27506         var r;
27507         Roo.each(sl, function(i) {
27508             r = st.getAt(i);
27509             nv.push(r.get(this.valueField));
27510         },this);
27511         this.setValue(Roo.encode(nv));
27512         if (this.value != this.valueBefore) {
27513
27514             this.fireEvent('change', this, this.value, this.valueBefore);
27515             this.valueBefore = this.value;
27516         }
27517         
27518     },
27519     
27520     setValue : function(v){
27521         // Roo.log(v);
27522         this.value = v;
27523         
27524         var vals = this.getValueArray();
27525         var tv = [];
27526         Roo.each(vals, function(k) {
27527             var r = this.findRecord(this.valueField, k);
27528             if(r){
27529                 tv.push(r.data[this.displayField]);
27530             }else if(this.valueNotFoundText !== undefined){
27531                 tv.push( this.valueNotFoundText );
27532             }
27533         },this);
27534        // Roo.log(tv);
27535         
27536         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27537         this.hiddenField.value = v;
27538         this.value = v;
27539     }
27540     
27541 });/*
27542  * Based on:
27543  * Ext JS Library 1.1.1
27544  * Copyright(c) 2006-2007, Ext JS, LLC.
27545  *
27546  * Originally Released Under LGPL - original licence link has changed is not relivant.
27547  *
27548  * Fork - LGPL
27549  * <script type="text/javascript">
27550  */
27551  
27552 /**
27553  * @class Roo.form.Signature
27554  * @extends Roo.form.Field
27555  * Signature field.  
27556  * @constructor
27557  * 
27558  * @param {Object} config Configuration options
27559  */
27560
27561 Roo.form.Signature = function(config){
27562     Roo.form.Signature.superclass.constructor.call(this, config);
27563     
27564     this.addEvents({// not in used??
27565          /**
27566          * @event confirm
27567          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27568              * @param {Roo.form.Signature} combo This combo box
27569              */
27570         'confirm' : true,
27571         /**
27572          * @event reset
27573          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27574              * @param {Roo.form.ComboBox} combo This combo box
27575              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27576              */
27577         'reset' : true
27578     });
27579 };
27580
27581 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27582     /**
27583      * @cfg {Object} labels Label to use when rendering a form.
27584      * defaults to 
27585      * labels : { 
27586      *      clear : "Clear",
27587      *      confirm : "Confirm"
27588      *  }
27589      */
27590     labels : { 
27591         clear : "Clear",
27592         confirm : "Confirm"
27593     },
27594     /**
27595      * @cfg {Number} width The signature panel width (defaults to 300)
27596      */
27597     width: 300,
27598     /**
27599      * @cfg {Number} height The signature panel height (defaults to 100)
27600      */
27601     height : 100,
27602     /**
27603      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27604      */
27605     allowBlank : false,
27606     
27607     //private
27608     // {Object} signPanel The signature SVG panel element (defaults to {})
27609     signPanel : {},
27610     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27611     isMouseDown : false,
27612     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27613     isConfirmed : false,
27614     // {String} signatureTmp SVG mapping string (defaults to empty string)
27615     signatureTmp : '',
27616     
27617     
27618     defaultAutoCreate : { // modified by initCompnoent..
27619         tag: "input",
27620         type:"hidden"
27621     },
27622
27623     // private
27624     onRender : function(ct, position){
27625         
27626         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27627         
27628         this.wrap = this.el.wrap({
27629             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27630         });
27631         
27632         this.createToolbar(this);
27633         this.signPanel = this.wrap.createChild({
27634                 tag: 'div',
27635                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27636             }, this.el
27637         );
27638             
27639         this.svgID = Roo.id();
27640         this.svgEl = this.signPanel.createChild({
27641               xmlns : 'http://www.w3.org/2000/svg',
27642               tag : 'svg',
27643               id : this.svgID + "-svg",
27644               width: this.width,
27645               height: this.height,
27646               viewBox: '0 0 '+this.width+' '+this.height,
27647               cn : [
27648                 {
27649                     tag: "rect",
27650                     id: this.svgID + "-svg-r",
27651                     width: this.width,
27652                     height: this.height,
27653                     fill: "#ffa"
27654                 },
27655                 {
27656                     tag: "line",
27657                     id: this.svgID + "-svg-l",
27658                     x1: "0", // start
27659                     y1: (this.height*0.8), // start set the line in 80% of height
27660                     x2: this.width, // end
27661                     y2: (this.height*0.8), // end set the line in 80% of height
27662                     'stroke': "#666",
27663                     'stroke-width': "1",
27664                     'stroke-dasharray': "3",
27665                     'shape-rendering': "crispEdges",
27666                     'pointer-events': "none"
27667                 },
27668                 {
27669                     tag: "path",
27670                     id: this.svgID + "-svg-p",
27671                     'stroke': "navy",
27672                     'stroke-width': "3",
27673                     'fill': "none",
27674                     'pointer-events': 'none'
27675                 }
27676               ]
27677         });
27678         this.createSVG();
27679         this.svgBox = this.svgEl.dom.getScreenCTM();
27680     },
27681     createSVG : function(){ 
27682         var svg = this.signPanel;
27683         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27684         var t = this;
27685
27686         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27687         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27688         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27689         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27690         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27691         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27692         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27693         
27694     },
27695     isTouchEvent : function(e){
27696         return e.type.match(/^touch/);
27697     },
27698     getCoords : function (e) {
27699         var pt    = this.svgEl.dom.createSVGPoint();
27700         pt.x = e.clientX; 
27701         pt.y = e.clientY;
27702         if (this.isTouchEvent(e)) {
27703             pt.x =  e.targetTouches[0].clientX;
27704             pt.y = e.targetTouches[0].clientY;
27705         }
27706         var a = this.svgEl.dom.getScreenCTM();
27707         var b = a.inverse();
27708         var mx = pt.matrixTransform(b);
27709         return mx.x + ',' + mx.y;
27710     },
27711     //mouse event headler 
27712     down : function (e) {
27713         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27714         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27715         
27716         this.isMouseDown = true;
27717         
27718         e.preventDefault();
27719     },
27720     move : function (e) {
27721         if (this.isMouseDown) {
27722             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27723             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27724         }
27725         
27726         e.preventDefault();
27727     },
27728     up : function (e) {
27729         this.isMouseDown = false;
27730         var sp = this.signatureTmp.split(' ');
27731         
27732         if(sp.length > 1){
27733             if(!sp[sp.length-2].match(/^L/)){
27734                 sp.pop();
27735                 sp.pop();
27736                 sp.push("");
27737                 this.signatureTmp = sp.join(" ");
27738             }
27739         }
27740         if(this.getValue() != this.signatureTmp){
27741             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27742             this.isConfirmed = false;
27743         }
27744         e.preventDefault();
27745     },
27746     
27747     /**
27748      * Protected method that will not generally be called directly. It
27749      * is called when the editor creates its toolbar. Override this method if you need to
27750      * add custom toolbar buttons.
27751      * @param {HtmlEditor} editor
27752      */
27753     createToolbar : function(editor){
27754          function btn(id, toggle, handler){
27755             var xid = fid + '-'+ id ;
27756             return {
27757                 id : xid,
27758                 cmd : id,
27759                 cls : 'x-btn-icon x-edit-'+id,
27760                 enableToggle:toggle !== false,
27761                 scope: editor, // was editor...
27762                 handler:handler||editor.relayBtnCmd,
27763                 clickEvent:'mousedown',
27764                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27765                 tabIndex:-1
27766             };
27767         }
27768         
27769         
27770         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27771         this.tb = tb;
27772         this.tb.add(
27773            {
27774                 cls : ' x-signature-btn x-signature-'+id,
27775                 scope: editor, // was editor...
27776                 handler: this.reset,
27777                 clickEvent:'mousedown',
27778                 text: this.labels.clear
27779             },
27780             {
27781                  xtype : 'Fill',
27782                  xns: Roo.Toolbar
27783             }, 
27784             {
27785                 cls : '  x-signature-btn x-signature-'+id,
27786                 scope: editor, // was editor...
27787                 handler: this.confirmHandler,
27788                 clickEvent:'mousedown',
27789                 text: this.labels.confirm
27790             }
27791         );
27792     
27793     },
27794     //public
27795     /**
27796      * when user is clicked confirm then show this image.....
27797      * 
27798      * @return {String} Image Data URI
27799      */
27800     getImageDataURI : function(){
27801         var svg = this.svgEl.dom.parentNode.innerHTML;
27802         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27803         return src; 
27804     },
27805     /**
27806      * 
27807      * @return {Boolean} this.isConfirmed
27808      */
27809     getConfirmed : function(){
27810         return this.isConfirmed;
27811     },
27812     /**
27813      * 
27814      * @return {Number} this.width
27815      */
27816     getWidth : function(){
27817         return this.width;
27818     },
27819     /**
27820      * 
27821      * @return {Number} this.height
27822      */
27823     getHeight : function(){
27824         return this.height;
27825     },
27826     // private
27827     getSignature : function(){
27828         return this.signatureTmp;
27829     },
27830     // private
27831     reset : function(){
27832         this.signatureTmp = '';
27833         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27834         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27835         this.isConfirmed = false;
27836         Roo.form.Signature.superclass.reset.call(this);
27837     },
27838     setSignature : function(s){
27839         this.signatureTmp = s;
27840         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27841         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27842         this.setValue(s);
27843         this.isConfirmed = false;
27844         Roo.form.Signature.superclass.reset.call(this);
27845     }, 
27846     test : function(){
27847 //        Roo.log(this.signPanel.dom.contentWindow.up())
27848     },
27849     //private
27850     setConfirmed : function(){
27851         
27852         
27853         
27854 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27855     },
27856     // private
27857     confirmHandler : function(){
27858         if(!this.getSignature()){
27859             return;
27860         }
27861         
27862         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27863         this.setValue(this.getSignature());
27864         this.isConfirmed = true;
27865         
27866         this.fireEvent('confirm', this);
27867     },
27868     // private
27869     // Subclasses should provide the validation implementation by overriding this
27870     validateValue : function(value){
27871         if(this.allowBlank){
27872             return true;
27873         }
27874         
27875         if(this.isConfirmed){
27876             return true;
27877         }
27878         return false;
27879     }
27880 });/*
27881  * Based on:
27882  * Ext JS Library 1.1.1
27883  * Copyright(c) 2006-2007, Ext JS, LLC.
27884  *
27885  * Originally Released Under LGPL - original licence link has changed is not relivant.
27886  *
27887  * Fork - LGPL
27888  * <script type="text/javascript">
27889  */
27890  
27891
27892 /**
27893  * @class Roo.form.ComboBox
27894  * @extends Roo.form.TriggerField
27895  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27896  * @constructor
27897  * Create a new ComboBox.
27898  * @param {Object} config Configuration options
27899  */
27900 Roo.form.Select = function(config){
27901     Roo.form.Select.superclass.constructor.call(this, config);
27902      
27903 };
27904
27905 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27906     /**
27907      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27908      */
27909     /**
27910      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27911      * rendering into an Roo.Editor, defaults to false)
27912      */
27913     /**
27914      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27915      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27916      */
27917     /**
27918      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27919      */
27920     /**
27921      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27922      * the dropdown list (defaults to undefined, with no header element)
27923      */
27924
27925      /**
27926      * @cfg {String/Roo.Template} tpl The template to use to render the output
27927      */
27928      
27929     // private
27930     defaultAutoCreate : {tag: "select"  },
27931     /**
27932      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27933      */
27934     listWidth: undefined,
27935     /**
27936      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27937      * mode = 'remote' or 'text' if mode = 'local')
27938      */
27939     displayField: undefined,
27940     /**
27941      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27942      * mode = 'remote' or 'value' if mode = 'local'). 
27943      * Note: use of a valueField requires the user make a selection
27944      * in order for a value to be mapped.
27945      */
27946     valueField: undefined,
27947     
27948     
27949     /**
27950      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27951      * field's data value (defaults to the underlying DOM element's name)
27952      */
27953     hiddenName: undefined,
27954     /**
27955      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27956      */
27957     listClass: '',
27958     /**
27959      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27960      */
27961     selectedClass: 'x-combo-selected',
27962     /**
27963      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27964      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27965      * which displays a downward arrow icon).
27966      */
27967     triggerClass : 'x-form-arrow-trigger',
27968     /**
27969      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27970      */
27971     shadow:'sides',
27972     /**
27973      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27974      * anchor positions (defaults to 'tl-bl')
27975      */
27976     listAlign: 'tl-bl?',
27977     /**
27978      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27979      */
27980     maxHeight: 300,
27981     /**
27982      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27983      * query specified by the allQuery config option (defaults to 'query')
27984      */
27985     triggerAction: 'query',
27986     /**
27987      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27988      * (defaults to 4, does not apply if editable = false)
27989      */
27990     minChars : 4,
27991     /**
27992      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27993      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27994      */
27995     typeAhead: false,
27996     /**
27997      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27998      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27999      */
28000     queryDelay: 500,
28001     /**
28002      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28003      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28004      */
28005     pageSize: 0,
28006     /**
28007      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28008      * when editable = true (defaults to false)
28009      */
28010     selectOnFocus:false,
28011     /**
28012      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28013      */
28014     queryParam: 'query',
28015     /**
28016      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28017      * when mode = 'remote' (defaults to 'Loading...')
28018      */
28019     loadingText: 'Loading...',
28020     /**
28021      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28022      */
28023     resizable: false,
28024     /**
28025      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28026      */
28027     handleHeight : 8,
28028     /**
28029      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28030      * traditional select (defaults to true)
28031      */
28032     editable: true,
28033     /**
28034      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28035      */
28036     allQuery: '',
28037     /**
28038      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28039      */
28040     mode: 'remote',
28041     /**
28042      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28043      * listWidth has a higher value)
28044      */
28045     minListWidth : 70,
28046     /**
28047      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28048      * allow the user to set arbitrary text into the field (defaults to false)
28049      */
28050     forceSelection:false,
28051     /**
28052      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28053      * if typeAhead = true (defaults to 250)
28054      */
28055     typeAheadDelay : 250,
28056     /**
28057      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28058      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28059      */
28060     valueNotFoundText : undefined,
28061     
28062     /**
28063      * @cfg {String} defaultValue The value displayed after loading the store.
28064      */
28065     defaultValue: '',
28066     
28067     /**
28068      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28069      */
28070     blockFocus : false,
28071     
28072     /**
28073      * @cfg {Boolean} disableClear Disable showing of clear button.
28074      */
28075     disableClear : false,
28076     /**
28077      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28078      */
28079     alwaysQuery : false,
28080     
28081     //private
28082     addicon : false,
28083     editicon: false,
28084     
28085     // element that contains real text value.. (when hidden is used..)
28086      
28087     // private
28088     onRender : function(ct, position){
28089         Roo.form.Field.prototype.onRender.call(this, ct, position);
28090         
28091         if(this.store){
28092             this.store.on('beforeload', this.onBeforeLoad, this);
28093             this.store.on('load', this.onLoad, this);
28094             this.store.on('loadexception', this.onLoadException, this);
28095             this.store.load({});
28096         }
28097         
28098         
28099         
28100     },
28101
28102     // private
28103     initEvents : function(){
28104         //Roo.form.ComboBox.superclass.initEvents.call(this);
28105  
28106     },
28107
28108     onDestroy : function(){
28109        
28110         if(this.store){
28111             this.store.un('beforeload', this.onBeforeLoad, this);
28112             this.store.un('load', this.onLoad, this);
28113             this.store.un('loadexception', this.onLoadException, this);
28114         }
28115         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28116     },
28117
28118     // private
28119     fireKey : function(e){
28120         if(e.isNavKeyPress() && !this.list.isVisible()){
28121             this.fireEvent("specialkey", this, e);
28122         }
28123     },
28124
28125     // private
28126     onResize: function(w, h){
28127         
28128         return; 
28129     
28130         
28131     },
28132
28133     /**
28134      * Allow or prevent the user from directly editing the field text.  If false is passed,
28135      * the user will only be able to select from the items defined in the dropdown list.  This method
28136      * is the runtime equivalent of setting the 'editable' config option at config time.
28137      * @param {Boolean} value True to allow the user to directly edit the field text
28138      */
28139     setEditable : function(value){
28140          
28141     },
28142
28143     // private
28144     onBeforeLoad : function(){
28145         
28146         Roo.log("Select before load");
28147         return;
28148     
28149         this.innerList.update(this.loadingText ?
28150                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28151         //this.restrictHeight();
28152         this.selectedIndex = -1;
28153     },
28154
28155     // private
28156     onLoad : function(){
28157
28158     
28159         var dom = this.el.dom;
28160         dom.innerHTML = '';
28161          var od = dom.ownerDocument;
28162          
28163         if (this.emptyText) {
28164             var op = od.createElement('option');
28165             op.setAttribute('value', '');
28166             op.innerHTML = String.format('{0}', this.emptyText);
28167             dom.appendChild(op);
28168         }
28169         if(this.store.getCount() > 0){
28170            
28171             var vf = this.valueField;
28172             var df = this.displayField;
28173             this.store.data.each(function(r) {
28174                 // which colmsn to use... testing - cdoe / title..
28175                 var op = od.createElement('option');
28176                 op.setAttribute('value', r.data[vf]);
28177                 op.innerHTML = String.format('{0}', r.data[df]);
28178                 dom.appendChild(op);
28179             });
28180             if (typeof(this.defaultValue != 'undefined')) {
28181                 this.setValue(this.defaultValue);
28182             }
28183             
28184              
28185         }else{
28186             //this.onEmptyResults();
28187         }
28188         //this.el.focus();
28189     },
28190     // private
28191     onLoadException : function()
28192     {
28193         dom.innerHTML = '';
28194             
28195         Roo.log("Select on load exception");
28196         return;
28197     
28198         this.collapse();
28199         Roo.log(this.store.reader.jsonData);
28200         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28201             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28202         }
28203         
28204         
28205     },
28206     // private
28207     onTypeAhead : function(){
28208          
28209     },
28210
28211     // private
28212     onSelect : function(record, index){
28213         Roo.log('on select?');
28214         return;
28215         if(this.fireEvent('beforeselect', this, record, index) !== false){
28216             this.setFromData(index > -1 ? record.data : false);
28217             this.collapse();
28218             this.fireEvent('select', this, record, index);
28219         }
28220     },
28221
28222     /**
28223      * Returns the currently selected field value or empty string if no value is set.
28224      * @return {String} value The selected value
28225      */
28226     getValue : function(){
28227         var dom = this.el.dom;
28228         this.value = dom.options[dom.selectedIndex].value;
28229         return this.value;
28230         
28231     },
28232
28233     /**
28234      * Clears any text/value currently set in the field
28235      */
28236     clearValue : function(){
28237         this.value = '';
28238         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28239         
28240     },
28241
28242     /**
28243      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28244      * will be displayed in the field.  If the value does not match the data value of an existing item,
28245      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28246      * Otherwise the field will be blank (although the value will still be set).
28247      * @param {String} value The value to match
28248      */
28249     setValue : function(v){
28250         var d = this.el.dom;
28251         for (var i =0; i < d.options.length;i++) {
28252             if (v == d.options[i].value) {
28253                 d.selectedIndex = i;
28254                 this.value = v;
28255                 return;
28256             }
28257         }
28258         this.clearValue();
28259     },
28260     /**
28261      * @property {Object} the last set data for the element
28262      */
28263     
28264     lastData : false,
28265     /**
28266      * Sets the value of the field based on a object which is related to the record format for the store.
28267      * @param {Object} value the value to set as. or false on reset?
28268      */
28269     setFromData : function(o){
28270         Roo.log('setfrom data?');
28271          
28272         
28273         
28274     },
28275     // private
28276     reset : function(){
28277         this.clearValue();
28278     },
28279     // private
28280     findRecord : function(prop, value){
28281         
28282         return false;
28283     
28284         var record;
28285         if(this.store.getCount() > 0){
28286             this.store.each(function(r){
28287                 if(r.data[prop] == value){
28288                     record = r;
28289                     return false;
28290                 }
28291                 return true;
28292             });
28293         }
28294         return record;
28295     },
28296     
28297     getName: function()
28298     {
28299         // returns hidden if it's set..
28300         if (!this.rendered) {return ''};
28301         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28302         
28303     },
28304      
28305
28306     
28307
28308     // private
28309     onEmptyResults : function(){
28310         Roo.log('empty results');
28311         //this.collapse();
28312     },
28313
28314     /**
28315      * Returns true if the dropdown list is expanded, else false.
28316      */
28317     isExpanded : function(){
28318         return false;
28319     },
28320
28321     /**
28322      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28323      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28324      * @param {String} value The data value of the item to select
28325      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28326      * selected item if it is not currently in view (defaults to true)
28327      * @return {Boolean} True if the value matched an item in the list, else false
28328      */
28329     selectByValue : function(v, scrollIntoView){
28330         Roo.log('select By Value');
28331         return false;
28332     
28333         if(v !== undefined && v !== null){
28334             var r = this.findRecord(this.valueField || this.displayField, v);
28335             if(r){
28336                 this.select(this.store.indexOf(r), scrollIntoView);
28337                 return true;
28338             }
28339         }
28340         return false;
28341     },
28342
28343     /**
28344      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28345      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28346      * @param {Number} index The zero-based index of the list item to select
28347      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28348      * selected item if it is not currently in view (defaults to true)
28349      */
28350     select : function(index, scrollIntoView){
28351         Roo.log('select ');
28352         return  ;
28353         
28354         this.selectedIndex = index;
28355         this.view.select(index);
28356         if(scrollIntoView !== false){
28357             var el = this.view.getNode(index);
28358             if(el){
28359                 this.innerList.scrollChildIntoView(el, false);
28360             }
28361         }
28362     },
28363
28364       
28365
28366     // private
28367     validateBlur : function(){
28368         
28369         return;
28370         
28371     },
28372
28373     // private
28374     initQuery : function(){
28375         this.doQuery(this.getRawValue());
28376     },
28377
28378     // private
28379     doForce : function(){
28380         if(this.el.dom.value.length > 0){
28381             this.el.dom.value =
28382                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28383              
28384         }
28385     },
28386
28387     /**
28388      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28389      * query allowing the query action to be canceled if needed.
28390      * @param {String} query The SQL query to execute
28391      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28392      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28393      * saved in the current store (defaults to false)
28394      */
28395     doQuery : function(q, forceAll){
28396         
28397         Roo.log('doQuery?');
28398         if(q === undefined || q === null){
28399             q = '';
28400         }
28401         var qe = {
28402             query: q,
28403             forceAll: forceAll,
28404             combo: this,
28405             cancel:false
28406         };
28407         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28408             return false;
28409         }
28410         q = qe.query;
28411         forceAll = qe.forceAll;
28412         if(forceAll === true || (q.length >= this.minChars)){
28413             if(this.lastQuery != q || this.alwaysQuery){
28414                 this.lastQuery = q;
28415                 if(this.mode == 'local'){
28416                     this.selectedIndex = -1;
28417                     if(forceAll){
28418                         this.store.clearFilter();
28419                     }else{
28420                         this.store.filter(this.displayField, q);
28421                     }
28422                     this.onLoad();
28423                 }else{
28424                     this.store.baseParams[this.queryParam] = q;
28425                     this.store.load({
28426                         params: this.getParams(q)
28427                     });
28428                     this.expand();
28429                 }
28430             }else{
28431                 this.selectedIndex = -1;
28432                 this.onLoad();   
28433             }
28434         }
28435     },
28436
28437     // private
28438     getParams : function(q){
28439         var p = {};
28440         //p[this.queryParam] = q;
28441         if(this.pageSize){
28442             p.start = 0;
28443             p.limit = this.pageSize;
28444         }
28445         return p;
28446     },
28447
28448     /**
28449      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28450      */
28451     collapse : function(){
28452         
28453     },
28454
28455     // private
28456     collapseIf : function(e){
28457         
28458     },
28459
28460     /**
28461      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28462      */
28463     expand : function(){
28464         
28465     } ,
28466
28467     // private
28468      
28469
28470     /** 
28471     * @cfg {Boolean} grow 
28472     * @hide 
28473     */
28474     /** 
28475     * @cfg {Number} growMin 
28476     * @hide 
28477     */
28478     /** 
28479     * @cfg {Number} growMax 
28480     * @hide 
28481     */
28482     /**
28483      * @hide
28484      * @method autoSize
28485      */
28486     
28487     setWidth : function()
28488     {
28489         
28490     },
28491     getResizeEl : function(){
28492         return this.el;
28493     }
28494 });//<script type="text/javasscript">
28495  
28496
28497 /**
28498  * @class Roo.DDView
28499  * A DnD enabled version of Roo.View.
28500  * @param {Element/String} container The Element in which to create the View.
28501  * @param {String} tpl The template string used to create the markup for each element of the View
28502  * @param {Object} config The configuration properties. These include all the config options of
28503  * {@link Roo.View} plus some specific to this class.<br>
28504  * <p>
28505  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28506  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28507  * <p>
28508  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28509 .x-view-drag-insert-above {
28510         border-top:1px dotted #3366cc;
28511 }
28512 .x-view-drag-insert-below {
28513         border-bottom:1px dotted #3366cc;
28514 }
28515 </code></pre>
28516  * 
28517  */
28518  
28519 Roo.DDView = function(container, tpl, config) {
28520     Roo.DDView.superclass.constructor.apply(this, arguments);
28521     this.getEl().setStyle("outline", "0px none");
28522     this.getEl().unselectable();
28523     if (this.dragGroup) {
28524         this.setDraggable(this.dragGroup.split(","));
28525     }
28526     if (this.dropGroup) {
28527         this.setDroppable(this.dropGroup.split(","));
28528     }
28529     if (this.deletable) {
28530         this.setDeletable();
28531     }
28532     this.isDirtyFlag = false;
28533         this.addEvents({
28534                 "drop" : true
28535         });
28536 };
28537
28538 Roo.extend(Roo.DDView, Roo.View, {
28539 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28540 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28541 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28542 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28543
28544         isFormField: true,
28545
28546         reset: Roo.emptyFn,
28547         
28548         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28549
28550         validate: function() {
28551                 return true;
28552         },
28553         
28554         destroy: function() {
28555                 this.purgeListeners();
28556                 this.getEl.removeAllListeners();
28557                 this.getEl().remove();
28558                 if (this.dragZone) {
28559                         if (this.dragZone.destroy) {
28560                                 this.dragZone.destroy();
28561                         }
28562                 }
28563                 if (this.dropZone) {
28564                         if (this.dropZone.destroy) {
28565                                 this.dropZone.destroy();
28566                         }
28567                 }
28568         },
28569
28570 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28571         getName: function() {
28572                 return this.name;
28573         },
28574
28575 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28576         setValue: function(v) {
28577                 if (!this.store) {
28578                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28579                 }
28580                 var data = {};
28581                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28582                 this.store.proxy = new Roo.data.MemoryProxy(data);
28583                 this.store.load();
28584         },
28585
28586 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28587         getValue: function() {
28588                 var result = '(';
28589                 this.store.each(function(rec) {
28590                         result += rec.id + ',';
28591                 });
28592                 return result.substr(0, result.length - 1) + ')';
28593         },
28594         
28595         getIds: function() {
28596                 var i = 0, result = new Array(this.store.getCount());
28597                 this.store.each(function(rec) {
28598                         result[i++] = rec.id;
28599                 });
28600                 return result;
28601         },
28602         
28603         isDirty: function() {
28604                 return this.isDirtyFlag;
28605         },
28606
28607 /**
28608  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28609  *      whole Element becomes the target, and this causes the drop gesture to append.
28610  */
28611     getTargetFromEvent : function(e) {
28612                 var target = e.getTarget();
28613                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28614                 target = target.parentNode;
28615                 }
28616                 if (!target) {
28617                         target = this.el.dom.lastChild || this.el.dom;
28618                 }
28619                 return target;
28620     },
28621
28622 /**
28623  *      Create the drag data which consists of an object which has the property "ddel" as
28624  *      the drag proxy element. 
28625  */
28626     getDragData : function(e) {
28627         var target = this.findItemFromChild(e.getTarget());
28628                 if(target) {
28629                         this.handleSelection(e);
28630                         var selNodes = this.getSelectedNodes();
28631             var dragData = {
28632                 source: this,
28633                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28634                 nodes: selNodes,
28635                 records: []
28636                         };
28637                         var selectedIndices = this.getSelectedIndexes();
28638                         for (var i = 0; i < selectedIndices.length; i++) {
28639                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28640                         }
28641                         if (selNodes.length == 1) {
28642                                 dragData.ddel = target.cloneNode(true); // the div element
28643                         } else {
28644                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28645                                 div.className = 'multi-proxy';
28646                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28647                                         div.appendChild(selNodes[i].cloneNode(true));
28648                                 }
28649                                 dragData.ddel = div;
28650                         }
28651             //console.log(dragData)
28652             //console.log(dragData.ddel.innerHTML)
28653                         return dragData;
28654                 }
28655         //console.log('nodragData')
28656                 return false;
28657     },
28658     
28659 /**     Specify to which ddGroup items in this DDView may be dragged. */
28660     setDraggable: function(ddGroup) {
28661         if (ddGroup instanceof Array) {
28662                 Roo.each(ddGroup, this.setDraggable, this);
28663                 return;
28664         }
28665         if (this.dragZone) {
28666                 this.dragZone.addToGroup(ddGroup);
28667         } else {
28668                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28669                                 containerScroll: true,
28670                                 ddGroup: ddGroup 
28671
28672                         });
28673 //                      Draggability implies selection. DragZone's mousedown selects the element.
28674                         if (!this.multiSelect) { this.singleSelect = true; }
28675
28676 //                      Wire the DragZone's handlers up to methods in *this*
28677                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28678                 }
28679     },
28680
28681 /**     Specify from which ddGroup this DDView accepts drops. */
28682     setDroppable: function(ddGroup) {
28683         if (ddGroup instanceof Array) {
28684                 Roo.each(ddGroup, this.setDroppable, this);
28685                 return;
28686         }
28687         if (this.dropZone) {
28688                 this.dropZone.addToGroup(ddGroup);
28689         } else {
28690                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28691                                 containerScroll: true,
28692                                 ddGroup: ddGroup
28693                         });
28694
28695 //                      Wire the DropZone's handlers up to methods in *this*
28696                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28697                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28698                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28699                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28700                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28701                 }
28702     },
28703
28704 /**     Decide whether to drop above or below a View node. */
28705     getDropPoint : function(e, n, dd){
28706         if (n == this.el.dom) { return "above"; }
28707                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28708                 var c = t + (b - t) / 2;
28709                 var y = Roo.lib.Event.getPageY(e);
28710                 if(y <= c) {
28711                         return "above";
28712                 }else{
28713                         return "below";
28714                 }
28715     },
28716
28717     onNodeEnter : function(n, dd, e, data){
28718                 return false;
28719     },
28720     
28721     onNodeOver : function(n, dd, e, data){
28722                 var pt = this.getDropPoint(e, n, dd);
28723                 // set the insert point style on the target node
28724                 var dragElClass = this.dropNotAllowed;
28725                 if (pt) {
28726                         var targetElClass;
28727                         if (pt == "above"){
28728                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28729                                 targetElClass = "x-view-drag-insert-above";
28730                         } else {
28731                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28732                                 targetElClass = "x-view-drag-insert-below";
28733                         }
28734                         if (this.lastInsertClass != targetElClass){
28735                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28736                                 this.lastInsertClass = targetElClass;
28737                         }
28738                 }
28739                 return dragElClass;
28740         },
28741
28742     onNodeOut : function(n, dd, e, data){
28743                 this.removeDropIndicators(n);
28744     },
28745
28746     onNodeDrop : function(n, dd, e, data){
28747         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28748                 return false;
28749         }
28750         var pt = this.getDropPoint(e, n, dd);
28751                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28752                 if (pt == "below") { insertAt++; }
28753                 for (var i = 0; i < data.records.length; i++) {
28754                         var r = data.records[i];
28755                         var dup = this.store.getById(r.id);
28756                         if (dup && (dd != this.dragZone)) {
28757                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28758                         } else {
28759                                 if (data.copy) {
28760                                         this.store.insert(insertAt++, r.copy());
28761                                 } else {
28762                                         data.source.isDirtyFlag = true;
28763                                         r.store.remove(r);
28764                                         this.store.insert(insertAt++, r);
28765                                 }
28766                                 this.isDirtyFlag = true;
28767                         }
28768                 }
28769                 this.dragZone.cachedTarget = null;
28770                 return true;
28771     },
28772
28773     removeDropIndicators : function(n){
28774                 if(n){
28775                         Roo.fly(n).removeClass([
28776                                 "x-view-drag-insert-above",
28777                                 "x-view-drag-insert-below"]);
28778                         this.lastInsertClass = "_noclass";
28779                 }
28780     },
28781
28782 /**
28783  *      Utility method. Add a delete option to the DDView's context menu.
28784  *      @param {String} imageUrl The URL of the "delete" icon image.
28785  */
28786         setDeletable: function(imageUrl) {
28787                 if (!this.singleSelect && !this.multiSelect) {
28788                         this.singleSelect = true;
28789                 }
28790                 var c = this.getContextMenu();
28791                 this.contextMenu.on("itemclick", function(item) {
28792                         switch (item.id) {
28793                                 case "delete":
28794                                         this.remove(this.getSelectedIndexes());
28795                                         break;
28796                         }
28797                 }, this);
28798                 this.contextMenu.add({
28799                         icon: imageUrl,
28800                         id: "delete",
28801                         text: 'Delete'
28802                 });
28803         },
28804         
28805 /**     Return the context menu for this DDView. */
28806         getContextMenu: function() {
28807                 if (!this.contextMenu) {
28808 //                      Create the View's context menu
28809                         this.contextMenu = new Roo.menu.Menu({
28810                                 id: this.id + "-contextmenu"
28811                         });
28812                         this.el.on("contextmenu", this.showContextMenu, this);
28813                 }
28814                 return this.contextMenu;
28815         },
28816         
28817         disableContextMenu: function() {
28818                 if (this.contextMenu) {
28819                         this.el.un("contextmenu", this.showContextMenu, this);
28820                 }
28821         },
28822
28823         showContextMenu: function(e, item) {
28824         item = this.findItemFromChild(e.getTarget());
28825                 if (item) {
28826                         e.stopEvent();
28827                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28828                         this.contextMenu.showAt(e.getXY());
28829             }
28830     },
28831
28832 /**
28833  *      Remove {@link Roo.data.Record}s at the specified indices.
28834  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28835  */
28836     remove: function(selectedIndices) {
28837                 selectedIndices = [].concat(selectedIndices);
28838                 for (var i = 0; i < selectedIndices.length; i++) {
28839                         var rec = this.store.getAt(selectedIndices[i]);
28840                         this.store.remove(rec);
28841                 }
28842     },
28843
28844 /**
28845  *      Double click fires the event, but also, if this is draggable, and there is only one other
28846  *      related DropZone, it transfers the selected node.
28847  */
28848     onDblClick : function(e){
28849         var item = this.findItemFromChild(e.getTarget());
28850         if(item){
28851             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28852                 return false;
28853             }
28854             if (this.dragGroup) {
28855                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28856                     while (targets.indexOf(this.dropZone) > -1) {
28857                             targets.remove(this.dropZone);
28858                                 }
28859                     if (targets.length == 1) {
28860                                         this.dragZone.cachedTarget = null;
28861                         var el = Roo.get(targets[0].getEl());
28862                         var box = el.getBox(true);
28863                         targets[0].onNodeDrop(el.dom, {
28864                                 target: el.dom,
28865                                 xy: [box.x, box.y + box.height - 1]
28866                         }, null, this.getDragData(e));
28867                     }
28868                 }
28869         }
28870     },
28871     
28872     handleSelection: function(e) {
28873                 this.dragZone.cachedTarget = null;
28874         var item = this.findItemFromChild(e.getTarget());
28875         if (!item) {
28876                 this.clearSelections(true);
28877                 return;
28878         }
28879                 if (item && (this.multiSelect || this.singleSelect)){
28880                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28881                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28882                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28883                                 this.unselect(item);
28884                         } else {
28885                                 this.select(item, this.multiSelect && e.ctrlKey);
28886                                 this.lastSelection = item;
28887                         }
28888                 }
28889     },
28890
28891     onItemClick : function(item, index, e){
28892                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28893                         return false;
28894                 }
28895                 return true;
28896     },
28897
28898     unselect : function(nodeInfo, suppressEvent){
28899                 var node = this.getNode(nodeInfo);
28900                 if(node && this.isSelected(node)){
28901                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28902                                 Roo.fly(node).removeClass(this.selectedClass);
28903                                 this.selections.remove(node);
28904                                 if(!suppressEvent){
28905                                         this.fireEvent("selectionchange", this, this.selections);
28906                                 }
28907                         }
28908                 }
28909     }
28910 });
28911 /*
28912  * Based on:
28913  * Ext JS Library 1.1.1
28914  * Copyright(c) 2006-2007, Ext JS, LLC.
28915  *
28916  * Originally Released Under LGPL - original licence link has changed is not relivant.
28917  *
28918  * Fork - LGPL
28919  * <script type="text/javascript">
28920  */
28921  
28922 /**
28923  * @class Roo.LayoutManager
28924  * @extends Roo.util.Observable
28925  * Base class for layout managers.
28926  */
28927 Roo.LayoutManager = function(container, config){
28928     Roo.LayoutManager.superclass.constructor.call(this);
28929     this.el = Roo.get(container);
28930     // ie scrollbar fix
28931     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28932         document.body.scroll = "no";
28933     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28934         this.el.position('relative');
28935     }
28936     this.id = this.el.id;
28937     this.el.addClass("x-layout-container");
28938     /** false to disable window resize monitoring @type Boolean */
28939     this.monitorWindowResize = true;
28940     this.regions = {};
28941     this.addEvents({
28942         /**
28943          * @event layout
28944          * Fires when a layout is performed. 
28945          * @param {Roo.LayoutManager} this
28946          */
28947         "layout" : true,
28948         /**
28949          * @event regionresized
28950          * Fires when the user resizes a region. 
28951          * @param {Roo.LayoutRegion} region The resized region
28952          * @param {Number} newSize The new size (width for east/west, height for north/south)
28953          */
28954         "regionresized" : true,
28955         /**
28956          * @event regioncollapsed
28957          * Fires when a region is collapsed. 
28958          * @param {Roo.LayoutRegion} region The collapsed region
28959          */
28960         "regioncollapsed" : true,
28961         /**
28962          * @event regionexpanded
28963          * Fires when a region is expanded.  
28964          * @param {Roo.LayoutRegion} region The expanded region
28965          */
28966         "regionexpanded" : true
28967     });
28968     this.updating = false;
28969     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28970 };
28971
28972 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28973     /**
28974      * Returns true if this layout is currently being updated
28975      * @return {Boolean}
28976      */
28977     isUpdating : function(){
28978         return this.updating; 
28979     },
28980     
28981     /**
28982      * Suspend the LayoutManager from doing auto-layouts while
28983      * making multiple add or remove calls
28984      */
28985     beginUpdate : function(){
28986         this.updating = true;    
28987     },
28988     
28989     /**
28990      * Restore auto-layouts and optionally disable the manager from performing a layout
28991      * @param {Boolean} noLayout true to disable a layout update 
28992      */
28993     endUpdate : function(noLayout){
28994         this.updating = false;
28995         if(!noLayout){
28996             this.layout();
28997         }    
28998     },
28999     
29000     layout: function(){
29001         
29002     },
29003     
29004     onRegionResized : function(region, newSize){
29005         this.fireEvent("regionresized", region, newSize);
29006         this.layout();
29007     },
29008     
29009     onRegionCollapsed : function(region){
29010         this.fireEvent("regioncollapsed", region);
29011     },
29012     
29013     onRegionExpanded : function(region){
29014         this.fireEvent("regionexpanded", region);
29015     },
29016         
29017     /**
29018      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29019      * performs box-model adjustments.
29020      * @return {Object} The size as an object {width: (the width), height: (the height)}
29021      */
29022     getViewSize : function(){
29023         var size;
29024         if(this.el.dom != document.body){
29025             size = this.el.getSize();
29026         }else{
29027             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29028         }
29029         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29030         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29031         return size;
29032     },
29033     
29034     /**
29035      * Returns the Element this layout is bound to.
29036      * @return {Roo.Element}
29037      */
29038     getEl : function(){
29039         return this.el;
29040     },
29041     
29042     /**
29043      * Returns the specified region.
29044      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29045      * @return {Roo.LayoutRegion}
29046      */
29047     getRegion : function(target){
29048         return this.regions[target.toLowerCase()];
29049     },
29050     
29051     onWindowResize : function(){
29052         if(this.monitorWindowResize){
29053             this.layout();
29054         }
29055     }
29056 });/*
29057  * Based on:
29058  * Ext JS Library 1.1.1
29059  * Copyright(c) 2006-2007, Ext JS, LLC.
29060  *
29061  * Originally Released Under LGPL - original licence link has changed is not relivant.
29062  *
29063  * Fork - LGPL
29064  * <script type="text/javascript">
29065  */
29066 /**
29067  * @class Roo.BorderLayout
29068  * @extends Roo.LayoutManager
29069  * @children Roo.ContentPanel
29070  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29071  * please see: <br><br>
29072  * <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>
29073  * <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>
29074  * Example:
29075  <pre><code>
29076  var layout = new Roo.BorderLayout(document.body, {
29077     north: {
29078         initialSize: 25,
29079         titlebar: false
29080     },
29081     west: {
29082         split:true,
29083         initialSize: 200,
29084         minSize: 175,
29085         maxSize: 400,
29086         titlebar: true,
29087         collapsible: true
29088     },
29089     east: {
29090         split:true,
29091         initialSize: 202,
29092         minSize: 175,
29093         maxSize: 400,
29094         titlebar: true,
29095         collapsible: true
29096     },
29097     south: {
29098         split:true,
29099         initialSize: 100,
29100         minSize: 100,
29101         maxSize: 200,
29102         titlebar: true,
29103         collapsible: true
29104     },
29105     center: {
29106         titlebar: true,
29107         autoScroll:true,
29108         resizeTabs: true,
29109         minTabWidth: 50,
29110         preferredTabWidth: 150
29111     }
29112 });
29113
29114 // shorthand
29115 var CP = Roo.ContentPanel;
29116
29117 layout.beginUpdate();
29118 layout.add("north", new CP("north", "North"));
29119 layout.add("south", new CP("south", {title: "South", closable: true}));
29120 layout.add("west", new CP("west", {title: "West"}));
29121 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29122 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29123 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29124 layout.getRegion("center").showPanel("center1");
29125 layout.endUpdate();
29126 </code></pre>
29127
29128 <b>The container the layout is rendered into can be either the body element or any other element.
29129 If it is not the body element, the container needs to either be an absolute positioned element,
29130 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29131 the container size if it is not the body element.</b>
29132
29133 * @constructor
29134 * Create a new BorderLayout
29135 * @param {String/HTMLElement/Element} container The container this layout is bound to
29136 * @param {Object} config Configuration options
29137  */
29138 Roo.BorderLayout = function(container, config){
29139     config = config || {};
29140     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29141     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29142     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29143         var target = this.factory.validRegions[i];
29144         if(config[target]){
29145             this.addRegion(target, config[target]);
29146         }
29147     }
29148 };
29149
29150 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29151         
29152         /**
29153          * @cfg {Roo.LayoutRegion} east
29154          */
29155         /**
29156          * @cfg {Roo.LayoutRegion} west
29157          */
29158         /**
29159          * @cfg {Roo.LayoutRegion} north
29160          */
29161         /**
29162          * @cfg {Roo.LayoutRegion} south
29163          */
29164         /**
29165          * @cfg {Roo.LayoutRegion} center
29166          */
29167     /**
29168      * Creates and adds a new region if it doesn't already exist.
29169      * @param {String} target The target region key (north, south, east, west or center).
29170      * @param {Object} config The regions config object
29171      * @return {BorderLayoutRegion} The new region
29172      */
29173     addRegion : function(target, config){
29174         if(!this.regions[target]){
29175             var r = this.factory.create(target, this, config);
29176             this.bindRegion(target, r);
29177         }
29178         return this.regions[target];
29179     },
29180
29181     // private (kinda)
29182     bindRegion : function(name, r){
29183         this.regions[name] = r;
29184         r.on("visibilitychange", this.layout, this);
29185         r.on("paneladded", this.layout, this);
29186         r.on("panelremoved", this.layout, this);
29187         r.on("invalidated", this.layout, this);
29188         r.on("resized", this.onRegionResized, this);
29189         r.on("collapsed", this.onRegionCollapsed, this);
29190         r.on("expanded", this.onRegionExpanded, this);
29191     },
29192
29193     /**
29194      * Performs a layout update.
29195      */
29196     layout : function(){
29197         if(this.updating) {
29198             return;
29199         }
29200         var size = this.getViewSize();
29201         var w = size.width;
29202         var h = size.height;
29203         var centerW = w;
29204         var centerH = h;
29205         var centerY = 0;
29206         var centerX = 0;
29207         //var x = 0, y = 0;
29208
29209         var rs = this.regions;
29210         var north = rs["north"];
29211         var south = rs["south"]; 
29212         var west = rs["west"];
29213         var east = rs["east"];
29214         var center = rs["center"];
29215         //if(this.hideOnLayout){ // not supported anymore
29216             //c.el.setStyle("display", "none");
29217         //}
29218         if(north && north.isVisible()){
29219             var b = north.getBox();
29220             var m = north.getMargins();
29221             b.width = w - (m.left+m.right);
29222             b.x = m.left;
29223             b.y = m.top;
29224             centerY = b.height + b.y + m.bottom;
29225             centerH -= centerY;
29226             north.updateBox(this.safeBox(b));
29227         }
29228         if(south && south.isVisible()){
29229             var b = south.getBox();
29230             var m = south.getMargins();
29231             b.width = w - (m.left+m.right);
29232             b.x = m.left;
29233             var totalHeight = (b.height + m.top + m.bottom);
29234             b.y = h - totalHeight + m.top;
29235             centerH -= totalHeight;
29236             south.updateBox(this.safeBox(b));
29237         }
29238         if(west && west.isVisible()){
29239             var b = west.getBox();
29240             var m = west.getMargins();
29241             b.height = centerH - (m.top+m.bottom);
29242             b.x = m.left;
29243             b.y = centerY + m.top;
29244             var totalWidth = (b.width + m.left + m.right);
29245             centerX += totalWidth;
29246             centerW -= totalWidth;
29247             west.updateBox(this.safeBox(b));
29248         }
29249         if(east && east.isVisible()){
29250             var b = east.getBox();
29251             var m = east.getMargins();
29252             b.height = centerH - (m.top+m.bottom);
29253             var totalWidth = (b.width + m.left + m.right);
29254             b.x = w - totalWidth + m.left;
29255             b.y = centerY + m.top;
29256             centerW -= totalWidth;
29257             east.updateBox(this.safeBox(b));
29258         }
29259         if(center){
29260             var m = center.getMargins();
29261             var centerBox = {
29262                 x: centerX + m.left,
29263                 y: centerY + m.top,
29264                 width: centerW - (m.left+m.right),
29265                 height: centerH - (m.top+m.bottom)
29266             };
29267             //if(this.hideOnLayout){
29268                 //center.el.setStyle("display", "block");
29269             //}
29270             center.updateBox(this.safeBox(centerBox));
29271         }
29272         this.el.repaint();
29273         this.fireEvent("layout", this);
29274     },
29275
29276     // private
29277     safeBox : function(box){
29278         box.width = Math.max(0, box.width);
29279         box.height = Math.max(0, box.height);
29280         return box;
29281     },
29282
29283     /**
29284      * Adds a ContentPanel (or subclass) to this layout.
29285      * @param {String} target The target region key (north, south, east, west or center).
29286      * @param {Roo.ContentPanel} panel The panel to add
29287      * @return {Roo.ContentPanel} The added panel
29288      */
29289     add : function(target, panel){
29290          
29291         target = target.toLowerCase();
29292         return this.regions[target].add(panel);
29293     },
29294
29295     /**
29296      * Remove a ContentPanel (or subclass) to this layout.
29297      * @param {String} target The target region key (north, south, east, west or center).
29298      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29299      * @return {Roo.ContentPanel} The removed panel
29300      */
29301     remove : function(target, panel){
29302         target = target.toLowerCase();
29303         return this.regions[target].remove(panel);
29304     },
29305
29306     /**
29307      * Searches all regions for a panel with the specified id
29308      * @param {String} panelId
29309      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29310      */
29311     findPanel : function(panelId){
29312         var rs = this.regions;
29313         for(var target in rs){
29314             if(typeof rs[target] != "function"){
29315                 var p = rs[target].getPanel(panelId);
29316                 if(p){
29317                     return p;
29318                 }
29319             }
29320         }
29321         return null;
29322     },
29323
29324     /**
29325      * Searches all regions for a panel with the specified id and activates (shows) it.
29326      * @param {String/ContentPanel} panelId The panels id or the panel itself
29327      * @return {Roo.ContentPanel} The shown panel or null
29328      */
29329     showPanel : function(panelId) {
29330       var rs = this.regions;
29331       for(var target in rs){
29332          var r = rs[target];
29333          if(typeof r != "function"){
29334             if(r.hasPanel(panelId)){
29335                return r.showPanel(panelId);
29336             }
29337          }
29338       }
29339       return null;
29340    },
29341
29342    /**
29343      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29344      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29345      */
29346     restoreState : function(provider){
29347         if(!provider){
29348             provider = Roo.state.Manager;
29349         }
29350         var sm = new Roo.LayoutStateManager();
29351         sm.init(this, provider);
29352     },
29353
29354     /**
29355      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29356      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29357      * a valid ContentPanel config object.  Example:
29358      * <pre><code>
29359 // Create the main layout
29360 var layout = new Roo.BorderLayout('main-ct', {
29361     west: {
29362         split:true,
29363         minSize: 175,
29364         titlebar: true
29365     },
29366     center: {
29367         title:'Components'
29368     }
29369 }, 'main-ct');
29370
29371 // Create and add multiple ContentPanels at once via configs
29372 layout.batchAdd({
29373    west: {
29374        id: 'source-files',
29375        autoCreate:true,
29376        title:'Ext Source Files',
29377        autoScroll:true,
29378        fitToFrame:true
29379    },
29380    center : {
29381        el: cview,
29382        autoScroll:true,
29383        fitToFrame:true,
29384        toolbar: tb,
29385        resizeEl:'cbody'
29386    }
29387 });
29388 </code></pre>
29389      * @param {Object} regions An object containing ContentPanel configs by region name
29390      */
29391     batchAdd : function(regions){
29392         this.beginUpdate();
29393         for(var rname in regions){
29394             var lr = this.regions[rname];
29395             if(lr){
29396                 this.addTypedPanels(lr, regions[rname]);
29397             }
29398         }
29399         this.endUpdate();
29400     },
29401
29402     // private
29403     addTypedPanels : function(lr, ps){
29404         if(typeof ps == 'string'){
29405             lr.add(new Roo.ContentPanel(ps));
29406         }
29407         else if(ps instanceof Array){
29408             for(var i =0, len = ps.length; i < len; i++){
29409                 this.addTypedPanels(lr, ps[i]);
29410             }
29411         }
29412         else if(!ps.events){ // raw config?
29413             var el = ps.el;
29414             delete ps.el; // prevent conflict
29415             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29416         }
29417         else {  // panel object assumed!
29418             lr.add(ps);
29419         }
29420     },
29421     /**
29422      * Adds a xtype elements to the layout.
29423      * <pre><code>
29424
29425 layout.addxtype({
29426        xtype : 'ContentPanel',
29427        region: 'west',
29428        items: [ .... ]
29429    }
29430 );
29431
29432 layout.addxtype({
29433         xtype : 'NestedLayoutPanel',
29434         region: 'west',
29435         layout: {
29436            center: { },
29437            west: { }   
29438         },
29439         items : [ ... list of content panels or nested layout panels.. ]
29440    }
29441 );
29442 </code></pre>
29443      * @param {Object} cfg Xtype definition of item to add.
29444      */
29445     addxtype : function(cfg)
29446     {
29447         // basically accepts a pannel...
29448         // can accept a layout region..!?!?
29449         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29450         
29451         if (!cfg.xtype.match(/Panel$/)) {
29452             return false;
29453         }
29454         var ret = false;
29455         
29456         if (typeof(cfg.region) == 'undefined') {
29457             Roo.log("Failed to add Panel, region was not set");
29458             Roo.log(cfg);
29459             return false;
29460         }
29461         var region = cfg.region;
29462         delete cfg.region;
29463         
29464           
29465         var xitems = [];
29466         if (cfg.items) {
29467             xitems = cfg.items;
29468             delete cfg.items;
29469         }
29470         var nb = false;
29471         
29472         switch(cfg.xtype) 
29473         {
29474             case 'ContentPanel':  // ContentPanel (el, cfg)
29475             case 'ScrollPanel':  // ContentPanel (el, cfg)
29476             case 'ViewPanel': 
29477                 if(cfg.autoCreate) {
29478                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29479                 } else {
29480                     var el = this.el.createChild();
29481                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29482                 }
29483                 
29484                 this.add(region, ret);
29485                 break;
29486             
29487             
29488             case 'TreePanel': // our new panel!
29489                 cfg.el = this.el.createChild();
29490                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29491                 this.add(region, ret);
29492                 break;
29493             
29494             case 'NestedLayoutPanel': 
29495                 // create a new Layout (which is  a Border Layout...
29496                 var el = this.el.createChild();
29497                 var clayout = cfg.layout;
29498                 delete cfg.layout;
29499                 clayout.items   = clayout.items  || [];
29500                 // replace this exitems with the clayout ones..
29501                 xitems = clayout.items;
29502                  
29503                 
29504                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29505                     cfg.background = false;
29506                 }
29507                 var layout = new Roo.BorderLayout(el, clayout);
29508                 
29509                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29510                 //console.log('adding nested layout panel '  + cfg.toSource());
29511                 this.add(region, ret);
29512                 nb = {}; /// find first...
29513                 break;
29514                 
29515             case 'GridPanel': 
29516             
29517                 // needs grid and region
29518                 
29519                 //var el = this.getRegion(region).el.createChild();
29520                 var el = this.el.createChild();
29521                 // create the grid first...
29522                 
29523                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29524                 delete cfg.grid;
29525                 if (region == 'center' && this.active ) {
29526                     cfg.background = false;
29527                 }
29528                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29529                 
29530                 this.add(region, ret);
29531                 if (cfg.background) {
29532                     ret.on('activate', function(gp) {
29533                         if (!gp.grid.rendered) {
29534                             gp.grid.render();
29535                         }
29536                     });
29537                 } else {
29538                     grid.render();
29539                 }
29540                 break;
29541            
29542            
29543            
29544                 
29545                 
29546                 
29547             default:
29548                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29549                     
29550                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29551                     this.add(region, ret);
29552                 } else {
29553                 
29554                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29555                     return null;
29556                 }
29557                 
29558              // GridPanel (grid, cfg)
29559             
29560         }
29561         this.beginUpdate();
29562         // add children..
29563         var region = '';
29564         var abn = {};
29565         Roo.each(xitems, function(i)  {
29566             region = nb && i.region ? i.region : false;
29567             
29568             var add = ret.addxtype(i);
29569            
29570             if (region) {
29571                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29572                 if (!i.background) {
29573                     abn[region] = nb[region] ;
29574                 }
29575             }
29576             
29577         });
29578         this.endUpdate();
29579
29580         // make the last non-background panel active..
29581         //if (nb) { Roo.log(abn); }
29582         if (nb) {
29583             
29584             for(var r in abn) {
29585                 region = this.getRegion(r);
29586                 if (region) {
29587                     // tried using nb[r], but it does not work..
29588                      
29589                     region.showPanel(abn[r]);
29590                    
29591                 }
29592             }
29593         }
29594         return ret;
29595         
29596     }
29597 });
29598
29599 /**
29600  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29601  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29602  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29603  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29604  * <pre><code>
29605 // shorthand
29606 var CP = Roo.ContentPanel;
29607
29608 var layout = Roo.BorderLayout.create({
29609     north: {
29610         initialSize: 25,
29611         titlebar: false,
29612         panels: [new CP("north", "North")]
29613     },
29614     west: {
29615         split:true,
29616         initialSize: 200,
29617         minSize: 175,
29618         maxSize: 400,
29619         titlebar: true,
29620         collapsible: true,
29621         panels: [new CP("west", {title: "West"})]
29622     },
29623     east: {
29624         split:true,
29625         initialSize: 202,
29626         minSize: 175,
29627         maxSize: 400,
29628         titlebar: true,
29629         collapsible: true,
29630         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29631     },
29632     south: {
29633         split:true,
29634         initialSize: 100,
29635         minSize: 100,
29636         maxSize: 200,
29637         titlebar: true,
29638         collapsible: true,
29639         panels: [new CP("south", {title: "South", closable: true})]
29640     },
29641     center: {
29642         titlebar: true,
29643         autoScroll:true,
29644         resizeTabs: true,
29645         minTabWidth: 50,
29646         preferredTabWidth: 150,
29647         panels: [
29648             new CP("center1", {title: "Close Me", closable: true}),
29649             new CP("center2", {title: "Center Panel", closable: false})
29650         ]
29651     }
29652 }, document.body);
29653
29654 layout.getRegion("center").showPanel("center1");
29655 </code></pre>
29656  * @param config
29657  * @param targetEl
29658  */
29659 Roo.BorderLayout.create = function(config, targetEl){
29660     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29661     layout.beginUpdate();
29662     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29663     for(var j = 0, jlen = regions.length; j < jlen; j++){
29664         var lr = regions[j];
29665         if(layout.regions[lr] && config[lr].panels){
29666             var r = layout.regions[lr];
29667             var ps = config[lr].panels;
29668             layout.addTypedPanels(r, ps);
29669         }
29670     }
29671     layout.endUpdate();
29672     return layout;
29673 };
29674
29675 // private
29676 Roo.BorderLayout.RegionFactory = {
29677     // private
29678     validRegions : ["north","south","east","west","center"],
29679
29680     // private
29681     create : function(target, mgr, config){
29682         target = target.toLowerCase();
29683         if(config.lightweight || config.basic){
29684             return new Roo.BasicLayoutRegion(mgr, config, target);
29685         }
29686         switch(target){
29687             case "north":
29688                 return new Roo.NorthLayoutRegion(mgr, config);
29689             case "south":
29690                 return new Roo.SouthLayoutRegion(mgr, config);
29691             case "east":
29692                 return new Roo.EastLayoutRegion(mgr, config);
29693             case "west":
29694                 return new Roo.WestLayoutRegion(mgr, config);
29695             case "center":
29696                 return new Roo.CenterLayoutRegion(mgr, config);
29697         }
29698         throw 'Layout region "'+target+'" not supported.';
29699     }
29700 };/*
29701  * Based on:
29702  * Ext JS Library 1.1.1
29703  * Copyright(c) 2006-2007, Ext JS, LLC.
29704  *
29705  * Originally Released Under LGPL - original licence link has changed is not relivant.
29706  *
29707  * Fork - LGPL
29708  * <script type="text/javascript">
29709  */
29710  
29711 /**
29712  * @class Roo.BasicLayoutRegion
29713  * @extends Roo.util.Observable
29714  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29715  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29716  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29717  */
29718 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29719     this.mgr = mgr;
29720     this.position  = pos;
29721     this.events = {
29722         /**
29723          * @scope Roo.BasicLayoutRegion
29724          */
29725         
29726         /**
29727          * @event beforeremove
29728          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29729          * @param {Roo.LayoutRegion} this
29730          * @param {Roo.ContentPanel} panel The panel
29731          * @param {Object} e The cancel event object
29732          */
29733         "beforeremove" : true,
29734         /**
29735          * @event invalidated
29736          * Fires when the layout for this region is changed.
29737          * @param {Roo.LayoutRegion} this
29738          */
29739         "invalidated" : true,
29740         /**
29741          * @event visibilitychange
29742          * Fires when this region is shown or hidden 
29743          * @param {Roo.LayoutRegion} this
29744          * @param {Boolean} visibility true or false
29745          */
29746         "visibilitychange" : true,
29747         /**
29748          * @event paneladded
29749          * Fires when a panel is added. 
29750          * @param {Roo.LayoutRegion} this
29751          * @param {Roo.ContentPanel} panel The panel
29752          */
29753         "paneladded" : true,
29754         /**
29755          * @event panelremoved
29756          * Fires when a panel is removed. 
29757          * @param {Roo.LayoutRegion} this
29758          * @param {Roo.ContentPanel} panel The panel
29759          */
29760         "panelremoved" : true,
29761         /**
29762          * @event beforecollapse
29763          * Fires when this region before collapse.
29764          * @param {Roo.LayoutRegion} this
29765          */
29766         "beforecollapse" : true,
29767         /**
29768          * @event collapsed
29769          * Fires when this region is collapsed.
29770          * @param {Roo.LayoutRegion} this
29771          */
29772         "collapsed" : true,
29773         /**
29774          * @event expanded
29775          * Fires when this region is expanded.
29776          * @param {Roo.LayoutRegion} this
29777          */
29778         "expanded" : true,
29779         /**
29780          * @event slideshow
29781          * Fires when this region is slid into view.
29782          * @param {Roo.LayoutRegion} this
29783          */
29784         "slideshow" : true,
29785         /**
29786          * @event slidehide
29787          * Fires when this region slides out of view. 
29788          * @param {Roo.LayoutRegion} this
29789          */
29790         "slidehide" : true,
29791         /**
29792          * @event panelactivated
29793          * Fires when a panel is activated. 
29794          * @param {Roo.LayoutRegion} this
29795          * @param {Roo.ContentPanel} panel The activated panel
29796          */
29797         "panelactivated" : true,
29798         /**
29799          * @event resized
29800          * Fires when the user resizes this region. 
29801          * @param {Roo.LayoutRegion} this
29802          * @param {Number} newSize The new size (width for east/west, height for north/south)
29803          */
29804         "resized" : true
29805     };
29806     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29807     this.panels = new Roo.util.MixedCollection();
29808     this.panels.getKey = this.getPanelId.createDelegate(this);
29809     this.box = null;
29810     this.activePanel = null;
29811     // ensure listeners are added...
29812     
29813     if (config.listeners || config.events) {
29814         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29815             listeners : config.listeners || {},
29816             events : config.events || {}
29817         });
29818     }
29819     
29820     if(skipConfig !== true){
29821         this.applyConfig(config);
29822     }
29823 };
29824
29825 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29826     getPanelId : function(p){
29827         return p.getId();
29828     },
29829     
29830     applyConfig : function(config){
29831         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29832         this.config = config;
29833         
29834     },
29835     
29836     /**
29837      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29838      * the width, for horizontal (north, south) the height.
29839      * @param {Number} newSize The new width or height
29840      */
29841     resizeTo : function(newSize){
29842         var el = this.el ? this.el :
29843                  (this.activePanel ? this.activePanel.getEl() : null);
29844         if(el){
29845             switch(this.position){
29846                 case "east":
29847                 case "west":
29848                     el.setWidth(newSize);
29849                     this.fireEvent("resized", this, newSize);
29850                 break;
29851                 case "north":
29852                 case "south":
29853                     el.setHeight(newSize);
29854                     this.fireEvent("resized", this, newSize);
29855                 break;                
29856             }
29857         }
29858     },
29859     
29860     getBox : function(){
29861         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29862     },
29863     
29864     getMargins : function(){
29865         return this.margins;
29866     },
29867     
29868     updateBox : function(box){
29869         this.box = box;
29870         var el = this.activePanel.getEl();
29871         el.dom.style.left = box.x + "px";
29872         el.dom.style.top = box.y + "px";
29873         this.activePanel.setSize(box.width, box.height);
29874     },
29875     
29876     /**
29877      * Returns the container element for this region.
29878      * @return {Roo.Element}
29879      */
29880     getEl : function(){
29881         return this.activePanel;
29882     },
29883     
29884     /**
29885      * Returns true if this region is currently visible.
29886      * @return {Boolean}
29887      */
29888     isVisible : function(){
29889         return this.activePanel ? true : false;
29890     },
29891     
29892     setActivePanel : function(panel){
29893         panel = this.getPanel(panel);
29894         if(this.activePanel && this.activePanel != panel){
29895             this.activePanel.setActiveState(false);
29896             this.activePanel.getEl().setLeftTop(-10000,-10000);
29897         }
29898         this.activePanel = panel;
29899         panel.setActiveState(true);
29900         if(this.box){
29901             panel.setSize(this.box.width, this.box.height);
29902         }
29903         this.fireEvent("panelactivated", this, panel);
29904         this.fireEvent("invalidated");
29905     },
29906     
29907     /**
29908      * Show the specified panel.
29909      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29910      * @return {Roo.ContentPanel} The shown panel or null
29911      */
29912     showPanel : function(panel){
29913         if(panel = this.getPanel(panel)){
29914             this.setActivePanel(panel);
29915         }
29916         return panel;
29917     },
29918     
29919     /**
29920      * Get the active panel for this region.
29921      * @return {Roo.ContentPanel} The active panel or null
29922      */
29923     getActivePanel : function(){
29924         return this.activePanel;
29925     },
29926     
29927     /**
29928      * Add the passed ContentPanel(s)
29929      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29930      * @return {Roo.ContentPanel} The panel added (if only one was added)
29931      */
29932     add : function(panel){
29933         if(arguments.length > 1){
29934             for(var i = 0, len = arguments.length; i < len; i++) {
29935                 this.add(arguments[i]);
29936             }
29937             return null;
29938         }
29939         if(this.hasPanel(panel)){
29940             this.showPanel(panel);
29941             return panel;
29942         }
29943         var el = panel.getEl();
29944         if(el.dom.parentNode != this.mgr.el.dom){
29945             this.mgr.el.dom.appendChild(el.dom);
29946         }
29947         if(panel.setRegion){
29948             panel.setRegion(this);
29949         }
29950         this.panels.add(panel);
29951         el.setStyle("position", "absolute");
29952         if(!panel.background){
29953             this.setActivePanel(panel);
29954             if(this.config.initialSize && this.panels.getCount()==1){
29955                 this.resizeTo(this.config.initialSize);
29956             }
29957         }
29958         this.fireEvent("paneladded", this, panel);
29959         return panel;
29960     },
29961     
29962     /**
29963      * Returns true if the panel is in this region.
29964      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29965      * @return {Boolean}
29966      */
29967     hasPanel : function(panel){
29968         if(typeof panel == "object"){ // must be panel obj
29969             panel = panel.getId();
29970         }
29971         return this.getPanel(panel) ? true : false;
29972     },
29973     
29974     /**
29975      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29976      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29977      * @param {Boolean} preservePanel Overrides the config preservePanel option
29978      * @return {Roo.ContentPanel} The panel that was removed
29979      */
29980     remove : function(panel, preservePanel){
29981         panel = this.getPanel(panel);
29982         if(!panel){
29983             return null;
29984         }
29985         var e = {};
29986         this.fireEvent("beforeremove", this, panel, e);
29987         if(e.cancel === true){
29988             return null;
29989         }
29990         var panelId = panel.getId();
29991         this.panels.removeKey(panelId);
29992         return panel;
29993     },
29994     
29995     /**
29996      * Returns the panel specified or null if it's not in this region.
29997      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29998      * @return {Roo.ContentPanel}
29999      */
30000     getPanel : function(id){
30001         if(typeof id == "object"){ // must be panel obj
30002             return id;
30003         }
30004         return this.panels.get(id);
30005     },
30006     
30007     /**
30008      * Returns this regions position (north/south/east/west/center).
30009      * @return {String} 
30010      */
30011     getPosition: function(){
30012         return this.position;    
30013     }
30014 });/*
30015  * Based on:
30016  * Ext JS Library 1.1.1
30017  * Copyright(c) 2006-2007, Ext JS, LLC.
30018  *
30019  * Originally Released Under LGPL - original licence link has changed is not relivant.
30020  *
30021  * Fork - LGPL
30022  * <script type="text/javascript">
30023  */
30024  
30025 /**
30026  * @class Roo.LayoutRegion
30027  * @extends Roo.BasicLayoutRegion
30028  * This class represents a region in a layout manager.
30029  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30030  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30031  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30032  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30033  * @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})
30034  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30035  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30036  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30037  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30038  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30039  * @cfg {String}    title           The title for the region (overrides panel titles)
30040  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30041  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30042  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30043  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30044  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30045  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30046  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30047  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30048  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30049  * @cfg {Boolean}   showPin         True to show a pin button
30050  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30051  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30052  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30053  * @cfg {Number}    width           For East/West panels
30054  * @cfg {Number}    height          For North/South panels
30055  * @cfg {Boolean}   split           To show the splitter
30056  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30057  */
30058 Roo.LayoutRegion = function(mgr, config, pos){
30059     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30060     var dh = Roo.DomHelper;
30061     /** This region's container element 
30062     * @type Roo.Element */
30063     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30064     /** This region's title element 
30065     * @type Roo.Element */
30066
30067     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30068         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30069         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30070     ]}, true);
30071     this.titleEl.enableDisplayMode();
30072     /** This region's title text element 
30073     * @type HTMLElement */
30074     this.titleTextEl = this.titleEl.dom.firstChild;
30075     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30076     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30077     this.closeBtn.enableDisplayMode();
30078     this.closeBtn.on("click", this.closeClicked, this);
30079     this.closeBtn.hide();
30080
30081     this.createBody(config);
30082     this.visible = true;
30083     this.collapsed = false;
30084
30085     if(config.hideWhenEmpty){
30086         this.hide();
30087         this.on("paneladded", this.validateVisibility, this);
30088         this.on("panelremoved", this.validateVisibility, this);
30089     }
30090     this.applyConfig(config);
30091 };
30092
30093 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30094
30095     createBody : function(){
30096         /** This region's body element 
30097         * @type Roo.Element */
30098         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30099     },
30100
30101     applyConfig : function(c){
30102         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30103             var dh = Roo.DomHelper;
30104             if(c.titlebar !== false){
30105                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30106                 this.collapseBtn.on("click", this.collapse, this);
30107                 this.collapseBtn.enableDisplayMode();
30108
30109                 if(c.showPin === true || this.showPin){
30110                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30111                     this.stickBtn.enableDisplayMode();
30112                     this.stickBtn.on("click", this.expand, this);
30113                     this.stickBtn.hide();
30114                 }
30115             }
30116             /** This region's collapsed element
30117             * @type Roo.Element */
30118             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30119                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30120             ]}, true);
30121             if(c.floatable !== false){
30122                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30123                this.collapsedEl.on("click", this.collapseClick, this);
30124             }
30125
30126             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30127                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30128                    id: "message", unselectable: "on", style:{"float":"left"}});
30129                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30130              }
30131             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30132             this.expandBtn.on("click", this.expand, this);
30133         }
30134         if(this.collapseBtn){
30135             this.collapseBtn.setVisible(c.collapsible == true);
30136         }
30137         this.cmargins = c.cmargins || this.cmargins ||
30138                          (this.position == "west" || this.position == "east" ?
30139                              {top: 0, left: 2, right:2, bottom: 0} :
30140                              {top: 2, left: 0, right:0, bottom: 2});
30141         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30142         this.bottomTabs = c.tabPosition != "top";
30143         this.autoScroll = c.autoScroll || false;
30144         if(this.autoScroll){
30145             this.bodyEl.setStyle("overflow", "auto");
30146         }else{
30147             this.bodyEl.setStyle("overflow", "hidden");
30148         }
30149         //if(c.titlebar !== false){
30150             if((!c.titlebar && !c.title) || c.titlebar === false){
30151                 this.titleEl.hide();
30152             }else{
30153                 this.titleEl.show();
30154                 if(c.title){
30155                     this.titleTextEl.innerHTML = c.title;
30156                 }
30157             }
30158         //}
30159         this.duration = c.duration || .30;
30160         this.slideDuration = c.slideDuration || .45;
30161         this.config = c;
30162         if(c.collapsed){
30163             this.collapse(true);
30164         }
30165         if(c.hidden){
30166             this.hide();
30167         }
30168     },
30169     /**
30170      * Returns true if this region is currently visible.
30171      * @return {Boolean}
30172      */
30173     isVisible : function(){
30174         return this.visible;
30175     },
30176
30177     /**
30178      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30179      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30180      */
30181     setCollapsedTitle : function(title){
30182         title = title || "&#160;";
30183         if(this.collapsedTitleTextEl){
30184             this.collapsedTitleTextEl.innerHTML = title;
30185         }
30186     },
30187
30188     getBox : function(){
30189         var b;
30190         if(!this.collapsed){
30191             b = this.el.getBox(false, true);
30192         }else{
30193             b = this.collapsedEl.getBox(false, true);
30194         }
30195         return b;
30196     },
30197
30198     getMargins : function(){
30199         return this.collapsed ? this.cmargins : this.margins;
30200     },
30201
30202     highlight : function(){
30203         this.el.addClass("x-layout-panel-dragover");
30204     },
30205
30206     unhighlight : function(){
30207         this.el.removeClass("x-layout-panel-dragover");
30208     },
30209
30210     updateBox : function(box){
30211         this.box = box;
30212         if(!this.collapsed){
30213             this.el.dom.style.left = box.x + "px";
30214             this.el.dom.style.top = box.y + "px";
30215             this.updateBody(box.width, box.height);
30216         }else{
30217             this.collapsedEl.dom.style.left = box.x + "px";
30218             this.collapsedEl.dom.style.top = box.y + "px";
30219             this.collapsedEl.setSize(box.width, box.height);
30220         }
30221         if(this.tabs){
30222             this.tabs.autoSizeTabs();
30223         }
30224     },
30225
30226     updateBody : function(w, h){
30227         if(w !== null){
30228             this.el.setWidth(w);
30229             w -= this.el.getBorderWidth("rl");
30230             if(this.config.adjustments){
30231                 w += this.config.adjustments[0];
30232             }
30233         }
30234         if(h !== null){
30235             this.el.setHeight(h);
30236             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30237             h -= this.el.getBorderWidth("tb");
30238             if(this.config.adjustments){
30239                 h += this.config.adjustments[1];
30240             }
30241             this.bodyEl.setHeight(h);
30242             if(this.tabs){
30243                 h = this.tabs.syncHeight(h);
30244             }
30245         }
30246         if(this.panelSize){
30247             w = w !== null ? w : this.panelSize.width;
30248             h = h !== null ? h : this.panelSize.height;
30249         }
30250         if(this.activePanel){
30251             var el = this.activePanel.getEl();
30252             w = w !== null ? w : el.getWidth();
30253             h = h !== null ? h : el.getHeight();
30254             this.panelSize = {width: w, height: h};
30255             this.activePanel.setSize(w, h);
30256         }
30257         if(Roo.isIE && this.tabs){
30258             this.tabs.el.repaint();
30259         }
30260     },
30261
30262     /**
30263      * Returns the container element for this region.
30264      * @return {Roo.Element}
30265      */
30266     getEl : function(){
30267         return this.el;
30268     },
30269
30270     /**
30271      * Hides this region.
30272      */
30273     hide : function(){
30274         if(!this.collapsed){
30275             this.el.dom.style.left = "-2000px";
30276             this.el.hide();
30277         }else{
30278             this.collapsedEl.dom.style.left = "-2000px";
30279             this.collapsedEl.hide();
30280         }
30281         this.visible = false;
30282         this.fireEvent("visibilitychange", this, false);
30283     },
30284
30285     /**
30286      * Shows this region if it was previously hidden.
30287      */
30288     show : function(){
30289         if(!this.collapsed){
30290             this.el.show();
30291         }else{
30292             this.collapsedEl.show();
30293         }
30294         this.visible = true;
30295         this.fireEvent("visibilitychange", this, true);
30296     },
30297
30298     closeClicked : function(){
30299         if(this.activePanel){
30300             this.remove(this.activePanel);
30301         }
30302     },
30303
30304     collapseClick : function(e){
30305         if(this.isSlid){
30306            e.stopPropagation();
30307            this.slideIn();
30308         }else{
30309            e.stopPropagation();
30310            this.slideOut();
30311         }
30312     },
30313
30314     /**
30315      * Collapses this region.
30316      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30317      */
30318     collapse : function(skipAnim, skipCheck){
30319         if(this.collapsed) {
30320             return;
30321         }
30322         
30323         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30324             
30325             this.collapsed = true;
30326             if(this.split){
30327                 this.split.el.hide();
30328             }
30329             if(this.config.animate && skipAnim !== true){
30330                 this.fireEvent("invalidated", this);
30331                 this.animateCollapse();
30332             }else{
30333                 this.el.setLocation(-20000,-20000);
30334                 this.el.hide();
30335                 this.collapsedEl.show();
30336                 this.fireEvent("collapsed", this);
30337                 this.fireEvent("invalidated", this);
30338             }
30339         }
30340         
30341     },
30342
30343     animateCollapse : function(){
30344         // overridden
30345     },
30346
30347     /**
30348      * Expands this region if it was previously collapsed.
30349      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30350      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30351      */
30352     expand : function(e, skipAnim){
30353         if(e) {
30354             e.stopPropagation();
30355         }
30356         if(!this.collapsed || this.el.hasActiveFx()) {
30357             return;
30358         }
30359         if(this.isSlid){
30360             this.afterSlideIn();
30361             skipAnim = true;
30362         }
30363         this.collapsed = false;
30364         if(this.config.animate && skipAnim !== true){
30365             this.animateExpand();
30366         }else{
30367             this.el.show();
30368             if(this.split){
30369                 this.split.el.show();
30370             }
30371             this.collapsedEl.setLocation(-2000,-2000);
30372             this.collapsedEl.hide();
30373             this.fireEvent("invalidated", this);
30374             this.fireEvent("expanded", this);
30375         }
30376     },
30377
30378     animateExpand : function(){
30379         // overridden
30380     },
30381
30382     initTabs : function()
30383     {
30384         this.bodyEl.setStyle("overflow", "hidden");
30385         var ts = new Roo.TabPanel(
30386                 this.bodyEl.dom,
30387                 {
30388                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30389                     disableTooltips: this.config.disableTabTips,
30390                     toolbar : this.config.toolbar
30391                 }
30392         );
30393         if(this.config.hideTabs){
30394             ts.stripWrap.setDisplayed(false);
30395         }
30396         this.tabs = ts;
30397         ts.resizeTabs = this.config.resizeTabs === true;
30398         ts.minTabWidth = this.config.minTabWidth || 40;
30399         ts.maxTabWidth = this.config.maxTabWidth || 250;
30400         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30401         ts.monitorResize = false;
30402         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30403         ts.bodyEl.addClass('x-layout-tabs-body');
30404         this.panels.each(this.initPanelAsTab, this);
30405     },
30406
30407     initPanelAsTab : function(panel){
30408         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30409                     this.config.closeOnTab && panel.isClosable());
30410         if(panel.tabTip !== undefined){
30411             ti.setTooltip(panel.tabTip);
30412         }
30413         ti.on("activate", function(){
30414               this.setActivePanel(panel);
30415         }, this);
30416         if(this.config.closeOnTab){
30417             ti.on("beforeclose", function(t, e){
30418                 e.cancel = true;
30419                 this.remove(panel);
30420             }, this);
30421         }
30422         return ti;
30423     },
30424
30425     updatePanelTitle : function(panel, title){
30426         if(this.activePanel == panel){
30427             this.updateTitle(title);
30428         }
30429         if(this.tabs){
30430             var ti = this.tabs.getTab(panel.getEl().id);
30431             ti.setText(title);
30432             if(panel.tabTip !== undefined){
30433                 ti.setTooltip(panel.tabTip);
30434             }
30435         }
30436     },
30437
30438     updateTitle : function(title){
30439         if(this.titleTextEl && !this.config.title){
30440             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30441         }
30442     },
30443
30444     setActivePanel : function(panel){
30445         panel = this.getPanel(panel);
30446         if(this.activePanel && this.activePanel != panel){
30447             this.activePanel.setActiveState(false);
30448         }
30449         this.activePanel = panel;
30450         panel.setActiveState(true);
30451         if(this.panelSize){
30452             panel.setSize(this.panelSize.width, this.panelSize.height);
30453         }
30454         if(this.closeBtn){
30455             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30456         }
30457         this.updateTitle(panel.getTitle());
30458         if(this.tabs){
30459             this.fireEvent("invalidated", this);
30460         }
30461         this.fireEvent("panelactivated", this, panel);
30462     },
30463
30464     /**
30465      * Shows the specified panel.
30466      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30467      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30468      */
30469     showPanel : function(panel)
30470     {
30471         panel = this.getPanel(panel);
30472         if(panel){
30473             if(this.tabs){
30474                 var tab = this.tabs.getTab(panel.getEl().id);
30475                 if(tab.isHidden()){
30476                     this.tabs.unhideTab(tab.id);
30477                 }
30478                 tab.activate();
30479             }else{
30480                 this.setActivePanel(panel);
30481             }
30482         }
30483         return panel;
30484     },
30485
30486     /**
30487      * Get the active panel for this region.
30488      * @return {Roo.ContentPanel} The active panel or null
30489      */
30490     getActivePanel : function(){
30491         return this.activePanel;
30492     },
30493
30494     validateVisibility : function(){
30495         if(this.panels.getCount() < 1){
30496             this.updateTitle("&#160;");
30497             this.closeBtn.hide();
30498             this.hide();
30499         }else{
30500             if(!this.isVisible()){
30501                 this.show();
30502             }
30503         }
30504     },
30505
30506     /**
30507      * Adds the passed ContentPanel(s) to this region.
30508      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30509      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30510      */
30511     add : function(panel){
30512         if(arguments.length > 1){
30513             for(var i = 0, len = arguments.length; i < len; i++) {
30514                 this.add(arguments[i]);
30515             }
30516             return null;
30517         }
30518         if(this.hasPanel(panel)){
30519             this.showPanel(panel);
30520             return panel;
30521         }
30522         panel.setRegion(this);
30523         this.panels.add(panel);
30524         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30525             this.bodyEl.dom.appendChild(panel.getEl().dom);
30526             if(panel.background !== true){
30527                 this.setActivePanel(panel);
30528             }
30529             this.fireEvent("paneladded", this, panel);
30530             return panel;
30531         }
30532         if(!this.tabs){
30533             this.initTabs();
30534         }else{
30535             this.initPanelAsTab(panel);
30536         }
30537         if(panel.background !== true){
30538             this.tabs.activate(panel.getEl().id);
30539         }
30540         this.fireEvent("paneladded", this, panel);
30541         return panel;
30542     },
30543
30544     /**
30545      * Hides the tab for the specified panel.
30546      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30547      */
30548     hidePanel : function(panel){
30549         if(this.tabs && (panel = this.getPanel(panel))){
30550             this.tabs.hideTab(panel.getEl().id);
30551         }
30552     },
30553
30554     /**
30555      * Unhides the tab for a previously hidden panel.
30556      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30557      */
30558     unhidePanel : function(panel){
30559         if(this.tabs && (panel = this.getPanel(panel))){
30560             this.tabs.unhideTab(panel.getEl().id);
30561         }
30562     },
30563
30564     clearPanels : function(){
30565         while(this.panels.getCount() > 0){
30566              this.remove(this.panels.first());
30567         }
30568     },
30569
30570     /**
30571      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30572      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30573      * @param {Boolean} preservePanel Overrides the config preservePanel option
30574      * @return {Roo.ContentPanel} The panel that was removed
30575      */
30576     remove : function(panel, preservePanel){
30577         panel = this.getPanel(panel);
30578         if(!panel){
30579             return null;
30580         }
30581         var e = {};
30582         this.fireEvent("beforeremove", this, panel, e);
30583         if(e.cancel === true){
30584             return null;
30585         }
30586         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30587         var panelId = panel.getId();
30588         this.panels.removeKey(panelId);
30589         if(preservePanel){
30590             document.body.appendChild(panel.getEl().dom);
30591         }
30592         if(this.tabs){
30593             this.tabs.removeTab(panel.getEl().id);
30594         }else if (!preservePanel){
30595             this.bodyEl.dom.removeChild(panel.getEl().dom);
30596         }
30597         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30598             var p = this.panels.first();
30599             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30600             tempEl.appendChild(p.getEl().dom);
30601             this.bodyEl.update("");
30602             this.bodyEl.dom.appendChild(p.getEl().dom);
30603             tempEl = null;
30604             this.updateTitle(p.getTitle());
30605             this.tabs = null;
30606             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30607             this.setActivePanel(p);
30608         }
30609         panel.setRegion(null);
30610         if(this.activePanel == panel){
30611             this.activePanel = null;
30612         }
30613         if(this.config.autoDestroy !== false && preservePanel !== true){
30614             try{panel.destroy();}catch(e){}
30615         }
30616         this.fireEvent("panelremoved", this, panel);
30617         return panel;
30618     },
30619
30620     /**
30621      * Returns the TabPanel component used by this region
30622      * @return {Roo.TabPanel}
30623      */
30624     getTabs : function(){
30625         return this.tabs;
30626     },
30627
30628     createTool : function(parentEl, className){
30629         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30630             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30631         btn.addClassOnOver("x-layout-tools-button-over");
30632         return btn;
30633     }
30634 });/*
30635  * Based on:
30636  * Ext JS Library 1.1.1
30637  * Copyright(c) 2006-2007, Ext JS, LLC.
30638  *
30639  * Originally Released Under LGPL - original licence link has changed is not relivant.
30640  *
30641  * Fork - LGPL
30642  * <script type="text/javascript">
30643  */
30644  
30645
30646
30647 /**
30648  * @class Roo.SplitLayoutRegion
30649  * @extends Roo.LayoutRegion
30650  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30651  */
30652 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30653     this.cursor = cursor;
30654     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30655 };
30656
30657 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30658     splitTip : "Drag to resize.",
30659     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30660     useSplitTips : false,
30661
30662     applyConfig : function(config){
30663         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30664         if(config.split){
30665             if(!this.split){
30666                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30667                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30668                 /** The SplitBar for this region 
30669                 * @type Roo.SplitBar */
30670                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30671                 this.split.on("moved", this.onSplitMove, this);
30672                 this.split.useShim = config.useShim === true;
30673                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30674                 if(this.useSplitTips){
30675                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30676                 }
30677                 if(config.collapsible){
30678                     this.split.el.on("dblclick", this.collapse,  this);
30679                 }
30680             }
30681             if(typeof config.minSize != "undefined"){
30682                 this.split.minSize = config.minSize;
30683             }
30684             if(typeof config.maxSize != "undefined"){
30685                 this.split.maxSize = config.maxSize;
30686             }
30687             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30688                 this.hideSplitter();
30689             }
30690         }
30691     },
30692
30693     getHMaxSize : function(){
30694          var cmax = this.config.maxSize || 10000;
30695          var center = this.mgr.getRegion("center");
30696          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30697     },
30698
30699     getVMaxSize : function(){
30700          var cmax = this.config.maxSize || 10000;
30701          var center = this.mgr.getRegion("center");
30702          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30703     },
30704
30705     onSplitMove : function(split, newSize){
30706         this.fireEvent("resized", this, newSize);
30707     },
30708     
30709     /** 
30710      * Returns the {@link Roo.SplitBar} for this region.
30711      * @return {Roo.SplitBar}
30712      */
30713     getSplitBar : function(){
30714         return this.split;
30715     },
30716     
30717     hide : function(){
30718         this.hideSplitter();
30719         Roo.SplitLayoutRegion.superclass.hide.call(this);
30720     },
30721
30722     hideSplitter : function(){
30723         if(this.split){
30724             this.split.el.setLocation(-2000,-2000);
30725             this.split.el.hide();
30726         }
30727     },
30728
30729     show : function(){
30730         if(this.split){
30731             this.split.el.show();
30732         }
30733         Roo.SplitLayoutRegion.superclass.show.call(this);
30734     },
30735     
30736     beforeSlide: function(){
30737         if(Roo.isGecko){// firefox overflow auto bug workaround
30738             this.bodyEl.clip();
30739             if(this.tabs) {
30740                 this.tabs.bodyEl.clip();
30741             }
30742             if(this.activePanel){
30743                 this.activePanel.getEl().clip();
30744                 
30745                 if(this.activePanel.beforeSlide){
30746                     this.activePanel.beforeSlide();
30747                 }
30748             }
30749         }
30750     },
30751     
30752     afterSlide : function(){
30753         if(Roo.isGecko){// firefox overflow auto bug workaround
30754             this.bodyEl.unclip();
30755             if(this.tabs) {
30756                 this.tabs.bodyEl.unclip();
30757             }
30758             if(this.activePanel){
30759                 this.activePanel.getEl().unclip();
30760                 if(this.activePanel.afterSlide){
30761                     this.activePanel.afterSlide();
30762                 }
30763             }
30764         }
30765     },
30766
30767     initAutoHide : function(){
30768         if(this.autoHide !== false){
30769             if(!this.autoHideHd){
30770                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30771                 this.autoHideHd = {
30772                     "mouseout": function(e){
30773                         if(!e.within(this.el, true)){
30774                             st.delay(500);
30775                         }
30776                     },
30777                     "mouseover" : function(e){
30778                         st.cancel();
30779                     },
30780                     scope : this
30781                 };
30782             }
30783             this.el.on(this.autoHideHd);
30784         }
30785     },
30786
30787     clearAutoHide : function(){
30788         if(this.autoHide !== false){
30789             this.el.un("mouseout", this.autoHideHd.mouseout);
30790             this.el.un("mouseover", this.autoHideHd.mouseover);
30791         }
30792     },
30793
30794     clearMonitor : function(){
30795         Roo.get(document).un("click", this.slideInIf, this);
30796     },
30797
30798     // these names are backwards but not changed for compat
30799     slideOut : function(){
30800         if(this.isSlid || this.el.hasActiveFx()){
30801             return;
30802         }
30803         this.isSlid = true;
30804         if(this.collapseBtn){
30805             this.collapseBtn.hide();
30806         }
30807         this.closeBtnState = this.closeBtn.getStyle('display');
30808         this.closeBtn.hide();
30809         if(this.stickBtn){
30810             this.stickBtn.show();
30811         }
30812         this.el.show();
30813         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30814         this.beforeSlide();
30815         this.el.setStyle("z-index", 10001);
30816         this.el.slideIn(this.getSlideAnchor(), {
30817             callback: function(){
30818                 this.afterSlide();
30819                 this.initAutoHide();
30820                 Roo.get(document).on("click", this.slideInIf, this);
30821                 this.fireEvent("slideshow", this);
30822             },
30823             scope: this,
30824             block: true
30825         });
30826     },
30827
30828     afterSlideIn : function(){
30829         this.clearAutoHide();
30830         this.isSlid = false;
30831         this.clearMonitor();
30832         this.el.setStyle("z-index", "");
30833         if(this.collapseBtn){
30834             this.collapseBtn.show();
30835         }
30836         this.closeBtn.setStyle('display', this.closeBtnState);
30837         if(this.stickBtn){
30838             this.stickBtn.hide();
30839         }
30840         this.fireEvent("slidehide", this);
30841     },
30842
30843     slideIn : function(cb){
30844         if(!this.isSlid || this.el.hasActiveFx()){
30845             Roo.callback(cb);
30846             return;
30847         }
30848         this.isSlid = false;
30849         this.beforeSlide();
30850         this.el.slideOut(this.getSlideAnchor(), {
30851             callback: function(){
30852                 this.el.setLeftTop(-10000, -10000);
30853                 this.afterSlide();
30854                 this.afterSlideIn();
30855                 Roo.callback(cb);
30856             },
30857             scope: this,
30858             block: true
30859         });
30860     },
30861     
30862     slideInIf : function(e){
30863         if(!e.within(this.el)){
30864             this.slideIn();
30865         }
30866     },
30867
30868     animateCollapse : function(){
30869         this.beforeSlide();
30870         this.el.setStyle("z-index", 20000);
30871         var anchor = this.getSlideAnchor();
30872         this.el.slideOut(anchor, {
30873             callback : function(){
30874                 this.el.setStyle("z-index", "");
30875                 this.collapsedEl.slideIn(anchor, {duration:.3});
30876                 this.afterSlide();
30877                 this.el.setLocation(-10000,-10000);
30878                 this.el.hide();
30879                 this.fireEvent("collapsed", this);
30880             },
30881             scope: this,
30882             block: true
30883         });
30884     },
30885
30886     animateExpand : function(){
30887         this.beforeSlide();
30888         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30889         this.el.setStyle("z-index", 20000);
30890         this.collapsedEl.hide({
30891             duration:.1
30892         });
30893         this.el.slideIn(this.getSlideAnchor(), {
30894             callback : function(){
30895                 this.el.setStyle("z-index", "");
30896                 this.afterSlide();
30897                 if(this.split){
30898                     this.split.el.show();
30899                 }
30900                 this.fireEvent("invalidated", this);
30901                 this.fireEvent("expanded", this);
30902             },
30903             scope: this,
30904             block: true
30905         });
30906     },
30907
30908     anchors : {
30909         "west" : "left",
30910         "east" : "right",
30911         "north" : "top",
30912         "south" : "bottom"
30913     },
30914
30915     sanchors : {
30916         "west" : "l",
30917         "east" : "r",
30918         "north" : "t",
30919         "south" : "b"
30920     },
30921
30922     canchors : {
30923         "west" : "tl-tr",
30924         "east" : "tr-tl",
30925         "north" : "tl-bl",
30926         "south" : "bl-tl"
30927     },
30928
30929     getAnchor : function(){
30930         return this.anchors[this.position];
30931     },
30932
30933     getCollapseAnchor : function(){
30934         return this.canchors[this.position];
30935     },
30936
30937     getSlideAnchor : function(){
30938         return this.sanchors[this.position];
30939     },
30940
30941     getAlignAdj : function(){
30942         var cm = this.cmargins;
30943         switch(this.position){
30944             case "west":
30945                 return [0, 0];
30946             break;
30947             case "east":
30948                 return [0, 0];
30949             break;
30950             case "north":
30951                 return [0, 0];
30952             break;
30953             case "south":
30954                 return [0, 0];
30955             break;
30956         }
30957     },
30958
30959     getExpandAdj : function(){
30960         var c = this.collapsedEl, cm = this.cmargins;
30961         switch(this.position){
30962             case "west":
30963                 return [-(cm.right+c.getWidth()+cm.left), 0];
30964             break;
30965             case "east":
30966                 return [cm.right+c.getWidth()+cm.left, 0];
30967             break;
30968             case "north":
30969                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30970             break;
30971             case "south":
30972                 return [0, cm.top+cm.bottom+c.getHeight()];
30973             break;
30974         }
30975     }
30976 });/*
30977  * Based on:
30978  * Ext JS Library 1.1.1
30979  * Copyright(c) 2006-2007, Ext JS, LLC.
30980  *
30981  * Originally Released Under LGPL - original licence link has changed is not relivant.
30982  *
30983  * Fork - LGPL
30984  * <script type="text/javascript">
30985  */
30986 /*
30987  * These classes are private internal classes
30988  */
30989 Roo.CenterLayoutRegion = function(mgr, config){
30990     Roo.LayoutRegion.call(this, mgr, config, "center");
30991     this.visible = true;
30992     this.minWidth = config.minWidth || 20;
30993     this.minHeight = config.minHeight || 20;
30994 };
30995
30996 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30997     hide : function(){
30998         // center panel can't be hidden
30999     },
31000     
31001     show : function(){
31002         // center panel can't be hidden
31003     },
31004     
31005     getMinWidth: function(){
31006         return this.minWidth;
31007     },
31008     
31009     getMinHeight: function(){
31010         return this.minHeight;
31011     }
31012 });
31013
31014
31015 Roo.NorthLayoutRegion = function(mgr, config){
31016     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31017     if(this.split){
31018         this.split.placement = Roo.SplitBar.TOP;
31019         this.split.orientation = Roo.SplitBar.VERTICAL;
31020         this.split.el.addClass("x-layout-split-v");
31021     }
31022     var size = config.initialSize || config.height;
31023     if(typeof size != "undefined"){
31024         this.el.setHeight(size);
31025     }
31026 };
31027 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31028     orientation: Roo.SplitBar.VERTICAL,
31029     getBox : function(){
31030         if(this.collapsed){
31031             return this.collapsedEl.getBox();
31032         }
31033         var box = this.el.getBox();
31034         if(this.split){
31035             box.height += this.split.el.getHeight();
31036         }
31037         return box;
31038     },
31039     
31040     updateBox : function(box){
31041         if(this.split && !this.collapsed){
31042             box.height -= this.split.el.getHeight();
31043             this.split.el.setLeft(box.x);
31044             this.split.el.setTop(box.y+box.height);
31045             this.split.el.setWidth(box.width);
31046         }
31047         if(this.collapsed){
31048             this.updateBody(box.width, null);
31049         }
31050         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31051     }
31052 });
31053
31054 Roo.SouthLayoutRegion = function(mgr, config){
31055     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31056     if(this.split){
31057         this.split.placement = Roo.SplitBar.BOTTOM;
31058         this.split.orientation = Roo.SplitBar.VERTICAL;
31059         this.split.el.addClass("x-layout-split-v");
31060     }
31061     var size = config.initialSize || config.height;
31062     if(typeof size != "undefined"){
31063         this.el.setHeight(size);
31064     }
31065 };
31066 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31067     orientation: Roo.SplitBar.VERTICAL,
31068     getBox : function(){
31069         if(this.collapsed){
31070             return this.collapsedEl.getBox();
31071         }
31072         var box = this.el.getBox();
31073         if(this.split){
31074             var sh = this.split.el.getHeight();
31075             box.height += sh;
31076             box.y -= sh;
31077         }
31078         return box;
31079     },
31080     
31081     updateBox : function(box){
31082         if(this.split && !this.collapsed){
31083             var sh = this.split.el.getHeight();
31084             box.height -= sh;
31085             box.y += sh;
31086             this.split.el.setLeft(box.x);
31087             this.split.el.setTop(box.y-sh);
31088             this.split.el.setWidth(box.width);
31089         }
31090         if(this.collapsed){
31091             this.updateBody(box.width, null);
31092         }
31093         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31094     }
31095 });
31096
31097 Roo.EastLayoutRegion = function(mgr, config){
31098     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31099     if(this.split){
31100         this.split.placement = Roo.SplitBar.RIGHT;
31101         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31102         this.split.el.addClass("x-layout-split-h");
31103     }
31104     var size = config.initialSize || config.width;
31105     if(typeof size != "undefined"){
31106         this.el.setWidth(size);
31107     }
31108 };
31109 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31110     orientation: Roo.SplitBar.HORIZONTAL,
31111     getBox : function(){
31112         if(this.collapsed){
31113             return this.collapsedEl.getBox();
31114         }
31115         var box = this.el.getBox();
31116         if(this.split){
31117             var sw = this.split.el.getWidth();
31118             box.width += sw;
31119             box.x -= sw;
31120         }
31121         return box;
31122     },
31123
31124     updateBox : function(box){
31125         if(this.split && !this.collapsed){
31126             var sw = this.split.el.getWidth();
31127             box.width -= sw;
31128             this.split.el.setLeft(box.x);
31129             this.split.el.setTop(box.y);
31130             this.split.el.setHeight(box.height);
31131             box.x += sw;
31132         }
31133         if(this.collapsed){
31134             this.updateBody(null, box.height);
31135         }
31136         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31137     }
31138 });
31139
31140 Roo.WestLayoutRegion = function(mgr, config){
31141     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31142     if(this.split){
31143         this.split.placement = Roo.SplitBar.LEFT;
31144         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31145         this.split.el.addClass("x-layout-split-h");
31146     }
31147     var size = config.initialSize || config.width;
31148     if(typeof size != "undefined"){
31149         this.el.setWidth(size);
31150     }
31151 };
31152 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31153     orientation: Roo.SplitBar.HORIZONTAL,
31154     getBox : function(){
31155         if(this.collapsed){
31156             return this.collapsedEl.getBox();
31157         }
31158         var box = this.el.getBox();
31159         if(this.split){
31160             box.width += this.split.el.getWidth();
31161         }
31162         return box;
31163     },
31164     
31165     updateBox : function(box){
31166         if(this.split && !this.collapsed){
31167             var sw = this.split.el.getWidth();
31168             box.width -= sw;
31169             this.split.el.setLeft(box.x+box.width);
31170             this.split.el.setTop(box.y);
31171             this.split.el.setHeight(box.height);
31172         }
31173         if(this.collapsed){
31174             this.updateBody(null, box.height);
31175         }
31176         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31177     }
31178 });
31179 /*
31180  * Based on:
31181  * Ext JS Library 1.1.1
31182  * Copyright(c) 2006-2007, Ext JS, LLC.
31183  *
31184  * Originally Released Under LGPL - original licence link has changed is not relivant.
31185  *
31186  * Fork - LGPL
31187  * <script type="text/javascript">
31188  */
31189  
31190  
31191 /*
31192  * Private internal class for reading and applying state
31193  */
31194 Roo.LayoutStateManager = function(layout){
31195      // default empty state
31196      this.state = {
31197         north: {},
31198         south: {},
31199         east: {},
31200         west: {}       
31201     };
31202 };
31203
31204 Roo.LayoutStateManager.prototype = {
31205     init : function(layout, provider){
31206         this.provider = provider;
31207         var state = provider.get(layout.id+"-layout-state");
31208         if(state){
31209             var wasUpdating = layout.isUpdating();
31210             if(!wasUpdating){
31211                 layout.beginUpdate();
31212             }
31213             for(var key in state){
31214                 if(typeof state[key] != "function"){
31215                     var rstate = state[key];
31216                     var r = layout.getRegion(key);
31217                     if(r && rstate){
31218                         if(rstate.size){
31219                             r.resizeTo(rstate.size);
31220                         }
31221                         if(rstate.collapsed == true){
31222                             r.collapse(true);
31223                         }else{
31224                             r.expand(null, true);
31225                         }
31226                     }
31227                 }
31228             }
31229             if(!wasUpdating){
31230                 layout.endUpdate();
31231             }
31232             this.state = state; 
31233         }
31234         this.layout = layout;
31235         layout.on("regionresized", this.onRegionResized, this);
31236         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31237         layout.on("regionexpanded", this.onRegionExpanded, this);
31238     },
31239     
31240     storeState : function(){
31241         this.provider.set(this.layout.id+"-layout-state", this.state);
31242     },
31243     
31244     onRegionResized : function(region, newSize){
31245         this.state[region.getPosition()].size = newSize;
31246         this.storeState();
31247     },
31248     
31249     onRegionCollapsed : function(region){
31250         this.state[region.getPosition()].collapsed = true;
31251         this.storeState();
31252     },
31253     
31254     onRegionExpanded : function(region){
31255         this.state[region.getPosition()].collapsed = false;
31256         this.storeState();
31257     }
31258 };/*
31259  * Based on:
31260  * Ext JS Library 1.1.1
31261  * Copyright(c) 2006-2007, Ext JS, LLC.
31262  *
31263  * Originally Released Under LGPL - original licence link has changed is not relivant.
31264  *
31265  * Fork - LGPL
31266  * <script type="text/javascript">
31267  */
31268 /**
31269  * @class Roo.ContentPanel
31270  * @extends Roo.util.Observable
31271  * @children Roo.form.Form Roo.JsonView Roo.View
31272  * A basic ContentPanel element.
31273  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31274  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31275  * @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
31276  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31277  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31278  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31279  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
31280  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31281  * @cfg {String} title          The title for this panel
31282  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31283  * @cfg {String} url            Calls {@link #setUrl} with this value
31284  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31285  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31286  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31287  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31288  * @cfg {String}    style  Extra style to add to the content panel
31289  * @cfg {Roo.menu.Menu} menu  popup menu
31290
31291  * @constructor
31292  * Create a new ContentPanel.
31293  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31294  * @param {String/Object} config A string to set only the title or a config object
31295  * @param {String} content (optional) Set the HTML content for this panel
31296  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31297  */
31298 Roo.ContentPanel = function(el, config, content){
31299     
31300      
31301     /*
31302     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31303         config = el;
31304         el = Roo.id();
31305     }
31306     if (config && config.parentLayout) { 
31307         el = config.parentLayout.el.createChild(); 
31308     }
31309     */
31310     if(el.autoCreate){ // xtype is available if this is called from factory
31311         config = el;
31312         el = Roo.id();
31313     }
31314     this.el = Roo.get(el);
31315     if(!this.el && config && config.autoCreate){
31316         if(typeof config.autoCreate == "object"){
31317             if(!config.autoCreate.id){
31318                 config.autoCreate.id = config.id||el;
31319             }
31320             this.el = Roo.DomHelper.append(document.body,
31321                         config.autoCreate, true);
31322         }else{
31323             this.el = Roo.DomHelper.append(document.body,
31324                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31325         }
31326     }
31327     
31328     
31329     this.closable = false;
31330     this.loaded = false;
31331     this.active = false;
31332     if(typeof config == "string"){
31333         this.title = config;
31334     }else{
31335         Roo.apply(this, config);
31336     }
31337     
31338     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31339         this.wrapEl = this.el.wrap();
31340         this.toolbar.container = this.el.insertSibling(false, 'before');
31341         this.toolbar = new Roo.Toolbar(this.toolbar);
31342     }
31343     
31344     // xtype created footer. - not sure if will work as we normally have to render first..
31345     if (this.footer && !this.footer.el && this.footer.xtype) {
31346         if (!this.wrapEl) {
31347             this.wrapEl = this.el.wrap();
31348         }
31349     
31350         this.footer.container = this.wrapEl.createChild();
31351          
31352         this.footer = Roo.factory(this.footer, Roo);
31353         
31354     }
31355     
31356     if(this.resizeEl){
31357         this.resizeEl = Roo.get(this.resizeEl, true);
31358     }else{
31359         this.resizeEl = this.el;
31360     }
31361     // handle view.xtype
31362     
31363  
31364     
31365     
31366     this.addEvents({
31367         /**
31368          * @event activate
31369          * Fires when this panel is activated. 
31370          * @param {Roo.ContentPanel} this
31371          */
31372         "activate" : true,
31373         /**
31374          * @event deactivate
31375          * Fires when this panel is activated. 
31376          * @param {Roo.ContentPanel} this
31377          */
31378         "deactivate" : true,
31379
31380         /**
31381          * @event resize
31382          * Fires when this panel is resized if fitToFrame is true.
31383          * @param {Roo.ContentPanel} this
31384          * @param {Number} width The width after any component adjustments
31385          * @param {Number} height The height after any component adjustments
31386          */
31387         "resize" : true,
31388         
31389          /**
31390          * @event render
31391          * Fires when this tab is created
31392          * @param {Roo.ContentPanel} this
31393          */
31394         "render" : true
31395          
31396         
31397     });
31398     
31399
31400     
31401     
31402     if(this.autoScroll){
31403         this.resizeEl.setStyle("overflow", "auto");
31404     } else {
31405         // fix randome scrolling
31406         this.el.on('scroll', function() {
31407             Roo.log('fix random scolling');
31408             this.scrollTo('top',0); 
31409         });
31410     }
31411     content = content || this.content;
31412     if(content){
31413         this.setContent(content);
31414     }
31415     if(config && config.url){
31416         this.setUrl(this.url, this.params, this.loadOnce);
31417     }
31418     
31419     
31420     
31421     Roo.ContentPanel.superclass.constructor.call(this);
31422     
31423     if (this.view && typeof(this.view.xtype) != 'undefined') {
31424         this.view.el = this.el.appendChild(document.createElement("div"));
31425         this.view = Roo.factory(this.view); 
31426         this.view.render  &&  this.view.render(false, '');  
31427     }
31428     
31429     
31430     this.fireEvent('render', this);
31431 };
31432
31433 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31434     tabTip:'',
31435     setRegion : function(region){
31436         this.region = region;
31437         if(region){
31438            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31439         }else{
31440            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31441         } 
31442     },
31443     
31444     /**
31445      * Returns the toolbar for this Panel if one was configured. 
31446      * @return {Roo.Toolbar} 
31447      */
31448     getToolbar : function(){
31449         return this.toolbar;
31450     },
31451     
31452     setActiveState : function(active){
31453         this.active = active;
31454         if(!active){
31455             this.fireEvent("deactivate", this);
31456         }else{
31457             this.fireEvent("activate", this);
31458         }
31459     },
31460     /**
31461      * Updates this panel's element
31462      * @param {String} content The new content
31463      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31464     */
31465     setContent : function(content, loadScripts){
31466         this.el.update(content, loadScripts);
31467     },
31468
31469     ignoreResize : function(w, h){
31470         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31471             return true;
31472         }else{
31473             this.lastSize = {width: w, height: h};
31474             return false;
31475         }
31476     },
31477     /**
31478      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31479      * @return {Roo.UpdateManager} The UpdateManager
31480      */
31481     getUpdateManager : function(){
31482         return this.el.getUpdateManager();
31483     },
31484      /**
31485      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31486      * @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:
31487 <pre><code>
31488 panel.load({
31489     url: "your-url.php",
31490     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31491     callback: yourFunction,
31492     scope: yourObject, //(optional scope)
31493     discardUrl: false,
31494     nocache: false,
31495     text: "Loading...",
31496     timeout: 30,
31497     scripts: false
31498 });
31499 </code></pre>
31500      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31501      * 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.
31502      * @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}
31503      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31504      * @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.
31505      * @return {Roo.ContentPanel} this
31506      */
31507     load : function(){
31508         var um = this.el.getUpdateManager();
31509         um.update.apply(um, arguments);
31510         return this;
31511     },
31512
31513
31514     /**
31515      * 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.
31516      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31517      * @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)
31518      * @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)
31519      * @return {Roo.UpdateManager} The UpdateManager
31520      */
31521     setUrl : function(url, params, loadOnce){
31522         if(this.refreshDelegate){
31523             this.removeListener("activate", this.refreshDelegate);
31524         }
31525         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31526         this.on("activate", this.refreshDelegate);
31527         return this.el.getUpdateManager();
31528     },
31529     
31530     _handleRefresh : function(url, params, loadOnce){
31531         if(!loadOnce || !this.loaded){
31532             var updater = this.el.getUpdateManager();
31533             updater.update(url, params, this._setLoaded.createDelegate(this));
31534         }
31535     },
31536     
31537     _setLoaded : function(){
31538         this.loaded = true;
31539     }, 
31540     
31541     /**
31542      * Returns this panel's id
31543      * @return {String} 
31544      */
31545     getId : function(){
31546         return this.el.id;
31547     },
31548     
31549     /** 
31550      * Returns this panel's element - used by regiosn to add.
31551      * @return {Roo.Element} 
31552      */
31553     getEl : function(){
31554         return this.wrapEl || this.el;
31555     },
31556     
31557     adjustForComponents : function(width, height)
31558     {
31559         //Roo.log('adjustForComponents ');
31560         if(this.resizeEl != this.el){
31561             width -= this.el.getFrameWidth('lr');
31562             height -= this.el.getFrameWidth('tb');
31563         }
31564         if(this.toolbar){
31565             var te = this.toolbar.getEl();
31566             height -= te.getHeight();
31567             te.setWidth(width);
31568         }
31569         if(this.footer){
31570             var te = this.footer.getEl();
31571             //Roo.log("footer:" + te.getHeight());
31572             
31573             height -= te.getHeight();
31574             te.setWidth(width);
31575         }
31576         
31577         
31578         if(this.adjustments){
31579             width += this.adjustments[0];
31580             height += this.adjustments[1];
31581         }
31582         return {"width": width, "height": height};
31583     },
31584     
31585     setSize : function(width, height){
31586         if(this.fitToFrame && !this.ignoreResize(width, height)){
31587             if(this.fitContainer && this.resizeEl != this.el){
31588                 this.el.setSize(width, height);
31589             }
31590             var size = this.adjustForComponents(width, height);
31591             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31592             this.fireEvent('resize', this, size.width, size.height);
31593         }
31594     },
31595     
31596     /**
31597      * Returns this panel's title
31598      * @return {String} 
31599      */
31600     getTitle : function(){
31601         return this.title;
31602     },
31603     
31604     /**
31605      * Set this panel's title
31606      * @param {String} title
31607      */
31608     setTitle : function(title){
31609         this.title = title;
31610         if(this.region){
31611             this.region.updatePanelTitle(this, title);
31612         }
31613     },
31614     
31615     /**
31616      * Returns true is this panel was configured to be closable
31617      * @return {Boolean} 
31618      */
31619     isClosable : function(){
31620         return this.closable;
31621     },
31622     
31623     beforeSlide : function(){
31624         this.el.clip();
31625         this.resizeEl.clip();
31626     },
31627     
31628     afterSlide : function(){
31629         this.el.unclip();
31630         this.resizeEl.unclip();
31631     },
31632     
31633     /**
31634      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31635      *   Will fail silently if the {@link #setUrl} method has not been called.
31636      *   This does not activate the panel, just updates its content.
31637      */
31638     refresh : function(){
31639         if(this.refreshDelegate){
31640            this.loaded = false;
31641            this.refreshDelegate();
31642         }
31643     },
31644     
31645     /**
31646      * Destroys this panel
31647      */
31648     destroy : function(){
31649         this.el.removeAllListeners();
31650         var tempEl = document.createElement("span");
31651         tempEl.appendChild(this.el.dom);
31652         tempEl.innerHTML = "";
31653         this.el.remove();
31654         this.el = null;
31655     },
31656     
31657     /**
31658      * form - if the content panel contains a form - this is a reference to it.
31659      * @type {Roo.form.Form}
31660      */
31661     form : false,
31662     /**
31663      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31664      *    This contains a reference to it.
31665      * @type {Roo.View}
31666      */
31667     view : false,
31668     
31669       /**
31670      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31671      * <pre><code>
31672
31673 layout.addxtype({
31674        xtype : 'Form',
31675        items: [ .... ]
31676    }
31677 );
31678
31679 </code></pre>
31680      * @param {Object} cfg Xtype definition of item to add.
31681      */
31682     
31683     addxtype : function(cfg) {
31684         // add form..
31685         if (cfg.xtype.match(/^Form$/)) {
31686             
31687             var el;
31688             //if (this.footer) {
31689             //    el = this.footer.container.insertSibling(false, 'before');
31690             //} else {
31691                 el = this.el.createChild();
31692             //}
31693
31694             this.form = new  Roo.form.Form(cfg);
31695             
31696             
31697             if ( this.form.allItems.length) {
31698                 this.form.render(el.dom);
31699             }
31700             return this.form;
31701         }
31702         // should only have one of theses..
31703         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31704             // views.. should not be just added - used named prop 'view''
31705             
31706             cfg.el = this.el.appendChild(document.createElement("div"));
31707             // factory?
31708             
31709             var ret = new Roo.factory(cfg);
31710              
31711              ret.render && ret.render(false, ''); // render blank..
31712             this.view = ret;
31713             return ret;
31714         }
31715         return false;
31716     }
31717 });
31718
31719 /**
31720  * @class Roo.GridPanel
31721  * @extends Roo.ContentPanel
31722  * @constructor
31723  * Create a new GridPanel.
31724  * @param {Roo.grid.Grid} grid The grid for this panel
31725  * @param {String/Object} config A string to set only the panel's title, or a config object
31726  */
31727 Roo.GridPanel = function(grid, config){
31728     
31729   
31730     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31731         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31732         
31733     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31734     
31735     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31736     
31737     if(this.toolbar){
31738         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31739     }
31740     // xtype created footer. - not sure if will work as we normally have to render first..
31741     if (this.footer && !this.footer.el && this.footer.xtype) {
31742         
31743         this.footer.container = this.grid.getView().getFooterPanel(true);
31744         this.footer.dataSource = this.grid.dataSource;
31745         this.footer = Roo.factory(this.footer, Roo);
31746         
31747     }
31748     
31749     grid.monitorWindowResize = false; // turn off autosizing
31750     grid.autoHeight = false;
31751     grid.autoWidth = false;
31752     this.grid = grid;
31753     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31754 };
31755
31756 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31757     getId : function(){
31758         return this.grid.id;
31759     },
31760     
31761     /**
31762      * Returns the grid for this panel
31763      * @return {Roo.grid.Grid} 
31764      */
31765     getGrid : function(){
31766         return this.grid;    
31767     },
31768     
31769     setSize : function(width, height){
31770         if(!this.ignoreResize(width, height)){
31771             var grid = this.grid;
31772             var size = this.adjustForComponents(width, height);
31773             grid.getGridEl().setSize(size.width, size.height);
31774             grid.autoSize();
31775         }
31776     },
31777     
31778     beforeSlide : function(){
31779         this.grid.getView().scroller.clip();
31780     },
31781     
31782     afterSlide : function(){
31783         this.grid.getView().scroller.unclip();
31784     },
31785     
31786     destroy : function(){
31787         this.grid.destroy();
31788         delete this.grid;
31789         Roo.GridPanel.superclass.destroy.call(this); 
31790     }
31791 });
31792
31793
31794 /**
31795  * @class Roo.NestedLayoutPanel
31796  * @extends Roo.ContentPanel
31797  * @constructor
31798  * Create a new NestedLayoutPanel.
31799  * 
31800  * 
31801  * @param {Roo.BorderLayout} layout [required] The layout for this panel
31802  * @param {String/Object} config A string to set only the title or a config object
31803  */
31804 Roo.NestedLayoutPanel = function(layout, config)
31805 {
31806     // construct with only one argument..
31807     /* FIXME - implement nicer consturctors
31808     if (layout.layout) {
31809         config = layout;
31810         layout = config.layout;
31811         delete config.layout;
31812     }
31813     if (layout.xtype && !layout.getEl) {
31814         // then layout needs constructing..
31815         layout = Roo.factory(layout, Roo);
31816     }
31817     */
31818     
31819     
31820     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31821     
31822     layout.monitorWindowResize = false; // turn off autosizing
31823     this.layout = layout;
31824     this.layout.getEl().addClass("x-layout-nested-layout");
31825     
31826     
31827     
31828     
31829 };
31830
31831 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31832
31833     setSize : function(width, height){
31834         if(!this.ignoreResize(width, height)){
31835             var size = this.adjustForComponents(width, height);
31836             var el = this.layout.getEl();
31837             el.setSize(size.width, size.height);
31838             var touch = el.dom.offsetWidth;
31839             this.layout.layout();
31840             // ie requires a double layout on the first pass
31841             if(Roo.isIE && !this.initialized){
31842                 this.initialized = true;
31843                 this.layout.layout();
31844             }
31845         }
31846     },
31847     
31848     // activate all subpanels if not currently active..
31849     
31850     setActiveState : function(active){
31851         this.active = active;
31852         if(!active){
31853             this.fireEvent("deactivate", this);
31854             return;
31855         }
31856         
31857         this.fireEvent("activate", this);
31858         // not sure if this should happen before or after..
31859         if (!this.layout) {
31860             return; // should not happen..
31861         }
31862         var reg = false;
31863         for (var r in this.layout.regions) {
31864             reg = this.layout.getRegion(r);
31865             if (reg.getActivePanel()) {
31866                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31867                 reg.setActivePanel(reg.getActivePanel());
31868                 continue;
31869             }
31870             if (!reg.panels.length) {
31871                 continue;
31872             }
31873             reg.showPanel(reg.getPanel(0));
31874         }
31875         
31876         
31877         
31878         
31879     },
31880     
31881     /**
31882      * Returns the nested BorderLayout for this panel
31883      * @return {Roo.BorderLayout} 
31884      */
31885     getLayout : function(){
31886         return this.layout;
31887     },
31888     
31889      /**
31890      * Adds a xtype elements to the layout of the nested panel
31891      * <pre><code>
31892
31893 panel.addxtype({
31894        xtype : 'ContentPanel',
31895        region: 'west',
31896        items: [ .... ]
31897    }
31898 );
31899
31900 panel.addxtype({
31901         xtype : 'NestedLayoutPanel',
31902         region: 'west',
31903         layout: {
31904            center: { },
31905            west: { }   
31906         },
31907         items : [ ... list of content panels or nested layout panels.. ]
31908    }
31909 );
31910 </code></pre>
31911      * @param {Object} cfg Xtype definition of item to add.
31912      */
31913     addxtype : function(cfg) {
31914         return this.layout.addxtype(cfg);
31915     
31916     }
31917 });
31918
31919 Roo.ScrollPanel = function(el, config, content){
31920     config = config || {};
31921     config.fitToFrame = true;
31922     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31923     
31924     this.el.dom.style.overflow = "hidden";
31925     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31926     this.el.removeClass("x-layout-inactive-content");
31927     this.el.on("mousewheel", this.onWheel, this);
31928
31929     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31930     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31931     up.unselectable(); down.unselectable();
31932     up.on("click", this.scrollUp, this);
31933     down.on("click", this.scrollDown, this);
31934     up.addClassOnOver("x-scroller-btn-over");
31935     down.addClassOnOver("x-scroller-btn-over");
31936     up.addClassOnClick("x-scroller-btn-click");
31937     down.addClassOnClick("x-scroller-btn-click");
31938     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31939
31940     this.resizeEl = this.el;
31941     this.el = wrap; this.up = up; this.down = down;
31942 };
31943
31944 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31945     increment : 100,
31946     wheelIncrement : 5,
31947     scrollUp : function(){
31948         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31949     },
31950
31951     scrollDown : function(){
31952         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31953     },
31954
31955     afterScroll : function(){
31956         var el = this.resizeEl;
31957         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31958         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31959         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31960     },
31961
31962     setSize : function(){
31963         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31964         this.afterScroll();
31965     },
31966
31967     onWheel : function(e){
31968         var d = e.getWheelDelta();
31969         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31970         this.afterScroll();
31971         e.stopEvent();
31972     },
31973
31974     setContent : function(content, loadScripts){
31975         this.resizeEl.update(content, loadScripts);
31976     }
31977
31978 });
31979
31980
31981
31982 /**
31983  * @class Roo.TreePanel
31984  * @extends Roo.ContentPanel
31985  * Treepanel component
31986  * 
31987  * @constructor
31988  * Create a new TreePanel. - defaults to fit/scoll contents.
31989  * @param {String/Object} config A string to set only the panel's title, or a config object
31990  */
31991 Roo.TreePanel = function(config){
31992     var el = config.el;
31993     var tree = config.tree;
31994     delete config.tree; 
31995     delete config.el; // hopefull!
31996     
31997     // wrapper for IE7 strict & safari scroll issue
31998     
31999     var treeEl = el.createChild();
32000     config.resizeEl = treeEl;
32001     
32002     
32003     
32004     Roo.TreePanel.superclass.constructor.call(this, el, config);
32005  
32006  
32007     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32008     //console.log(tree);
32009     this.on('activate', function()
32010     {
32011         if (this.tree.rendered) {
32012             return;
32013         }
32014         //console.log('render tree');
32015         this.tree.render();
32016     });
32017     // this should not be needed.. - it's actually the 'el' that resizes?
32018     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32019     
32020     //this.on('resize',  function (cp, w, h) {
32021     //        this.tree.innerCt.setWidth(w);
32022     //        this.tree.innerCt.setHeight(h);
32023     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32024     //});
32025
32026         
32027     
32028 };
32029
32030 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32031     fitToFrame : true,
32032     autoScroll : true,
32033     /*
32034      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
32035      */
32036     tree : false
32037
32038 });
32039
32040
32041
32042
32043
32044
32045
32046
32047
32048
32049
32050 /*
32051  * Based on:
32052  * Ext JS Library 1.1.1
32053  * Copyright(c) 2006-2007, Ext JS, LLC.
32054  *
32055  * Originally Released Under LGPL - original licence link has changed is not relivant.
32056  *
32057  * Fork - LGPL
32058  * <script type="text/javascript">
32059  */
32060  
32061
32062 /**
32063  * @class Roo.ReaderLayout
32064  * @extends Roo.BorderLayout
32065  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32066  * center region containing two nested regions (a top one for a list view and one for item preview below),
32067  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32068  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32069  * expedites the setup of the overall layout and regions for this common application style.
32070  * Example:
32071  <pre><code>
32072 var reader = new Roo.ReaderLayout();
32073 var CP = Roo.ContentPanel;  // shortcut for adding
32074
32075 reader.beginUpdate();
32076 reader.add("north", new CP("north", "North"));
32077 reader.add("west", new CP("west", {title: "West"}));
32078 reader.add("east", new CP("east", {title: "East"}));
32079
32080 reader.regions.listView.add(new CP("listView", "List"));
32081 reader.regions.preview.add(new CP("preview", "Preview"));
32082 reader.endUpdate();
32083 </code></pre>
32084 * @constructor
32085 * Create a new ReaderLayout
32086 * @param {Object} config Configuration options
32087 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32088 * document.body if omitted)
32089 */
32090 Roo.ReaderLayout = function(config, renderTo){
32091     var c = config || {size:{}};
32092     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32093         north: c.north !== false ? Roo.apply({
32094             split:false,
32095             initialSize: 32,
32096             titlebar: false
32097         }, c.north) : false,
32098         west: c.west !== false ? Roo.apply({
32099             split:true,
32100             initialSize: 200,
32101             minSize: 175,
32102             maxSize: 400,
32103             titlebar: true,
32104             collapsible: true,
32105             animate: true,
32106             margins:{left:5,right:0,bottom:5,top:5},
32107             cmargins:{left:5,right:5,bottom:5,top:5}
32108         }, c.west) : false,
32109         east: c.east !== false ? Roo.apply({
32110             split:true,
32111             initialSize: 200,
32112             minSize: 175,
32113             maxSize: 400,
32114             titlebar: true,
32115             collapsible: true,
32116             animate: true,
32117             margins:{left:0,right:5,bottom:5,top:5},
32118             cmargins:{left:5,right:5,bottom:5,top:5}
32119         }, c.east) : false,
32120         center: Roo.apply({
32121             tabPosition: 'top',
32122             autoScroll:false,
32123             closeOnTab: true,
32124             titlebar:false,
32125             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32126         }, c.center)
32127     });
32128
32129     this.el.addClass('x-reader');
32130
32131     this.beginUpdate();
32132
32133     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32134         south: c.preview !== false ? Roo.apply({
32135             split:true,
32136             initialSize: 200,
32137             minSize: 100,
32138             autoScroll:true,
32139             collapsible:true,
32140             titlebar: true,
32141             cmargins:{top:5,left:0, right:0, bottom:0}
32142         }, c.preview) : false,
32143         center: Roo.apply({
32144             autoScroll:false,
32145             titlebar:false,
32146             minHeight:200
32147         }, c.listView)
32148     });
32149     this.add('center', new Roo.NestedLayoutPanel(inner,
32150             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32151
32152     this.endUpdate();
32153
32154     this.regions.preview = inner.getRegion('south');
32155     this.regions.listView = inner.getRegion('center');
32156 };
32157
32158 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32159  * Based on:
32160  * Ext JS Library 1.1.1
32161  * Copyright(c) 2006-2007, Ext JS, LLC.
32162  *
32163  * Originally Released Under LGPL - original licence link has changed is not relivant.
32164  *
32165  * Fork - LGPL
32166  * <script type="text/javascript">
32167  */
32168  
32169 /**
32170  * @class Roo.grid.Grid
32171  * @extends Roo.util.Observable
32172  * This class represents the primary interface of a component based grid control.
32173  * <br><br>Usage:<pre><code>
32174  var grid = new Roo.grid.Grid("my-container-id", {
32175      ds: myDataStore,
32176      cm: myColModel,
32177      selModel: mySelectionModel,
32178      autoSizeColumns: true,
32179      monitorWindowResize: false,
32180      trackMouseOver: true
32181  });
32182  // set any options
32183  grid.render();
32184  * </code></pre>
32185  * <b>Common Problems:</b><br/>
32186  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32187  * element will correct this<br/>
32188  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32189  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32190  * are unpredictable.<br/>
32191  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32192  * grid to calculate dimensions/offsets.<br/>
32193   * @constructor
32194  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32195  * The container MUST have some type of size defined for the grid to fill. The container will be
32196  * automatically set to position relative if it isn't already.
32197  * @param {Object} config A config object that sets properties on this grid.
32198  */
32199 Roo.grid.Grid = function(container, config){
32200         // initialize the container
32201         this.container = Roo.get(container);
32202         this.container.update("");
32203         this.container.setStyle("overflow", "hidden");
32204     this.container.addClass('x-grid-container');
32205
32206     this.id = this.container.id;
32207
32208     Roo.apply(this, config);
32209     // check and correct shorthanded configs
32210     if(this.ds){
32211         this.dataSource = this.ds;
32212         delete this.ds;
32213     }
32214     if(this.cm){
32215         this.colModel = this.cm;
32216         delete this.cm;
32217     }
32218     if(this.sm){
32219         this.selModel = this.sm;
32220         delete this.sm;
32221     }
32222
32223     if (this.selModel) {
32224         this.selModel = Roo.factory(this.selModel, Roo.grid);
32225         this.sm = this.selModel;
32226         this.sm.xmodule = this.xmodule || false;
32227     }
32228     if (typeof(this.colModel.config) == 'undefined') {
32229         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32230         this.cm = this.colModel;
32231         this.cm.xmodule = this.xmodule || false;
32232     }
32233     if (this.dataSource) {
32234         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32235         this.ds = this.dataSource;
32236         this.ds.xmodule = this.xmodule || false;
32237          
32238     }
32239     
32240     
32241     
32242     if(this.width){
32243         this.container.setWidth(this.width);
32244     }
32245
32246     if(this.height){
32247         this.container.setHeight(this.height);
32248     }
32249     /** @private */
32250         this.addEvents({
32251         // raw events
32252         /**
32253          * @event click
32254          * The raw click event for the entire grid.
32255          * @param {Roo.EventObject} e
32256          */
32257         "click" : true,
32258         /**
32259          * @event dblclick
32260          * The raw dblclick event for the entire grid.
32261          * @param {Roo.EventObject} e
32262          */
32263         "dblclick" : true,
32264         /**
32265          * @event contextmenu
32266          * The raw contextmenu event for the entire grid.
32267          * @param {Roo.EventObject} e
32268          */
32269         "contextmenu" : true,
32270         /**
32271          * @event mousedown
32272          * The raw mousedown event for the entire grid.
32273          * @param {Roo.EventObject} e
32274          */
32275         "mousedown" : true,
32276         /**
32277          * @event mouseup
32278          * The raw mouseup event for the entire grid.
32279          * @param {Roo.EventObject} e
32280          */
32281         "mouseup" : true,
32282         /**
32283          * @event mouseover
32284          * The raw mouseover event for the entire grid.
32285          * @param {Roo.EventObject} e
32286          */
32287         "mouseover" : true,
32288         /**
32289          * @event mouseout
32290          * The raw mouseout event for the entire grid.
32291          * @param {Roo.EventObject} e
32292          */
32293         "mouseout" : true,
32294         /**
32295          * @event keypress
32296          * The raw keypress event for the entire grid.
32297          * @param {Roo.EventObject} e
32298          */
32299         "keypress" : true,
32300         /**
32301          * @event keydown
32302          * The raw keydown event for the entire grid.
32303          * @param {Roo.EventObject} e
32304          */
32305         "keydown" : true,
32306
32307         // custom events
32308
32309         /**
32310          * @event cellclick
32311          * Fires when a cell is clicked
32312          * @param {Grid} this
32313          * @param {Number} rowIndex
32314          * @param {Number} columnIndex
32315          * @param {Roo.EventObject} e
32316          */
32317         "cellclick" : true,
32318         /**
32319          * @event celldblclick
32320          * Fires when a cell is double clicked
32321          * @param {Grid} this
32322          * @param {Number} rowIndex
32323          * @param {Number} columnIndex
32324          * @param {Roo.EventObject} e
32325          */
32326         "celldblclick" : true,
32327         /**
32328          * @event rowclick
32329          * Fires when a row is clicked
32330          * @param {Grid} this
32331          * @param {Number} rowIndex
32332          * @param {Roo.EventObject} e
32333          */
32334         "rowclick" : true,
32335         /**
32336          * @event rowdblclick
32337          * Fires when a row is double clicked
32338          * @param {Grid} this
32339          * @param {Number} rowIndex
32340          * @param {Roo.EventObject} e
32341          */
32342         "rowdblclick" : true,
32343         /**
32344          * @event headerclick
32345          * Fires when a header is clicked
32346          * @param {Grid} this
32347          * @param {Number} columnIndex
32348          * @param {Roo.EventObject} e
32349          */
32350         "headerclick" : true,
32351         /**
32352          * @event headerdblclick
32353          * Fires when a header cell is double clicked
32354          * @param {Grid} this
32355          * @param {Number} columnIndex
32356          * @param {Roo.EventObject} e
32357          */
32358         "headerdblclick" : true,
32359         /**
32360          * @event rowcontextmenu
32361          * Fires when a row is right clicked
32362          * @param {Grid} this
32363          * @param {Number} rowIndex
32364          * @param {Roo.EventObject} e
32365          */
32366         "rowcontextmenu" : true,
32367         /**
32368          * @event cellcontextmenu
32369          * Fires when a cell is right clicked
32370          * @param {Grid} this
32371          * @param {Number} rowIndex
32372          * @param {Number} cellIndex
32373          * @param {Roo.EventObject} e
32374          */
32375          "cellcontextmenu" : true,
32376         /**
32377          * @event headercontextmenu
32378          * Fires when a header is right clicked
32379          * @param {Grid} this
32380          * @param {Number} columnIndex
32381          * @param {Roo.EventObject} e
32382          */
32383         "headercontextmenu" : true,
32384         /**
32385          * @event bodyscroll
32386          * Fires when the body element is scrolled
32387          * @param {Number} scrollLeft
32388          * @param {Number} scrollTop
32389          */
32390         "bodyscroll" : true,
32391         /**
32392          * @event columnresize
32393          * Fires when the user resizes a column
32394          * @param {Number} columnIndex
32395          * @param {Number} newSize
32396          */
32397         "columnresize" : true,
32398         /**
32399          * @event columnmove
32400          * Fires when the user moves a column
32401          * @param {Number} oldIndex
32402          * @param {Number} newIndex
32403          */
32404         "columnmove" : true,
32405         /**
32406          * @event startdrag
32407          * Fires when row(s) start being dragged
32408          * @param {Grid} this
32409          * @param {Roo.GridDD} dd The drag drop object
32410          * @param {event} e The raw browser event
32411          */
32412         "startdrag" : true,
32413         /**
32414          * @event enddrag
32415          * Fires when a drag operation is complete
32416          * @param {Grid} this
32417          * @param {Roo.GridDD} dd The drag drop object
32418          * @param {event} e The raw browser event
32419          */
32420         "enddrag" : true,
32421         /**
32422          * @event dragdrop
32423          * Fires when dragged row(s) are dropped on a valid DD target
32424          * @param {Grid} this
32425          * @param {Roo.GridDD} dd The drag drop object
32426          * @param {String} targetId The target drag drop object
32427          * @param {event} e The raw browser event
32428          */
32429         "dragdrop" : true,
32430         /**
32431          * @event dragover
32432          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32433          * @param {Grid} this
32434          * @param {Roo.GridDD} dd The drag drop object
32435          * @param {String} targetId The target drag drop object
32436          * @param {event} e The raw browser event
32437          */
32438         "dragover" : true,
32439         /**
32440          * @event dragenter
32441          *  Fires when the dragged row(s) first cross another DD target while being dragged
32442          * @param {Grid} this
32443          * @param {Roo.GridDD} dd The drag drop object
32444          * @param {String} targetId The target drag drop object
32445          * @param {event} e The raw browser event
32446          */
32447         "dragenter" : true,
32448         /**
32449          * @event dragout
32450          * Fires when the dragged row(s) leave another DD target while being dragged
32451          * @param {Grid} this
32452          * @param {Roo.GridDD} dd The drag drop object
32453          * @param {String} targetId The target drag drop object
32454          * @param {event} e The raw browser event
32455          */
32456         "dragout" : true,
32457         /**
32458          * @event rowclass
32459          * Fires when a row is rendered, so you can change add a style to it.
32460          * @param {GridView} gridview   The grid view
32461          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32462          */
32463         'rowclass' : true,
32464
32465         /**
32466          * @event render
32467          * Fires when the grid is rendered
32468          * @param {Grid} grid
32469          */
32470         'render' : true
32471     });
32472
32473     Roo.grid.Grid.superclass.constructor.call(this);
32474 };
32475 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32476     
32477     /**
32478          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
32479          */
32480         /**
32481          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
32482          */
32483         /**
32484          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
32485          */
32486         /**
32487          * @cfg {Roo.grid.Store} ds The data store for the grid
32488          */
32489         /**
32490          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
32491          */
32492         /**
32493      * @cfg {String} ddGroup - drag drop group.
32494      */
32495       /**
32496      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32497      */
32498
32499     /**
32500      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32501      */
32502     minColumnWidth : 25,
32503
32504     /**
32505      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32506      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32507      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32508      */
32509     autoSizeColumns : false,
32510
32511     /**
32512      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32513      */
32514     autoSizeHeaders : true,
32515
32516     /**
32517      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32518      */
32519     monitorWindowResize : true,
32520
32521     /**
32522      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32523      * rows measured to get a columns size. Default is 0 (all rows).
32524      */
32525     maxRowsToMeasure : 0,
32526
32527     /**
32528      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32529      */
32530     trackMouseOver : true,
32531
32532     /**
32533     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32534     */
32535       /**
32536     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32537     */
32538     
32539     /**
32540     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32541     */
32542     enableDragDrop : false,
32543     
32544     /**
32545     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32546     */
32547     enableColumnMove : true,
32548     
32549     /**
32550     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32551     */
32552     enableColumnHide : true,
32553     
32554     /**
32555     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32556     */
32557     enableRowHeightSync : false,
32558     
32559     /**
32560     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32561     */
32562     stripeRows : true,
32563     
32564     /**
32565     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32566     */
32567     autoHeight : false,
32568
32569     /**
32570      * @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.
32571      */
32572     autoExpandColumn : false,
32573
32574     /**
32575     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32576     * Default is 50.
32577     */
32578     autoExpandMin : 50,
32579
32580     /**
32581     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32582     */
32583     autoExpandMax : 1000,
32584
32585     /**
32586     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32587     */
32588     view : null,
32589
32590     /**
32591     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32592     */
32593     loadMask : false,
32594     /**
32595     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32596     */
32597     dropTarget: false,
32598     
32599    
32600     
32601     // private
32602     rendered : false,
32603
32604     /**
32605     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32606     * of a fixed width. Default is false.
32607     */
32608     /**
32609     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32610     */
32611     
32612     
32613     /**
32614     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32615     * %0 is replaced with the number of selected rows.
32616     */
32617     ddText : "{0} selected row{1}",
32618     
32619     
32620     /**
32621      * Called once after all setup has been completed and the grid is ready to be rendered.
32622      * @return {Roo.grid.Grid} this
32623      */
32624     render : function()
32625     {
32626         var c = this.container;
32627         // try to detect autoHeight/width mode
32628         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32629             this.autoHeight = true;
32630         }
32631         var view = this.getView();
32632         view.init(this);
32633
32634         c.on("click", this.onClick, this);
32635         c.on("dblclick", this.onDblClick, this);
32636         c.on("contextmenu", this.onContextMenu, this);
32637         c.on("keydown", this.onKeyDown, this);
32638         if (Roo.isTouch) {
32639             c.on("touchstart", this.onTouchStart, this);
32640         }
32641
32642         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32643
32644         this.getSelectionModel().init(this);
32645
32646         view.render();
32647
32648         if(this.loadMask){
32649             this.loadMask = new Roo.LoadMask(this.container,
32650                     Roo.apply({store:this.dataSource}, this.loadMask));
32651         }
32652         
32653         
32654         if (this.toolbar && this.toolbar.xtype) {
32655             this.toolbar.container = this.getView().getHeaderPanel(true);
32656             this.toolbar = new Roo.Toolbar(this.toolbar);
32657         }
32658         if (this.footer && this.footer.xtype) {
32659             this.footer.dataSource = this.getDataSource();
32660             this.footer.container = this.getView().getFooterPanel(true);
32661             this.footer = Roo.factory(this.footer, Roo);
32662         }
32663         if (this.dropTarget && this.dropTarget.xtype) {
32664             delete this.dropTarget.xtype;
32665             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32666         }
32667         
32668         
32669         this.rendered = true;
32670         this.fireEvent('render', this);
32671         return this;
32672     },
32673
32674     /**
32675      * Reconfigures the grid to use a different Store and Column Model.
32676      * The View will be bound to the new objects and refreshed.
32677      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32678      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32679      */
32680     reconfigure : function(dataSource, colModel){
32681         if(this.loadMask){
32682             this.loadMask.destroy();
32683             this.loadMask = new Roo.LoadMask(this.container,
32684                     Roo.apply({store:dataSource}, this.loadMask));
32685         }
32686         this.view.bind(dataSource, colModel);
32687         this.dataSource = dataSource;
32688         this.colModel = colModel;
32689         this.view.refresh(true);
32690     },
32691     /**
32692      * addColumns
32693      * Add's a column, default at the end..
32694      
32695      * @param {int} position to add (default end)
32696      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32697      */
32698     addColumns : function(pos, ar)
32699     {
32700         
32701         for (var i =0;i< ar.length;i++) {
32702             var cfg = ar[i];
32703             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32704             this.cm.lookup[cfg.id] = cfg;
32705         }
32706         
32707         
32708         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32709             pos = this.cm.config.length; //this.cm.config.push(cfg);
32710         } 
32711         pos = Math.max(0,pos);
32712         ar.unshift(0);
32713         ar.unshift(pos);
32714         this.cm.config.splice.apply(this.cm.config, ar);
32715         
32716         
32717         
32718         this.view.generateRules(this.cm);
32719         this.view.refresh(true);
32720         
32721     },
32722     
32723     
32724     
32725     
32726     // private
32727     onKeyDown : function(e){
32728         this.fireEvent("keydown", e);
32729     },
32730
32731     /**
32732      * Destroy this grid.
32733      * @param {Boolean} removeEl True to remove the element
32734      */
32735     destroy : function(removeEl, keepListeners){
32736         if(this.loadMask){
32737             this.loadMask.destroy();
32738         }
32739         var c = this.container;
32740         c.removeAllListeners();
32741         this.view.destroy();
32742         this.colModel.purgeListeners();
32743         if(!keepListeners){
32744             this.purgeListeners();
32745         }
32746         c.update("");
32747         if(removeEl === true){
32748             c.remove();
32749         }
32750     },
32751
32752     // private
32753     processEvent : function(name, e){
32754         // does this fire select???
32755         //Roo.log('grid:processEvent '  + name);
32756         
32757         if (name != 'touchstart' ) {
32758             this.fireEvent(name, e);    
32759         }
32760         
32761         var t = e.getTarget();
32762         var v = this.view;
32763         var header = v.findHeaderIndex(t);
32764         if(header !== false){
32765             var ename = name == 'touchstart' ? 'click' : name;
32766              
32767             this.fireEvent("header" + ename, this, header, e);
32768         }else{
32769             var row = v.findRowIndex(t);
32770             var cell = v.findCellIndex(t);
32771             if (name == 'touchstart') {
32772                 // first touch is always a click.
32773                 // hopefull this happens after selection is updated.?
32774                 name = false;
32775                 
32776                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32777                     var cs = this.selModel.getSelectedCell();
32778                     if (row == cs[0] && cell == cs[1]){
32779                         name = 'dblclick';
32780                     }
32781                 }
32782                 if (typeof(this.selModel.getSelections) != 'undefined') {
32783                     var cs = this.selModel.getSelections();
32784                     var ds = this.dataSource;
32785                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32786                         name = 'dblclick';
32787                     }
32788                 }
32789                 if (!name) {
32790                     return;
32791                 }
32792             }
32793             
32794             
32795             if(row !== false){
32796                 this.fireEvent("row" + name, this, row, e);
32797                 if(cell !== false){
32798                     this.fireEvent("cell" + name, this, row, cell, e);
32799                 }
32800             }
32801         }
32802     },
32803
32804     // private
32805     onClick : function(e){
32806         this.processEvent("click", e);
32807     },
32808    // private
32809     onTouchStart : function(e){
32810         this.processEvent("touchstart", e);
32811     },
32812
32813     // private
32814     onContextMenu : function(e, t){
32815         this.processEvent("contextmenu", e);
32816     },
32817
32818     // private
32819     onDblClick : function(e){
32820         this.processEvent("dblclick", e);
32821     },
32822
32823     // private
32824     walkCells : function(row, col, step, fn, scope){
32825         var cm = this.colModel, clen = cm.getColumnCount();
32826         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32827         if(step < 0){
32828             if(col < 0){
32829                 row--;
32830                 first = false;
32831             }
32832             while(row >= 0){
32833                 if(!first){
32834                     col = clen-1;
32835                 }
32836                 first = false;
32837                 while(col >= 0){
32838                     if(fn.call(scope || this, row, col, cm) === true){
32839                         return [row, col];
32840                     }
32841                     col--;
32842                 }
32843                 row--;
32844             }
32845         } else {
32846             if(col >= clen){
32847                 row++;
32848                 first = false;
32849             }
32850             while(row < rlen){
32851                 if(!first){
32852                     col = 0;
32853                 }
32854                 first = false;
32855                 while(col < clen){
32856                     if(fn.call(scope || this, row, col, cm) === true){
32857                         return [row, col];
32858                     }
32859                     col++;
32860                 }
32861                 row++;
32862             }
32863         }
32864         return null;
32865     },
32866
32867     // private
32868     getSelections : function(){
32869         return this.selModel.getSelections();
32870     },
32871
32872     /**
32873      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32874      * but if manual update is required this method will initiate it.
32875      */
32876     autoSize : function(){
32877         if(this.rendered){
32878             this.view.layout();
32879             if(this.view.adjustForScroll){
32880                 this.view.adjustForScroll();
32881             }
32882         }
32883     },
32884
32885     /**
32886      * Returns the grid's underlying element.
32887      * @return {Element} The element
32888      */
32889     getGridEl : function(){
32890         return this.container;
32891     },
32892
32893     // private for compatibility, overridden by editor grid
32894     stopEditing : function(){},
32895
32896     /**
32897      * Returns the grid's SelectionModel.
32898      * @return {SelectionModel}
32899      */
32900     getSelectionModel : function(){
32901         if(!this.selModel){
32902             this.selModel = new Roo.grid.RowSelectionModel();
32903         }
32904         return this.selModel;
32905     },
32906
32907     /**
32908      * Returns the grid's DataSource.
32909      * @return {DataSource}
32910      */
32911     getDataSource : function(){
32912         return this.dataSource;
32913     },
32914
32915     /**
32916      * Returns the grid's ColumnModel.
32917      * @return {ColumnModel}
32918      */
32919     getColumnModel : function(){
32920         return this.colModel;
32921     },
32922
32923     /**
32924      * Returns the grid's GridView object.
32925      * @return {GridView}
32926      */
32927     getView : function(){
32928         if(!this.view){
32929             this.view = new Roo.grid.GridView(this.viewConfig);
32930             this.relayEvents(this.view, [
32931                 "beforerowremoved", "beforerowsinserted",
32932                 "beforerefresh", "rowremoved",
32933                 "rowsinserted", "rowupdated" ,"refresh"
32934             ]);
32935         }
32936         return this.view;
32937     },
32938     /**
32939      * Called to get grid's drag proxy text, by default returns this.ddText.
32940      * Override this to put something different in the dragged text.
32941      * @return {String}
32942      */
32943     getDragDropText : function(){
32944         var count = this.selModel.getCount();
32945         return String.format(this.ddText, count, count == 1 ? '' : 's');
32946     }
32947 });
32948 /*
32949  * Based on:
32950  * Ext JS Library 1.1.1
32951  * Copyright(c) 2006-2007, Ext JS, LLC.
32952  *
32953  * Originally Released Under LGPL - original licence link has changed is not relivant.
32954  *
32955  * Fork - LGPL
32956  * <script type="text/javascript">
32957  */
32958  /**
32959  * @class Roo.grid.AbstractGridView
32960  * @extends Roo.util.Observable
32961  * @abstract
32962  * Abstract base class for grid Views
32963  * @constructor
32964  */
32965 Roo.grid.AbstractGridView = function(){
32966         this.grid = null;
32967         
32968         this.events = {
32969             "beforerowremoved" : true,
32970             "beforerowsinserted" : true,
32971             "beforerefresh" : true,
32972             "rowremoved" : true,
32973             "rowsinserted" : true,
32974             "rowupdated" : true,
32975             "refresh" : true
32976         };
32977     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32978 };
32979
32980 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32981     rowClass : "x-grid-row",
32982     cellClass : "x-grid-cell",
32983     tdClass : "x-grid-td",
32984     hdClass : "x-grid-hd",
32985     splitClass : "x-grid-hd-split",
32986     
32987     init: function(grid){
32988         this.grid = grid;
32989                 var cid = this.grid.getGridEl().id;
32990         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32991         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32992         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32993         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32994         },
32995         
32996     getColumnRenderers : function(){
32997         var renderers = [];
32998         var cm = this.grid.colModel;
32999         var colCount = cm.getColumnCount();
33000         for(var i = 0; i < colCount; i++){
33001             renderers[i] = cm.getRenderer(i);
33002         }
33003         return renderers;
33004     },
33005     
33006     getColumnIds : function(){
33007         var ids = [];
33008         var cm = this.grid.colModel;
33009         var colCount = cm.getColumnCount();
33010         for(var i = 0; i < colCount; i++){
33011             ids[i] = cm.getColumnId(i);
33012         }
33013         return ids;
33014     },
33015     
33016     getDataIndexes : function(){
33017         if(!this.indexMap){
33018             this.indexMap = this.buildIndexMap();
33019         }
33020         return this.indexMap.colToData;
33021     },
33022     
33023     getColumnIndexByDataIndex : function(dataIndex){
33024         if(!this.indexMap){
33025             this.indexMap = this.buildIndexMap();
33026         }
33027         return this.indexMap.dataToCol[dataIndex];
33028     },
33029     
33030     /**
33031      * Set a css style for a column dynamically. 
33032      * @param {Number} colIndex The index of the column
33033      * @param {String} name The css property name
33034      * @param {String} value The css value
33035      */
33036     setCSSStyle : function(colIndex, name, value){
33037         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33038         Roo.util.CSS.updateRule(selector, name, value);
33039     },
33040     
33041     generateRules : function(cm){
33042         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33043         Roo.util.CSS.removeStyleSheet(rulesId);
33044         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33045             var cid = cm.getColumnId(i);
33046             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33047                          this.tdSelector, cid, " {\n}\n",
33048                          this.hdSelector, cid, " {\n}\n",
33049                          this.splitSelector, cid, " {\n}\n");
33050         }
33051         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33052     }
33053 });/*
33054  * Based on:
33055  * Ext JS Library 1.1.1
33056  * Copyright(c) 2006-2007, Ext JS, LLC.
33057  *
33058  * Originally Released Under LGPL - original licence link has changed is not relivant.
33059  *
33060  * Fork - LGPL
33061  * <script type="text/javascript">
33062  */
33063
33064 // private
33065 // This is a support class used internally by the Grid components
33066 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33067     this.grid = grid;
33068     this.view = grid.getView();
33069     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33070     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33071     if(hd2){
33072         this.setHandleElId(Roo.id(hd));
33073         this.setOuterHandleElId(Roo.id(hd2));
33074     }
33075     this.scroll = false;
33076 };
33077 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33078     maxDragWidth: 120,
33079     getDragData : function(e){
33080         var t = Roo.lib.Event.getTarget(e);
33081         var h = this.view.findHeaderCell(t);
33082         if(h){
33083             return {ddel: h.firstChild, header:h};
33084         }
33085         return false;
33086     },
33087
33088     onInitDrag : function(e){
33089         this.view.headersDisabled = true;
33090         var clone = this.dragData.ddel.cloneNode(true);
33091         clone.id = Roo.id();
33092         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33093         this.proxy.update(clone);
33094         return true;
33095     },
33096
33097     afterValidDrop : function(){
33098         var v = this.view;
33099         setTimeout(function(){
33100             v.headersDisabled = false;
33101         }, 50);
33102     },
33103
33104     afterInvalidDrop : function(){
33105         var v = this.view;
33106         setTimeout(function(){
33107             v.headersDisabled = false;
33108         }, 50);
33109     }
33110 });
33111 /*
33112  * Based on:
33113  * Ext JS Library 1.1.1
33114  * Copyright(c) 2006-2007, Ext JS, LLC.
33115  *
33116  * Originally Released Under LGPL - original licence link has changed is not relivant.
33117  *
33118  * Fork - LGPL
33119  * <script type="text/javascript">
33120  */
33121 // private
33122 // This is a support class used internally by the Grid components
33123 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33124     this.grid = grid;
33125     this.view = grid.getView();
33126     // split the proxies so they don't interfere with mouse events
33127     this.proxyTop = Roo.DomHelper.append(document.body, {
33128         cls:"col-move-top", html:"&#160;"
33129     }, true);
33130     this.proxyBottom = Roo.DomHelper.append(document.body, {
33131         cls:"col-move-bottom", html:"&#160;"
33132     }, true);
33133     this.proxyTop.hide = this.proxyBottom.hide = function(){
33134         this.setLeftTop(-100,-100);
33135         this.setStyle("visibility", "hidden");
33136     };
33137     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33138     // temporarily disabled
33139     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33140     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33141 };
33142 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33143     proxyOffsets : [-4, -9],
33144     fly: Roo.Element.fly,
33145
33146     getTargetFromEvent : function(e){
33147         var t = Roo.lib.Event.getTarget(e);
33148         var cindex = this.view.findCellIndex(t);
33149         if(cindex !== false){
33150             return this.view.getHeaderCell(cindex);
33151         }
33152         return null;
33153     },
33154
33155     nextVisible : function(h){
33156         var v = this.view, cm = this.grid.colModel;
33157         h = h.nextSibling;
33158         while(h){
33159             if(!cm.isHidden(v.getCellIndex(h))){
33160                 return h;
33161             }
33162             h = h.nextSibling;
33163         }
33164         return null;
33165     },
33166
33167     prevVisible : function(h){
33168         var v = this.view, cm = this.grid.colModel;
33169         h = h.prevSibling;
33170         while(h){
33171             if(!cm.isHidden(v.getCellIndex(h))){
33172                 return h;
33173             }
33174             h = h.prevSibling;
33175         }
33176         return null;
33177     },
33178
33179     positionIndicator : function(h, n, e){
33180         var x = Roo.lib.Event.getPageX(e);
33181         var r = Roo.lib.Dom.getRegion(n.firstChild);
33182         var px, pt, py = r.top + this.proxyOffsets[1];
33183         if((r.right - x) <= (r.right-r.left)/2){
33184             px = r.right+this.view.borderWidth;
33185             pt = "after";
33186         }else{
33187             px = r.left;
33188             pt = "before";
33189         }
33190         var oldIndex = this.view.getCellIndex(h);
33191         var newIndex = this.view.getCellIndex(n);
33192
33193         if(this.grid.colModel.isFixed(newIndex)){
33194             return false;
33195         }
33196
33197         var locked = this.grid.colModel.isLocked(newIndex);
33198
33199         if(pt == "after"){
33200             newIndex++;
33201         }
33202         if(oldIndex < newIndex){
33203             newIndex--;
33204         }
33205         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33206             return false;
33207         }
33208         px +=  this.proxyOffsets[0];
33209         this.proxyTop.setLeftTop(px, py);
33210         this.proxyTop.show();
33211         if(!this.bottomOffset){
33212             this.bottomOffset = this.view.mainHd.getHeight();
33213         }
33214         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33215         this.proxyBottom.show();
33216         return pt;
33217     },
33218
33219     onNodeEnter : function(n, dd, e, data){
33220         if(data.header != n){
33221             this.positionIndicator(data.header, n, e);
33222         }
33223     },
33224
33225     onNodeOver : function(n, dd, e, data){
33226         var result = false;
33227         if(data.header != n){
33228             result = this.positionIndicator(data.header, n, e);
33229         }
33230         if(!result){
33231             this.proxyTop.hide();
33232             this.proxyBottom.hide();
33233         }
33234         return result ? this.dropAllowed : this.dropNotAllowed;
33235     },
33236
33237     onNodeOut : function(n, dd, e, data){
33238         this.proxyTop.hide();
33239         this.proxyBottom.hide();
33240     },
33241
33242     onNodeDrop : function(n, dd, e, data){
33243         var h = data.header;
33244         if(h != n){
33245             var cm = this.grid.colModel;
33246             var x = Roo.lib.Event.getPageX(e);
33247             var r = Roo.lib.Dom.getRegion(n.firstChild);
33248             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33249             var oldIndex = this.view.getCellIndex(h);
33250             var newIndex = this.view.getCellIndex(n);
33251             var locked = cm.isLocked(newIndex);
33252             if(pt == "after"){
33253                 newIndex++;
33254             }
33255             if(oldIndex < newIndex){
33256                 newIndex--;
33257             }
33258             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33259                 return false;
33260             }
33261             cm.setLocked(oldIndex, locked, true);
33262             cm.moveColumn(oldIndex, newIndex);
33263             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33264             return true;
33265         }
33266         return false;
33267     }
33268 });
33269 /*
33270  * Based on:
33271  * Ext JS Library 1.1.1
33272  * Copyright(c) 2006-2007, Ext JS, LLC.
33273  *
33274  * Originally Released Under LGPL - original licence link has changed is not relivant.
33275  *
33276  * Fork - LGPL
33277  * <script type="text/javascript">
33278  */
33279   
33280 /**
33281  * @class Roo.grid.GridView
33282  * @extends Roo.util.Observable
33283  *
33284  * @constructor
33285  * @param {Object} config
33286  */
33287 Roo.grid.GridView = function(config){
33288     Roo.grid.GridView.superclass.constructor.call(this);
33289     this.el = null;
33290
33291     Roo.apply(this, config);
33292 };
33293
33294 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33295
33296     unselectable :  'unselectable="on"',
33297     unselectableCls :  'x-unselectable',
33298     
33299     
33300     rowClass : "x-grid-row",
33301
33302     cellClass : "x-grid-col",
33303
33304     tdClass : "x-grid-td",
33305
33306     hdClass : "x-grid-hd",
33307
33308     splitClass : "x-grid-split",
33309
33310     sortClasses : ["sort-asc", "sort-desc"],
33311
33312     enableMoveAnim : false,
33313
33314     hlColor: "C3DAF9",
33315
33316     dh : Roo.DomHelper,
33317
33318     fly : Roo.Element.fly,
33319
33320     css : Roo.util.CSS,
33321
33322     borderWidth: 1,
33323
33324     splitOffset: 3,
33325
33326     scrollIncrement : 22,
33327
33328     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33329
33330     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33331
33332     bind : function(ds, cm){
33333         if(this.ds){
33334             this.ds.un("load", this.onLoad, this);
33335             this.ds.un("datachanged", this.onDataChange, this);
33336             this.ds.un("add", this.onAdd, this);
33337             this.ds.un("remove", this.onRemove, this);
33338             this.ds.un("update", this.onUpdate, this);
33339             this.ds.un("clear", this.onClear, this);
33340         }
33341         if(ds){
33342             ds.on("load", this.onLoad, this);
33343             ds.on("datachanged", this.onDataChange, this);
33344             ds.on("add", this.onAdd, this);
33345             ds.on("remove", this.onRemove, this);
33346             ds.on("update", this.onUpdate, this);
33347             ds.on("clear", this.onClear, this);
33348         }
33349         this.ds = ds;
33350
33351         if(this.cm){
33352             this.cm.un("widthchange", this.onColWidthChange, this);
33353             this.cm.un("headerchange", this.onHeaderChange, this);
33354             this.cm.un("hiddenchange", this.onHiddenChange, this);
33355             this.cm.un("columnmoved", this.onColumnMove, this);
33356             this.cm.un("columnlockchange", this.onColumnLock, this);
33357         }
33358         if(cm){
33359             this.generateRules(cm);
33360             cm.on("widthchange", this.onColWidthChange, this);
33361             cm.on("headerchange", this.onHeaderChange, this);
33362             cm.on("hiddenchange", this.onHiddenChange, this);
33363             cm.on("columnmoved", this.onColumnMove, this);
33364             cm.on("columnlockchange", this.onColumnLock, this);
33365         }
33366         this.cm = cm;
33367     },
33368
33369     init: function(grid){
33370         Roo.grid.GridView.superclass.init.call(this, grid);
33371
33372         this.bind(grid.dataSource, grid.colModel);
33373
33374         grid.on("headerclick", this.handleHeaderClick, this);
33375
33376         if(grid.trackMouseOver){
33377             grid.on("mouseover", this.onRowOver, this);
33378             grid.on("mouseout", this.onRowOut, this);
33379         }
33380         grid.cancelTextSelection = function(){};
33381         this.gridId = grid.id;
33382
33383         var tpls = this.templates || {};
33384
33385         if(!tpls.master){
33386             tpls.master = new Roo.Template(
33387                '<div class="x-grid" hidefocus="true">',
33388                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33389                   '<div class="x-grid-topbar"></div>',
33390                   '<div class="x-grid-scroller"><div></div></div>',
33391                   '<div class="x-grid-locked">',
33392                       '<div class="x-grid-header">{lockedHeader}</div>',
33393                       '<div class="x-grid-body">{lockedBody}</div>',
33394                   "</div>",
33395                   '<div class="x-grid-viewport">',
33396                       '<div class="x-grid-header">{header}</div>',
33397                       '<div class="x-grid-body">{body}</div>',
33398                   "</div>",
33399                   '<div class="x-grid-bottombar"></div>',
33400                  
33401                   '<div class="x-grid-resize-proxy">&#160;</div>',
33402                "</div>"
33403             );
33404             tpls.master.disableformats = true;
33405         }
33406
33407         if(!tpls.header){
33408             tpls.header = new Roo.Template(
33409                '<table border="0" cellspacing="0" cellpadding="0">',
33410                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33411                "</table>{splits}"
33412             );
33413             tpls.header.disableformats = true;
33414         }
33415         tpls.header.compile();
33416
33417         if(!tpls.hcell){
33418             tpls.hcell = new Roo.Template(
33419                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33420                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33421                 "</div></td>"
33422              );
33423              tpls.hcell.disableFormats = true;
33424         }
33425         tpls.hcell.compile();
33426
33427         if(!tpls.hsplit){
33428             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33429                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33430             tpls.hsplit.disableFormats = true;
33431         }
33432         tpls.hsplit.compile();
33433
33434         if(!tpls.body){
33435             tpls.body = new Roo.Template(
33436                '<table border="0" cellspacing="0" cellpadding="0">',
33437                "<tbody>{rows}</tbody>",
33438                "</table>"
33439             );
33440             tpls.body.disableFormats = true;
33441         }
33442         tpls.body.compile();
33443
33444         if(!tpls.row){
33445             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33446             tpls.row.disableFormats = true;
33447         }
33448         tpls.row.compile();
33449
33450         if(!tpls.cell){
33451             tpls.cell = new Roo.Template(
33452                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33453                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33454                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33455                 "</td>"
33456             );
33457             tpls.cell.disableFormats = true;
33458         }
33459         tpls.cell.compile();
33460
33461         this.templates = tpls;
33462     },
33463
33464     // remap these for backwards compat
33465     onColWidthChange : function(){
33466         this.updateColumns.apply(this, arguments);
33467     },
33468     onHeaderChange : function(){
33469         this.updateHeaders.apply(this, arguments);
33470     }, 
33471     onHiddenChange : function(){
33472         this.handleHiddenChange.apply(this, arguments);
33473     },
33474     onColumnMove : function(){
33475         this.handleColumnMove.apply(this, arguments);
33476     },
33477     onColumnLock : function(){
33478         this.handleLockChange.apply(this, arguments);
33479     },
33480
33481     onDataChange : function(){
33482         this.refresh();
33483         this.updateHeaderSortState();
33484     },
33485
33486     onClear : function(){
33487         this.refresh();
33488     },
33489
33490     onUpdate : function(ds, record){
33491         this.refreshRow(record);
33492     },
33493
33494     refreshRow : function(record){
33495         var ds = this.ds, index;
33496         if(typeof record == 'number'){
33497             index = record;
33498             record = ds.getAt(index);
33499         }else{
33500             index = ds.indexOf(record);
33501         }
33502         this.insertRows(ds, index, index, true);
33503         this.onRemove(ds, record, index+1, true);
33504         this.syncRowHeights(index, index);
33505         this.layout();
33506         this.fireEvent("rowupdated", this, index, record);
33507     },
33508
33509     onAdd : function(ds, records, index){
33510         this.insertRows(ds, index, index + (records.length-1));
33511     },
33512
33513     onRemove : function(ds, record, index, isUpdate){
33514         if(isUpdate !== true){
33515             this.fireEvent("beforerowremoved", this, index, record);
33516         }
33517         var bt = this.getBodyTable(), lt = this.getLockedTable();
33518         if(bt.rows[index]){
33519             bt.firstChild.removeChild(bt.rows[index]);
33520         }
33521         if(lt.rows[index]){
33522             lt.firstChild.removeChild(lt.rows[index]);
33523         }
33524         if(isUpdate !== true){
33525             this.stripeRows(index);
33526             this.syncRowHeights(index, index);
33527             this.layout();
33528             this.fireEvent("rowremoved", this, index, record);
33529         }
33530     },
33531
33532     onLoad : function(){
33533         this.scrollToTop();
33534     },
33535
33536     /**
33537      * Scrolls the grid to the top
33538      */
33539     scrollToTop : function(){
33540         if(this.scroller){
33541             this.scroller.dom.scrollTop = 0;
33542             this.syncScroll();
33543         }
33544     },
33545
33546     /**
33547      * Gets a panel in the header of the grid that can be used for toolbars etc.
33548      * After modifying the contents of this panel a call to grid.autoSize() may be
33549      * required to register any changes in size.
33550      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33551      * @return Roo.Element
33552      */
33553     getHeaderPanel : function(doShow){
33554         if(doShow){
33555             this.headerPanel.show();
33556         }
33557         return this.headerPanel;
33558     },
33559
33560     /**
33561      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33562      * After modifying the contents of this panel a call to grid.autoSize() may be
33563      * required to register any changes in size.
33564      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33565      * @return Roo.Element
33566      */
33567     getFooterPanel : function(doShow){
33568         if(doShow){
33569             this.footerPanel.show();
33570         }
33571         return this.footerPanel;
33572     },
33573
33574     initElements : function(){
33575         var E = Roo.Element;
33576         var el = this.grid.getGridEl().dom.firstChild;
33577         var cs = el.childNodes;
33578
33579         this.el = new E(el);
33580         
33581          this.focusEl = new E(el.firstChild);
33582         this.focusEl.swallowEvent("click", true);
33583         
33584         this.headerPanel = new E(cs[1]);
33585         this.headerPanel.enableDisplayMode("block");
33586
33587         this.scroller = new E(cs[2]);
33588         this.scrollSizer = new E(this.scroller.dom.firstChild);
33589
33590         this.lockedWrap = new E(cs[3]);
33591         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33592         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33593
33594         this.mainWrap = new E(cs[4]);
33595         this.mainHd = new E(this.mainWrap.dom.firstChild);
33596         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33597
33598         this.footerPanel = new E(cs[5]);
33599         this.footerPanel.enableDisplayMode("block");
33600
33601         this.resizeProxy = new E(cs[6]);
33602
33603         this.headerSelector = String.format(
33604            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33605            this.lockedHd.id, this.mainHd.id
33606         );
33607
33608         this.splitterSelector = String.format(
33609            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33610            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33611         );
33612     },
33613     idToCssName : function(s)
33614     {
33615         return s.replace(/[^a-z0-9]+/ig, '-');
33616     },
33617
33618     getHeaderCell : function(index){
33619         return Roo.DomQuery.select(this.headerSelector)[index];
33620     },
33621
33622     getHeaderCellMeasure : function(index){
33623         return this.getHeaderCell(index).firstChild;
33624     },
33625
33626     getHeaderCellText : function(index){
33627         return this.getHeaderCell(index).firstChild.firstChild;
33628     },
33629
33630     getLockedTable : function(){
33631         return this.lockedBody.dom.firstChild;
33632     },
33633
33634     getBodyTable : function(){
33635         return this.mainBody.dom.firstChild;
33636     },
33637
33638     getLockedRow : function(index){
33639         return this.getLockedTable().rows[index];
33640     },
33641
33642     getRow : function(index){
33643         return this.getBodyTable().rows[index];
33644     },
33645
33646     getRowComposite : function(index){
33647         if(!this.rowEl){
33648             this.rowEl = new Roo.CompositeElementLite();
33649         }
33650         var els = [], lrow, mrow;
33651         if(lrow = this.getLockedRow(index)){
33652             els.push(lrow);
33653         }
33654         if(mrow = this.getRow(index)){
33655             els.push(mrow);
33656         }
33657         this.rowEl.elements = els;
33658         return this.rowEl;
33659     },
33660     /**
33661      * Gets the 'td' of the cell
33662      * 
33663      * @param {Integer} rowIndex row to select
33664      * @param {Integer} colIndex column to select
33665      * 
33666      * @return {Object} 
33667      */
33668     getCell : function(rowIndex, colIndex){
33669         var locked = this.cm.getLockedCount();
33670         var source;
33671         if(colIndex < locked){
33672             source = this.lockedBody.dom.firstChild;
33673         }else{
33674             source = this.mainBody.dom.firstChild;
33675             colIndex -= locked;
33676         }
33677         return source.rows[rowIndex].childNodes[colIndex];
33678     },
33679
33680     getCellText : function(rowIndex, colIndex){
33681         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33682     },
33683
33684     getCellBox : function(cell){
33685         var b = this.fly(cell).getBox();
33686         if(Roo.isOpera){ // opera fails to report the Y
33687             b.y = cell.offsetTop + this.mainBody.getY();
33688         }
33689         return b;
33690     },
33691
33692     getCellIndex : function(cell){
33693         var id = String(cell.className).match(this.cellRE);
33694         if(id){
33695             return parseInt(id[1], 10);
33696         }
33697         return 0;
33698     },
33699
33700     findHeaderIndex : function(n){
33701         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33702         return r ? this.getCellIndex(r) : false;
33703     },
33704
33705     findHeaderCell : function(n){
33706         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33707         return r ? r : false;
33708     },
33709
33710     findRowIndex : function(n){
33711         if(!n){
33712             return false;
33713         }
33714         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33715         return r ? r.rowIndex : false;
33716     },
33717
33718     findCellIndex : function(node){
33719         var stop = this.el.dom;
33720         while(node && node != stop){
33721             if(this.findRE.test(node.className)){
33722                 return this.getCellIndex(node);
33723             }
33724             node = node.parentNode;
33725         }
33726         return false;
33727     },
33728
33729     getColumnId : function(index){
33730         return this.cm.getColumnId(index);
33731     },
33732
33733     getSplitters : function()
33734     {
33735         if(this.splitterSelector){
33736            return Roo.DomQuery.select(this.splitterSelector);
33737         }else{
33738             return null;
33739       }
33740     },
33741
33742     getSplitter : function(index){
33743         return this.getSplitters()[index];
33744     },
33745
33746     onRowOver : function(e, t){
33747         var row;
33748         if((row = this.findRowIndex(t)) !== false){
33749             this.getRowComposite(row).addClass("x-grid-row-over");
33750         }
33751     },
33752
33753     onRowOut : function(e, t){
33754         var row;
33755         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33756             this.getRowComposite(row).removeClass("x-grid-row-over");
33757         }
33758     },
33759
33760     renderHeaders : function(){
33761         var cm = this.cm;
33762         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33763         var cb = [], lb = [], sb = [], lsb = [], p = {};
33764         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33765             p.cellId = "x-grid-hd-0-" + i;
33766             p.splitId = "x-grid-csplit-0-" + i;
33767             p.id = cm.getColumnId(i);
33768             p.value = cm.getColumnHeader(i) || "";
33769             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33770             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33771             if(!cm.isLocked(i)){
33772                 cb[cb.length] = ct.apply(p);
33773                 sb[sb.length] = st.apply(p);
33774             }else{
33775                 lb[lb.length] = ct.apply(p);
33776                 lsb[lsb.length] = st.apply(p);
33777             }
33778         }
33779         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33780                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33781     },
33782
33783     updateHeaders : function(){
33784         var html = this.renderHeaders();
33785         this.lockedHd.update(html[0]);
33786         this.mainHd.update(html[1]);
33787     },
33788
33789     /**
33790      * Focuses the specified row.
33791      * @param {Number} row The row index
33792      */
33793     focusRow : function(row)
33794     {
33795         //Roo.log('GridView.focusRow');
33796         var x = this.scroller.dom.scrollLeft;
33797         this.focusCell(row, 0, false);
33798         this.scroller.dom.scrollLeft = x;
33799     },
33800
33801     /**
33802      * Focuses the specified cell.
33803      * @param {Number} row The row index
33804      * @param {Number} col The column index
33805      * @param {Boolean} hscroll false to disable horizontal scrolling
33806      */
33807     focusCell : function(row, col, hscroll)
33808     {
33809         //Roo.log('GridView.focusCell');
33810         var el = this.ensureVisible(row, col, hscroll);
33811         this.focusEl.alignTo(el, "tl-tl");
33812         if(Roo.isGecko){
33813             this.focusEl.focus();
33814         }else{
33815             this.focusEl.focus.defer(1, this.focusEl);
33816         }
33817     },
33818
33819     /**
33820      * Scrolls the specified cell into view
33821      * @param {Number} row The row index
33822      * @param {Number} col The column index
33823      * @param {Boolean} hscroll false to disable horizontal scrolling
33824      */
33825     ensureVisible : function(row, col, hscroll)
33826     {
33827         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33828         //return null; //disable for testing.
33829         if(typeof row != "number"){
33830             row = row.rowIndex;
33831         }
33832         if(row < 0 && row >= this.ds.getCount()){
33833             return  null;
33834         }
33835         col = (col !== undefined ? col : 0);
33836         var cm = this.grid.colModel;
33837         while(cm.isHidden(col)){
33838             col++;
33839         }
33840
33841         var el = this.getCell(row, col);
33842         if(!el){
33843             return null;
33844         }
33845         var c = this.scroller.dom;
33846
33847         var ctop = parseInt(el.offsetTop, 10);
33848         var cleft = parseInt(el.offsetLeft, 10);
33849         var cbot = ctop + el.offsetHeight;
33850         var cright = cleft + el.offsetWidth;
33851         
33852         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33853         var stop = parseInt(c.scrollTop, 10);
33854         var sleft = parseInt(c.scrollLeft, 10);
33855         var sbot = stop + ch;
33856         var sright = sleft + c.clientWidth;
33857         /*
33858         Roo.log('GridView.ensureVisible:' +
33859                 ' ctop:' + ctop +
33860                 ' c.clientHeight:' + c.clientHeight +
33861                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33862                 ' stop:' + stop +
33863                 ' cbot:' + cbot +
33864                 ' sbot:' + sbot +
33865                 ' ch:' + ch  
33866                 );
33867         */
33868         if(ctop < stop){
33869             c.scrollTop = ctop;
33870             //Roo.log("set scrolltop to ctop DISABLE?");
33871         }else if(cbot > sbot){
33872             //Roo.log("set scrolltop to cbot-ch");
33873             c.scrollTop = cbot-ch;
33874         }
33875         
33876         if(hscroll !== false){
33877             if(cleft < sleft){
33878                 c.scrollLeft = cleft;
33879             }else if(cright > sright){
33880                 c.scrollLeft = cright-c.clientWidth;
33881             }
33882         }
33883          
33884         return el;
33885     },
33886
33887     updateColumns : function(){
33888         this.grid.stopEditing();
33889         var cm = this.grid.colModel, colIds = this.getColumnIds();
33890         //var totalWidth = cm.getTotalWidth();
33891         var pos = 0;
33892         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33893             //if(cm.isHidden(i)) continue;
33894             var w = cm.getColumnWidth(i);
33895             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33896             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33897         }
33898         this.updateSplitters();
33899     },
33900
33901     generateRules : function(cm){
33902         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33903         Roo.util.CSS.removeStyleSheet(rulesId);
33904         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33905             var cid = cm.getColumnId(i);
33906             var align = '';
33907             if(cm.config[i].align){
33908                 align = 'text-align:'+cm.config[i].align+';';
33909             }
33910             var hidden = '';
33911             if(cm.isHidden(i)){
33912                 hidden = 'display:none;';
33913             }
33914             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33915             ruleBuf.push(
33916                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33917                     this.hdSelector, cid, " {\n", align, width, "}\n",
33918                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33919                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33920         }
33921         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33922     },
33923
33924     updateSplitters : function(){
33925         var cm = this.cm, s = this.getSplitters();
33926         if(s){ // splitters not created yet
33927             var pos = 0, locked = true;
33928             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33929                 if(cm.isHidden(i)) {
33930                     continue;
33931                 }
33932                 var w = cm.getColumnWidth(i); // make sure it's a number
33933                 if(!cm.isLocked(i) && locked){
33934                     pos = 0;
33935                     locked = false;
33936                 }
33937                 pos += w;
33938                 s[i].style.left = (pos-this.splitOffset) + "px";
33939             }
33940         }
33941     },
33942
33943     handleHiddenChange : function(colModel, colIndex, hidden){
33944         if(hidden){
33945             this.hideColumn(colIndex);
33946         }else{
33947             this.unhideColumn(colIndex);
33948         }
33949     },
33950
33951     hideColumn : function(colIndex){
33952         var cid = this.getColumnId(colIndex);
33953         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33954         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33955         if(Roo.isSafari){
33956             this.updateHeaders();
33957         }
33958         this.updateSplitters();
33959         this.layout();
33960     },
33961
33962     unhideColumn : function(colIndex){
33963         var cid = this.getColumnId(colIndex);
33964         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33965         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33966
33967         if(Roo.isSafari){
33968             this.updateHeaders();
33969         }
33970         this.updateSplitters();
33971         this.layout();
33972     },
33973
33974     insertRows : function(dm, firstRow, lastRow, isUpdate){
33975         if(firstRow == 0 && lastRow == dm.getCount()-1){
33976             this.refresh();
33977         }else{
33978             if(!isUpdate){
33979                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33980             }
33981             var s = this.getScrollState();
33982             var markup = this.renderRows(firstRow, lastRow);
33983             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33984             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33985             this.restoreScroll(s);
33986             if(!isUpdate){
33987                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33988                 this.syncRowHeights(firstRow, lastRow);
33989                 this.stripeRows(firstRow);
33990                 this.layout();
33991             }
33992         }
33993     },
33994
33995     bufferRows : function(markup, target, index){
33996         var before = null, trows = target.rows, tbody = target.tBodies[0];
33997         if(index < trows.length){
33998             before = trows[index];
33999         }
34000         var b = document.createElement("div");
34001         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34002         var rows = b.firstChild.rows;
34003         for(var i = 0, len = rows.length; i < len; i++){
34004             if(before){
34005                 tbody.insertBefore(rows[0], before);
34006             }else{
34007                 tbody.appendChild(rows[0]);
34008             }
34009         }
34010         b.innerHTML = "";
34011         b = null;
34012     },
34013
34014     deleteRows : function(dm, firstRow, lastRow){
34015         if(dm.getRowCount()<1){
34016             this.fireEvent("beforerefresh", this);
34017             this.mainBody.update("");
34018             this.lockedBody.update("");
34019             this.fireEvent("refresh", this);
34020         }else{
34021             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34022             var bt = this.getBodyTable();
34023             var tbody = bt.firstChild;
34024             var rows = bt.rows;
34025             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34026                 tbody.removeChild(rows[firstRow]);
34027             }
34028             this.stripeRows(firstRow);
34029             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34030         }
34031     },
34032
34033     updateRows : function(dataSource, firstRow, lastRow){
34034         var s = this.getScrollState();
34035         this.refresh();
34036         this.restoreScroll(s);
34037     },
34038
34039     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34040         if(!noRefresh){
34041            this.refresh();
34042         }
34043         this.updateHeaderSortState();
34044     },
34045
34046     getScrollState : function(){
34047         
34048         var sb = this.scroller.dom;
34049         return {left: sb.scrollLeft, top: sb.scrollTop};
34050     },
34051
34052     stripeRows : function(startRow){
34053         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34054             return;
34055         }
34056         startRow = startRow || 0;
34057         var rows = this.getBodyTable().rows;
34058         var lrows = this.getLockedTable().rows;
34059         var cls = ' x-grid-row-alt ';
34060         for(var i = startRow, len = rows.length; i < len; i++){
34061             var row = rows[i], lrow = lrows[i];
34062             var isAlt = ((i+1) % 2 == 0);
34063             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34064             if(isAlt == hasAlt){
34065                 continue;
34066             }
34067             if(isAlt){
34068                 row.className += " x-grid-row-alt";
34069             }else{
34070                 row.className = row.className.replace("x-grid-row-alt", "");
34071             }
34072             if(lrow){
34073                 lrow.className = row.className;
34074             }
34075         }
34076     },
34077
34078     restoreScroll : function(state){
34079         //Roo.log('GridView.restoreScroll');
34080         var sb = this.scroller.dom;
34081         sb.scrollLeft = state.left;
34082         sb.scrollTop = state.top;
34083         this.syncScroll();
34084     },
34085
34086     syncScroll : function(){
34087         //Roo.log('GridView.syncScroll');
34088         var sb = this.scroller.dom;
34089         var sh = this.mainHd.dom;
34090         var bs = this.mainBody.dom;
34091         var lv = this.lockedBody.dom;
34092         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34093         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34094     },
34095
34096     handleScroll : function(e){
34097         this.syncScroll();
34098         var sb = this.scroller.dom;
34099         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34100         e.stopEvent();
34101     },
34102
34103     handleWheel : function(e){
34104         var d = e.getWheelDelta();
34105         this.scroller.dom.scrollTop -= d*22;
34106         // set this here to prevent jumpy scrolling on large tables
34107         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34108         e.stopEvent();
34109     },
34110
34111     renderRows : function(startRow, endRow){
34112         // pull in all the crap needed to render rows
34113         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34114         var colCount = cm.getColumnCount();
34115
34116         if(ds.getCount() < 1){
34117             return ["", ""];
34118         }
34119
34120         // build a map for all the columns
34121         var cs = [];
34122         for(var i = 0; i < colCount; i++){
34123             var name = cm.getDataIndex(i);
34124             cs[i] = {
34125                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34126                 renderer : cm.getRenderer(i),
34127                 id : cm.getColumnId(i),
34128                 locked : cm.isLocked(i),
34129                 has_editor : cm.isCellEditable(i)
34130             };
34131         }
34132
34133         startRow = startRow || 0;
34134         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34135
34136         // records to render
34137         var rs = ds.getRange(startRow, endRow);
34138
34139         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34140     },
34141
34142     // As much as I hate to duplicate code, this was branched because FireFox really hates
34143     // [].join("") on strings. The performance difference was substantial enough to
34144     // branch this function
34145     doRender : Roo.isGecko ?
34146             function(cs, rs, ds, startRow, colCount, stripe){
34147                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34148                 // buffers
34149                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34150                 
34151                 var hasListener = this.grid.hasListener('rowclass');
34152                 var rowcfg = {};
34153                 for(var j = 0, len = rs.length; j < len; j++){
34154                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34155                     for(var i = 0; i < colCount; i++){
34156                         c = cs[i];
34157                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34158                         p.id = c.id;
34159                         p.css = p.attr = "";
34160                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34161                         if(p.value == undefined || p.value === "") {
34162                             p.value = "&#160;";
34163                         }
34164                         if(c.has_editor){
34165                             p.css += ' x-grid-editable-cell';
34166                         }
34167                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34168                             p.css +=  ' x-grid-dirty-cell';
34169                         }
34170                         var markup = ct.apply(p);
34171                         if(!c.locked){
34172                             cb+= markup;
34173                         }else{
34174                             lcb+= markup;
34175                         }
34176                     }
34177                     var alt = [];
34178                     if(stripe && ((rowIndex+1) % 2 == 0)){
34179                         alt.push("x-grid-row-alt")
34180                     }
34181                     if(r.dirty){
34182                         alt.push(  " x-grid-dirty-row");
34183                     }
34184                     rp.cells = lcb;
34185                     if(this.getRowClass){
34186                         alt.push(this.getRowClass(r, rowIndex));
34187                     }
34188                     if (hasListener) {
34189                         rowcfg = {
34190                              
34191                             record: r,
34192                             rowIndex : rowIndex,
34193                             rowClass : ''
34194                         };
34195                         this.grid.fireEvent('rowclass', this, rowcfg);
34196                         alt.push(rowcfg.rowClass);
34197                     }
34198                     rp.alt = alt.join(" ");
34199                     lbuf+= rt.apply(rp);
34200                     rp.cells = cb;
34201                     buf+=  rt.apply(rp);
34202                 }
34203                 return [lbuf, buf];
34204             } :
34205             function(cs, rs, ds, startRow, colCount, stripe){
34206                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34207                 // buffers
34208                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34209                 var hasListener = this.grid.hasListener('rowclass');
34210  
34211                 var rowcfg = {};
34212                 for(var j = 0, len = rs.length; j < len; j++){
34213                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34214                     for(var i = 0; i < colCount; i++){
34215                         c = cs[i];
34216                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34217                         p.id = c.id;
34218                         p.css = p.attr = "";
34219                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34220                         if(p.value == undefined || p.value === "") {
34221                             p.value = "&#160;";
34222                         }
34223                         //Roo.log(c);
34224                          if(c.has_editor){
34225                             p.css += ' x-grid-editable-cell';
34226                         }
34227                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34228                             p.css += ' x-grid-dirty-cell' 
34229                         }
34230                         
34231                         var markup = ct.apply(p);
34232                         if(!c.locked){
34233                             cb[cb.length] = markup;
34234                         }else{
34235                             lcb[lcb.length] = markup;
34236                         }
34237                     }
34238                     var alt = [];
34239                     if(stripe && ((rowIndex+1) % 2 == 0)){
34240                         alt.push( "x-grid-row-alt");
34241                     }
34242                     if(r.dirty){
34243                         alt.push(" x-grid-dirty-row");
34244                     }
34245                     rp.cells = lcb;
34246                     if(this.getRowClass){
34247                         alt.push( this.getRowClass(r, rowIndex));
34248                     }
34249                     if (hasListener) {
34250                         rowcfg = {
34251                              
34252                             record: r,
34253                             rowIndex : rowIndex,
34254                             rowClass : ''
34255                         };
34256                         this.grid.fireEvent('rowclass', this, rowcfg);
34257                         alt.push(rowcfg.rowClass);
34258                     }
34259                     
34260                     rp.alt = alt.join(" ");
34261                     rp.cells = lcb.join("");
34262                     lbuf[lbuf.length] = rt.apply(rp);
34263                     rp.cells = cb.join("");
34264                     buf[buf.length] =  rt.apply(rp);
34265                 }
34266                 return [lbuf.join(""), buf.join("")];
34267             },
34268
34269     renderBody : function(){
34270         var markup = this.renderRows();
34271         var bt = this.templates.body;
34272         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34273     },
34274
34275     /**
34276      * Refreshes the grid
34277      * @param {Boolean} headersToo
34278      */
34279     refresh : function(headersToo){
34280         this.fireEvent("beforerefresh", this);
34281         this.grid.stopEditing();
34282         var result = this.renderBody();
34283         this.lockedBody.update(result[0]);
34284         this.mainBody.update(result[1]);
34285         if(headersToo === true){
34286             this.updateHeaders();
34287             this.updateColumns();
34288             this.updateSplitters();
34289             this.updateHeaderSortState();
34290         }
34291         this.syncRowHeights();
34292         this.layout();
34293         this.fireEvent("refresh", this);
34294     },
34295
34296     handleColumnMove : function(cm, oldIndex, newIndex){
34297         this.indexMap = null;
34298         var s = this.getScrollState();
34299         this.refresh(true);
34300         this.restoreScroll(s);
34301         this.afterMove(newIndex);
34302     },
34303
34304     afterMove : function(colIndex){
34305         if(this.enableMoveAnim && Roo.enableFx){
34306             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34307         }
34308         // if multisort - fix sortOrder, and reload..
34309         if (this.grid.dataSource.multiSort) {
34310             // the we can call sort again..
34311             var dm = this.grid.dataSource;
34312             var cm = this.grid.colModel;
34313             var so = [];
34314             for(var i = 0; i < cm.config.length; i++ ) {
34315                 
34316                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34317                     continue; // dont' bother, it's not in sort list or being set.
34318                 }
34319                 
34320                 so.push(cm.config[i].dataIndex);
34321             };
34322             dm.sortOrder = so;
34323             dm.load(dm.lastOptions);
34324             
34325             
34326         }
34327         
34328     },
34329
34330     updateCell : function(dm, rowIndex, dataIndex){
34331         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34332         if(typeof colIndex == "undefined"){ // not present in grid
34333             return;
34334         }
34335         var cm = this.grid.colModel;
34336         var cell = this.getCell(rowIndex, colIndex);
34337         var cellText = this.getCellText(rowIndex, colIndex);
34338
34339         var p = {
34340             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34341             id : cm.getColumnId(colIndex),
34342             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34343         };
34344         var renderer = cm.getRenderer(colIndex);
34345         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34346         if(typeof val == "undefined" || val === "") {
34347             val = "&#160;";
34348         }
34349         cellText.innerHTML = val;
34350         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34351         this.syncRowHeights(rowIndex, rowIndex);
34352     },
34353
34354     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34355         var maxWidth = 0;
34356         if(this.grid.autoSizeHeaders){
34357             var h = this.getHeaderCellMeasure(colIndex);
34358             maxWidth = Math.max(maxWidth, h.scrollWidth);
34359         }
34360         var tb, index;
34361         if(this.cm.isLocked(colIndex)){
34362             tb = this.getLockedTable();
34363             index = colIndex;
34364         }else{
34365             tb = this.getBodyTable();
34366             index = colIndex - this.cm.getLockedCount();
34367         }
34368         if(tb && tb.rows){
34369             var rows = tb.rows;
34370             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34371             for(var i = 0; i < stopIndex; i++){
34372                 var cell = rows[i].childNodes[index].firstChild;
34373                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34374             }
34375         }
34376         return maxWidth + /*margin for error in IE*/ 5;
34377     },
34378     /**
34379      * Autofit a column to its content.
34380      * @param {Number} colIndex
34381      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34382      */
34383      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34384          if(this.cm.isHidden(colIndex)){
34385              return; // can't calc a hidden column
34386          }
34387         if(forceMinSize){
34388             var cid = this.cm.getColumnId(colIndex);
34389             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34390            if(this.grid.autoSizeHeaders){
34391                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34392            }
34393         }
34394         var newWidth = this.calcColumnWidth(colIndex);
34395         this.cm.setColumnWidth(colIndex,
34396             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34397         if(!suppressEvent){
34398             this.grid.fireEvent("columnresize", colIndex, newWidth);
34399         }
34400     },
34401
34402     /**
34403      * Autofits all columns to their content and then expands to fit any extra space in the grid
34404      */
34405      autoSizeColumns : function(){
34406         var cm = this.grid.colModel;
34407         var colCount = cm.getColumnCount();
34408         for(var i = 0; i < colCount; i++){
34409             this.autoSizeColumn(i, true, true);
34410         }
34411         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34412             this.fitColumns();
34413         }else{
34414             this.updateColumns();
34415             this.layout();
34416         }
34417     },
34418
34419     /**
34420      * Autofits all columns to the grid's width proportionate with their current size
34421      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34422      */
34423     fitColumns : function(reserveScrollSpace){
34424         var cm = this.grid.colModel;
34425         var colCount = cm.getColumnCount();
34426         var cols = [];
34427         var width = 0;
34428         var i, w;
34429         for (i = 0; i < colCount; i++){
34430             if(!cm.isHidden(i) && !cm.isFixed(i)){
34431                 w = cm.getColumnWidth(i);
34432                 cols.push(i);
34433                 cols.push(w);
34434                 width += w;
34435             }
34436         }
34437         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34438         if(reserveScrollSpace){
34439             avail -= 17;
34440         }
34441         var frac = (avail - cm.getTotalWidth())/width;
34442         while (cols.length){
34443             w = cols.pop();
34444             i = cols.pop();
34445             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34446         }
34447         this.updateColumns();
34448         this.layout();
34449     },
34450
34451     onRowSelect : function(rowIndex){
34452         var row = this.getRowComposite(rowIndex);
34453         row.addClass("x-grid-row-selected");
34454     },
34455
34456     onRowDeselect : function(rowIndex){
34457         var row = this.getRowComposite(rowIndex);
34458         row.removeClass("x-grid-row-selected");
34459     },
34460
34461     onCellSelect : function(row, col){
34462         var cell = this.getCell(row, col);
34463         if(cell){
34464             Roo.fly(cell).addClass("x-grid-cell-selected");
34465         }
34466     },
34467
34468     onCellDeselect : function(row, col){
34469         var cell = this.getCell(row, col);
34470         if(cell){
34471             Roo.fly(cell).removeClass("x-grid-cell-selected");
34472         }
34473     },
34474
34475     updateHeaderSortState : function(){
34476         
34477         // sort state can be single { field: xxx, direction : yyy}
34478         // or   { xxx=>ASC , yyy : DESC ..... }
34479         
34480         var mstate = {};
34481         if (!this.ds.multiSort) { 
34482             var state = this.ds.getSortState();
34483             if(!state){
34484                 return;
34485             }
34486             mstate[state.field] = state.direction;
34487             // FIXME... - this is not used here.. but might be elsewhere..
34488             this.sortState = state;
34489             
34490         } else {
34491             mstate = this.ds.sortToggle;
34492         }
34493         //remove existing sort classes..
34494         
34495         var sc = this.sortClasses;
34496         var hds = this.el.select(this.headerSelector).removeClass(sc);
34497         
34498         for(var f in mstate) {
34499         
34500             var sortColumn = this.cm.findColumnIndex(f);
34501             
34502             if(sortColumn != -1){
34503                 var sortDir = mstate[f];        
34504                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34505             }
34506         }
34507         
34508          
34509         
34510     },
34511
34512
34513     handleHeaderClick : function(g, index,e){
34514         
34515         Roo.log("header click");
34516         
34517         if (Roo.isTouch) {
34518             // touch events on header are handled by context
34519             this.handleHdCtx(g,index,e);
34520             return;
34521         }
34522         
34523         
34524         if(this.headersDisabled){
34525             return;
34526         }
34527         var dm = g.dataSource, cm = g.colModel;
34528         if(!cm.isSortable(index)){
34529             return;
34530         }
34531         g.stopEditing();
34532         
34533         if (dm.multiSort) {
34534             // update the sortOrder
34535             var so = [];
34536             for(var i = 0; i < cm.config.length; i++ ) {
34537                 
34538                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34539                     continue; // dont' bother, it's not in sort list or being set.
34540                 }
34541                 
34542                 so.push(cm.config[i].dataIndex);
34543             };
34544             dm.sortOrder = so;
34545         }
34546         
34547         
34548         dm.sort(cm.getDataIndex(index));
34549     },
34550
34551
34552     destroy : function(){
34553         if(this.colMenu){
34554             this.colMenu.removeAll();
34555             Roo.menu.MenuMgr.unregister(this.colMenu);
34556             this.colMenu.getEl().remove();
34557             delete this.colMenu;
34558         }
34559         if(this.hmenu){
34560             this.hmenu.removeAll();
34561             Roo.menu.MenuMgr.unregister(this.hmenu);
34562             this.hmenu.getEl().remove();
34563             delete this.hmenu;
34564         }
34565         if(this.grid.enableColumnMove){
34566             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34567             if(dds){
34568                 for(var dd in dds){
34569                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34570                         var elid = dds[dd].dragElId;
34571                         dds[dd].unreg();
34572                         Roo.get(elid).remove();
34573                     } else if(dds[dd].config.isTarget){
34574                         dds[dd].proxyTop.remove();
34575                         dds[dd].proxyBottom.remove();
34576                         dds[dd].unreg();
34577                     }
34578                     if(Roo.dd.DDM.locationCache[dd]){
34579                         delete Roo.dd.DDM.locationCache[dd];
34580                     }
34581                 }
34582                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34583             }
34584         }
34585         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34586         this.bind(null, null);
34587         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34588     },
34589
34590     handleLockChange : function(){
34591         this.refresh(true);
34592     },
34593
34594     onDenyColumnLock : function(){
34595
34596     },
34597
34598     onDenyColumnHide : function(){
34599
34600     },
34601
34602     handleHdMenuClick : function(item){
34603         var index = this.hdCtxIndex;
34604         var cm = this.cm, ds = this.ds;
34605         switch(item.id){
34606             case "asc":
34607                 ds.sort(cm.getDataIndex(index), "ASC");
34608                 break;
34609             case "desc":
34610                 ds.sort(cm.getDataIndex(index), "DESC");
34611                 break;
34612             case "lock":
34613                 var lc = cm.getLockedCount();
34614                 if(cm.getColumnCount(true) <= lc+1){
34615                     this.onDenyColumnLock();
34616                     return;
34617                 }
34618                 if(lc != index){
34619                     cm.setLocked(index, true, true);
34620                     cm.moveColumn(index, lc);
34621                     this.grid.fireEvent("columnmove", index, lc);
34622                 }else{
34623                     cm.setLocked(index, true);
34624                 }
34625             break;
34626             case "unlock":
34627                 var lc = cm.getLockedCount();
34628                 if((lc-1) != index){
34629                     cm.setLocked(index, false, true);
34630                     cm.moveColumn(index, lc-1);
34631                     this.grid.fireEvent("columnmove", index, lc-1);
34632                 }else{
34633                     cm.setLocked(index, false);
34634                 }
34635             break;
34636             case 'wider': // used to expand cols on touch..
34637             case 'narrow':
34638                 var cw = cm.getColumnWidth(index);
34639                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34640                 cw = Math.max(0, cw);
34641                 cw = Math.min(cw,4000);
34642                 cm.setColumnWidth(index, cw);
34643                 break;
34644                 
34645             default:
34646                 index = cm.getIndexById(item.id.substr(4));
34647                 if(index != -1){
34648                     if(item.checked && cm.getColumnCount(true) <= 1){
34649                         this.onDenyColumnHide();
34650                         return false;
34651                     }
34652                     cm.setHidden(index, item.checked);
34653                 }
34654         }
34655         return true;
34656     },
34657
34658     beforeColMenuShow : function(){
34659         var cm = this.cm,  colCount = cm.getColumnCount();
34660         this.colMenu.removeAll();
34661         for(var i = 0; i < colCount; i++){
34662             this.colMenu.add(new Roo.menu.CheckItem({
34663                 id: "col-"+cm.getColumnId(i),
34664                 text: cm.getColumnHeader(i),
34665                 checked: !cm.isHidden(i),
34666                 hideOnClick:false
34667             }));
34668         }
34669     },
34670
34671     handleHdCtx : function(g, index, e){
34672         e.stopEvent();
34673         var hd = this.getHeaderCell(index);
34674         this.hdCtxIndex = index;
34675         var ms = this.hmenu.items, cm = this.cm;
34676         ms.get("asc").setDisabled(!cm.isSortable(index));
34677         ms.get("desc").setDisabled(!cm.isSortable(index));
34678         if(this.grid.enableColLock !== false){
34679             ms.get("lock").setDisabled(cm.isLocked(index));
34680             ms.get("unlock").setDisabled(!cm.isLocked(index));
34681         }
34682         this.hmenu.show(hd, "tl-bl");
34683     },
34684
34685     handleHdOver : function(e){
34686         var hd = this.findHeaderCell(e.getTarget());
34687         if(hd && !this.headersDisabled){
34688             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34689                this.fly(hd).addClass("x-grid-hd-over");
34690             }
34691         }
34692     },
34693
34694     handleHdOut : function(e){
34695         var hd = this.findHeaderCell(e.getTarget());
34696         if(hd){
34697             this.fly(hd).removeClass("x-grid-hd-over");
34698         }
34699     },
34700
34701     handleSplitDblClick : function(e, t){
34702         var i = this.getCellIndex(t);
34703         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34704             this.autoSizeColumn(i, true);
34705             this.layout();
34706         }
34707     },
34708
34709     render : function(){
34710
34711         var cm = this.cm;
34712         var colCount = cm.getColumnCount();
34713
34714         if(this.grid.monitorWindowResize === true){
34715             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34716         }
34717         var header = this.renderHeaders();
34718         var body = this.templates.body.apply({rows:""});
34719         var html = this.templates.master.apply({
34720             lockedBody: body,
34721             body: body,
34722             lockedHeader: header[0],
34723             header: header[1]
34724         });
34725
34726         //this.updateColumns();
34727
34728         this.grid.getGridEl().dom.innerHTML = html;
34729
34730         this.initElements();
34731         
34732         // a kludge to fix the random scolling effect in webkit
34733         this.el.on("scroll", function() {
34734             this.el.dom.scrollTop=0; // hopefully not recursive..
34735         },this);
34736
34737         this.scroller.on("scroll", this.handleScroll, this);
34738         this.lockedBody.on("mousewheel", this.handleWheel, this);
34739         this.mainBody.on("mousewheel", this.handleWheel, this);
34740
34741         this.mainHd.on("mouseover", this.handleHdOver, this);
34742         this.mainHd.on("mouseout", this.handleHdOut, this);
34743         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34744                 {delegate: "."+this.splitClass});
34745
34746         this.lockedHd.on("mouseover", this.handleHdOver, this);
34747         this.lockedHd.on("mouseout", this.handleHdOut, this);
34748         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34749                 {delegate: "."+this.splitClass});
34750
34751         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34752             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34753         }
34754
34755         this.updateSplitters();
34756
34757         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34758             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34759             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34760         }
34761
34762         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34763             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34764             this.hmenu.add(
34765                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34766                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34767             );
34768             if(this.grid.enableColLock !== false){
34769                 this.hmenu.add('-',
34770                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34771                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34772                 );
34773             }
34774             if (Roo.isTouch) {
34775                  this.hmenu.add('-',
34776                     {id:"wider", text: this.columnsWiderText},
34777                     {id:"narrow", text: this.columnsNarrowText }
34778                 );
34779                 
34780                  
34781             }
34782             
34783             if(this.grid.enableColumnHide !== false){
34784
34785                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34786                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34787                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34788
34789                 this.hmenu.add('-',
34790                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34791                 );
34792             }
34793             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34794
34795             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34796         }
34797
34798         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34799             this.dd = new Roo.grid.GridDragZone(this.grid, {
34800                 ddGroup : this.grid.ddGroup || 'GridDD'
34801             });
34802             
34803         }
34804
34805         /*
34806         for(var i = 0; i < colCount; i++){
34807             if(cm.isHidden(i)){
34808                 this.hideColumn(i);
34809             }
34810             if(cm.config[i].align){
34811                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34812                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34813             }
34814         }*/
34815         
34816         this.updateHeaderSortState();
34817
34818         this.beforeInitialResize();
34819         this.layout(true);
34820
34821         // two part rendering gives faster view to the user
34822         this.renderPhase2.defer(1, this);
34823     },
34824
34825     renderPhase2 : function(){
34826         // render the rows now
34827         this.refresh();
34828         if(this.grid.autoSizeColumns){
34829             this.autoSizeColumns();
34830         }
34831     },
34832
34833     beforeInitialResize : function(){
34834
34835     },
34836
34837     onColumnSplitterMoved : function(i, w){
34838         this.userResized = true;
34839         var cm = this.grid.colModel;
34840         cm.setColumnWidth(i, w, true);
34841         var cid = cm.getColumnId(i);
34842         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34843         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34844         this.updateSplitters();
34845         this.layout();
34846         this.grid.fireEvent("columnresize", i, w);
34847     },
34848
34849     syncRowHeights : function(startIndex, endIndex){
34850         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34851             startIndex = startIndex || 0;
34852             var mrows = this.getBodyTable().rows;
34853             var lrows = this.getLockedTable().rows;
34854             var len = mrows.length-1;
34855             endIndex = Math.min(endIndex || len, len);
34856             for(var i = startIndex; i <= endIndex; i++){
34857                 var m = mrows[i], l = lrows[i];
34858                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34859                 m.style.height = l.style.height = h + "px";
34860             }
34861         }
34862     },
34863
34864     layout : function(initialRender, is2ndPass)
34865     {
34866         var g = this.grid;
34867         var auto = g.autoHeight;
34868         var scrollOffset = 16;
34869         var c = g.getGridEl(), cm = this.cm,
34870                 expandCol = g.autoExpandColumn,
34871                 gv = this;
34872         //c.beginMeasure();
34873
34874         if(!c.dom.offsetWidth){ // display:none?
34875             if(initialRender){
34876                 this.lockedWrap.show();
34877                 this.mainWrap.show();
34878             }
34879             return;
34880         }
34881
34882         var hasLock = this.cm.isLocked(0);
34883
34884         var tbh = this.headerPanel.getHeight();
34885         var bbh = this.footerPanel.getHeight();
34886
34887         if(auto){
34888             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34889             var newHeight = ch + c.getBorderWidth("tb");
34890             if(g.maxHeight){
34891                 newHeight = Math.min(g.maxHeight, newHeight);
34892             }
34893             c.setHeight(newHeight);
34894         }
34895
34896         if(g.autoWidth){
34897             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34898         }
34899
34900         var s = this.scroller;
34901
34902         var csize = c.getSize(true);
34903
34904         this.el.setSize(csize.width, csize.height);
34905
34906         this.headerPanel.setWidth(csize.width);
34907         this.footerPanel.setWidth(csize.width);
34908
34909         var hdHeight = this.mainHd.getHeight();
34910         var vw = csize.width;
34911         var vh = csize.height - (tbh + bbh);
34912
34913         s.setSize(vw, vh);
34914
34915         var bt = this.getBodyTable();
34916         
34917         if(cm.getLockedCount() == cm.config.length){
34918             bt = this.getLockedTable();
34919         }
34920         
34921         var ltWidth = hasLock ?
34922                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34923
34924         var scrollHeight = bt.offsetHeight;
34925         var scrollWidth = ltWidth + bt.offsetWidth;
34926         var vscroll = false, hscroll = false;
34927
34928         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34929
34930         var lw = this.lockedWrap, mw = this.mainWrap;
34931         var lb = this.lockedBody, mb = this.mainBody;
34932
34933         setTimeout(function(){
34934             var t = s.dom.offsetTop;
34935             var w = s.dom.clientWidth,
34936                 h = s.dom.clientHeight;
34937
34938             lw.setTop(t);
34939             lw.setSize(ltWidth, h);
34940
34941             mw.setLeftTop(ltWidth, t);
34942             mw.setSize(w-ltWidth, h);
34943
34944             lb.setHeight(h-hdHeight);
34945             mb.setHeight(h-hdHeight);
34946
34947             if(is2ndPass !== true && !gv.userResized && expandCol){
34948                 // high speed resize without full column calculation
34949                 
34950                 var ci = cm.getIndexById(expandCol);
34951                 if (ci < 0) {
34952                     ci = cm.findColumnIndex(expandCol);
34953                 }
34954                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34955                 var expandId = cm.getColumnId(ci);
34956                 var  tw = cm.getTotalWidth(false);
34957                 var currentWidth = cm.getColumnWidth(ci);
34958                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34959                 if(currentWidth != cw){
34960                     cm.setColumnWidth(ci, cw, true);
34961                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34962                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34963                     gv.updateSplitters();
34964                     gv.layout(false, true);
34965                 }
34966             }
34967
34968             if(initialRender){
34969                 lw.show();
34970                 mw.show();
34971             }
34972             //c.endMeasure();
34973         }, 10);
34974     },
34975
34976     onWindowResize : function(){
34977         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34978             return;
34979         }
34980         this.layout();
34981     },
34982
34983     appendFooter : function(parentEl){
34984         return null;
34985     },
34986
34987     sortAscText : "Sort Ascending",
34988     sortDescText : "Sort Descending",
34989     lockText : "Lock Column",
34990     unlockText : "Unlock Column",
34991     columnsText : "Columns",
34992  
34993     columnsWiderText : "Wider",
34994     columnsNarrowText : "Thinner"
34995 });
34996
34997
34998 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34999     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35000     this.proxy.el.addClass('x-grid3-col-dd');
35001 };
35002
35003 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35004     handleMouseDown : function(e){
35005
35006     },
35007
35008     callHandleMouseDown : function(e){
35009         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35010     }
35011 });
35012 /*
35013  * Based on:
35014  * Ext JS Library 1.1.1
35015  * Copyright(c) 2006-2007, Ext JS, LLC.
35016  *
35017  * Originally Released Under LGPL - original licence link has changed is not relivant.
35018  *
35019  * Fork - LGPL
35020  * <script type="text/javascript">
35021  */
35022  /**
35023  * @extends Roo.dd.DDProxy
35024  * @class Roo.grid.SplitDragZone
35025  * Support for Column Header resizing
35026  * @constructor
35027  * @param {Object} config
35028  */
35029 // private
35030 // This is a support class used internally by the Grid components
35031 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35032     this.grid = grid;
35033     this.view = grid.getView();
35034     this.proxy = this.view.resizeProxy;
35035     Roo.grid.SplitDragZone.superclass.constructor.call(
35036         this,
35037         hd, // ID
35038         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35039         {  // CONFIG
35040             dragElId : Roo.id(this.proxy.dom),
35041             resizeFrame:false
35042         }
35043     );
35044     
35045     this.setHandleElId(Roo.id(hd));
35046     if (hd2 !== false) {
35047         this.setOuterHandleElId(Roo.id(hd2));
35048     }
35049     
35050     this.scroll = false;
35051 };
35052 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35053     fly: Roo.Element.fly,
35054
35055     b4StartDrag : function(x, y){
35056         this.view.headersDisabled = true;
35057         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35058                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35059         );
35060         this.proxy.setHeight(h);
35061         
35062         // for old system colWidth really stored the actual width?
35063         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35064         // which in reality did not work.. - it worked only for fixed sizes
35065         // for resizable we need to use actual sizes.
35066         var w = this.cm.getColumnWidth(this.cellIndex);
35067         if (!this.view.mainWrap) {
35068             // bootstrap.
35069             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35070         }
35071         
35072         
35073         
35074         // this was w-this.grid.minColumnWidth;
35075         // doesnt really make sense? - w = thie curren width or the rendered one?
35076         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35077         this.resetConstraints();
35078         this.setXConstraint(minw, 1000);
35079         this.setYConstraint(0, 0);
35080         this.minX = x - minw;
35081         this.maxX = x + 1000;
35082         this.startPos = x;
35083         if (!this.view.mainWrap) { // this is Bootstrap code..
35084             this.getDragEl().style.display='block';
35085         }
35086         
35087         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35088     },
35089
35090
35091     handleMouseDown : function(e){
35092         ev = Roo.EventObject.setEvent(e);
35093         var t = this.fly(ev.getTarget());
35094         if(t.hasClass("x-grid-split")){
35095             this.cellIndex = this.view.getCellIndex(t.dom);
35096             this.split = t.dom;
35097             this.cm = this.grid.colModel;
35098             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35099                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35100             }
35101         }
35102     },
35103
35104     endDrag : function(e){
35105         this.view.headersDisabled = false;
35106         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35107         var diff = endX - this.startPos;
35108         // 
35109         var w = this.cm.getColumnWidth(this.cellIndex);
35110         if (!this.view.mainWrap) {
35111             w = 0;
35112         }
35113         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35114     },
35115
35116     autoOffset : function(){
35117         this.setDelta(0,0);
35118     }
35119 });/*
35120  * Based on:
35121  * Ext JS Library 1.1.1
35122  * Copyright(c) 2006-2007, Ext JS, LLC.
35123  *
35124  * Originally Released Under LGPL - original licence link has changed is not relivant.
35125  *
35126  * Fork - LGPL
35127  * <script type="text/javascript">
35128  */
35129  
35130 // private
35131 // This is a support class used internally by the Grid components
35132 Roo.grid.GridDragZone = function(grid, config){
35133     this.view = grid.getView();
35134     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35135     if(this.view.lockedBody){
35136         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35137         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35138     }
35139     this.scroll = false;
35140     this.grid = grid;
35141     this.ddel = document.createElement('div');
35142     this.ddel.className = 'x-grid-dd-wrap';
35143 };
35144
35145 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35146     ddGroup : "GridDD",
35147
35148     getDragData : function(e){
35149         var t = Roo.lib.Event.getTarget(e);
35150         var rowIndex = this.view.findRowIndex(t);
35151         var sm = this.grid.selModel;
35152             
35153         //Roo.log(rowIndex);
35154         
35155         if (sm.getSelectedCell) {
35156             // cell selection..
35157             if (!sm.getSelectedCell()) {
35158                 return false;
35159             }
35160             if (rowIndex != sm.getSelectedCell()[0]) {
35161                 return false;
35162             }
35163         
35164         }
35165         if (sm.getSelections && sm.getSelections().length < 1) {
35166             return false;
35167         }
35168         
35169         
35170         // before it used to all dragging of unseleted... - now we dont do that.
35171         if(rowIndex !== false){
35172             
35173             // if editorgrid.. 
35174             
35175             
35176             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35177                
35178             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35179               //  
35180             //}
35181             if (e.hasModifier()){
35182                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35183             }
35184             
35185             Roo.log("getDragData");
35186             
35187             return {
35188                 grid: this.grid,
35189                 ddel: this.ddel,
35190                 rowIndex: rowIndex,
35191                 selections: sm.getSelections ? sm.getSelections() : (
35192                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35193             };
35194         }
35195         return false;
35196     },
35197     
35198     
35199     onInitDrag : function(e){
35200         var data = this.dragData;
35201         this.ddel.innerHTML = this.grid.getDragDropText();
35202         this.proxy.update(this.ddel);
35203         // fire start drag?
35204     },
35205
35206     afterRepair : function(){
35207         this.dragging = false;
35208     },
35209
35210     getRepairXY : function(e, data){
35211         return false;
35212     },
35213
35214     onEndDrag : function(data, e){
35215         // fire end drag?
35216     },
35217
35218     onValidDrop : function(dd, e, id){
35219         // fire drag drop?
35220         this.hideProxy();
35221     },
35222
35223     beforeInvalidDrop : function(e, id){
35224
35225     }
35226 });/*
35227  * Based on:
35228  * Ext JS Library 1.1.1
35229  * Copyright(c) 2006-2007, Ext JS, LLC.
35230  *
35231  * Originally Released Under LGPL - original licence link has changed is not relivant.
35232  *
35233  * Fork - LGPL
35234  * <script type="text/javascript">
35235  */
35236  
35237
35238 /**
35239  * @class Roo.grid.ColumnModel
35240  * @extends Roo.util.Observable
35241  * This is the default implementation of a ColumnModel used by the Grid. It defines
35242  * the columns in the grid.
35243  * <br>Usage:<br>
35244  <pre><code>
35245  var colModel = new Roo.grid.ColumnModel([
35246         {header: "Ticker", width: 60, sortable: true, locked: true},
35247         {header: "Company Name", width: 150, sortable: true},
35248         {header: "Market Cap.", width: 100, sortable: true},
35249         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35250         {header: "Employees", width: 100, sortable: true, resizable: false}
35251  ]);
35252  </code></pre>
35253  * <p>
35254  
35255  * The config options listed for this class are options which may appear in each
35256  * individual column definition.
35257  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35258  * @constructor
35259  * @param {Object} config An Array of column config objects. See this class's
35260  * config objects for details.
35261 */
35262 Roo.grid.ColumnModel = function(config){
35263         /**
35264      * The config passed into the constructor
35265      */
35266     this.config = []; //config;
35267     this.lookup = {};
35268
35269     // if no id, create one
35270     // if the column does not have a dataIndex mapping,
35271     // map it to the order it is in the config
35272     for(var i = 0, len = config.length; i < len; i++){
35273         this.addColumn(config[i]);
35274         
35275     }
35276
35277     /**
35278      * The width of columns which have no width specified (defaults to 100)
35279      * @type Number
35280      */
35281     this.defaultWidth = 100;
35282
35283     /**
35284      * Default sortable of columns which have no sortable specified (defaults to false)
35285      * @type Boolean
35286      */
35287     this.defaultSortable = false;
35288
35289     this.addEvents({
35290         /**
35291              * @event widthchange
35292              * Fires when the width of a column changes.
35293              * @param {ColumnModel} this
35294              * @param {Number} columnIndex The column index
35295              * @param {Number} newWidth The new width
35296              */
35297             "widthchange": true,
35298         /**
35299              * @event headerchange
35300              * Fires when the text of a header changes.
35301              * @param {ColumnModel} this
35302              * @param {Number} columnIndex The column index
35303              * @param {Number} newText The new header text
35304              */
35305             "headerchange": true,
35306         /**
35307              * @event hiddenchange
35308              * Fires when a column is hidden or "unhidden".
35309              * @param {ColumnModel} this
35310              * @param {Number} columnIndex The column index
35311              * @param {Boolean} hidden true if hidden, false otherwise
35312              */
35313             "hiddenchange": true,
35314             /**
35315          * @event columnmoved
35316          * Fires when a column is moved.
35317          * @param {ColumnModel} this
35318          * @param {Number} oldIndex
35319          * @param {Number} newIndex
35320          */
35321         "columnmoved" : true,
35322         /**
35323          * @event columlockchange
35324          * Fires when a column's locked state is changed
35325          * @param {ColumnModel} this
35326          * @param {Number} colIndex
35327          * @param {Boolean} locked true if locked
35328          */
35329         "columnlockchange" : true
35330     });
35331     Roo.grid.ColumnModel.superclass.constructor.call(this);
35332 };
35333 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35334     /**
35335      * @cfg {String} header The header text to display in the Grid view.
35336      */
35337         /**
35338      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35339      */
35340         /**
35341      * @cfg {String} smHeader Header at Bootsrap Small width
35342      */
35343         /**
35344      * @cfg {String} mdHeader Header at Bootsrap Medium width
35345      */
35346         /**
35347      * @cfg {String} lgHeader Header at Bootsrap Large width
35348      */
35349         /**
35350      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35351      */
35352     /**
35353      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35354      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35355      * specified, the column's index is used as an index into the Record's data Array.
35356      */
35357     /**
35358      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35359      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35360      */
35361     /**
35362      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35363      * Defaults to the value of the {@link #defaultSortable} property.
35364      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35365      */
35366     /**
35367      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35368      */
35369     /**
35370      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35371      */
35372     /**
35373      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35374      */
35375     /**
35376      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35377      */
35378     /**
35379      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35380      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35381      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35382      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35383      */
35384        /**
35385      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35386      */
35387     /**
35388      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35389      */
35390     /**
35391      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35392      */
35393     /**
35394      * @cfg {String} cursor (Optional)
35395      */
35396     /**
35397      * @cfg {String} tooltip (Optional)
35398      */
35399     /**
35400      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35401      */
35402     /**
35403      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35404      */
35405     /**
35406      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35407      */
35408     /**
35409      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35410      */
35411         /**
35412      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35413      */
35414     /**
35415      * Returns the id of the column at the specified index.
35416      * @param {Number} index The column index
35417      * @return {String} the id
35418      */
35419     getColumnId : function(index){
35420         return this.config[index].id;
35421     },
35422
35423     /**
35424      * Returns the column for a specified id.
35425      * @param {String} id The column id
35426      * @return {Object} the column
35427      */
35428     getColumnById : function(id){
35429         return this.lookup[id];
35430     },
35431
35432     
35433     /**
35434      * Returns the column Object for a specified dataIndex.
35435      * @param {String} dataIndex The column dataIndex
35436      * @return {Object|Boolean} the column or false if not found
35437      */
35438     getColumnByDataIndex: function(dataIndex){
35439         var index = this.findColumnIndex(dataIndex);
35440         return index > -1 ? this.config[index] : false;
35441     },
35442     
35443     /**
35444      * Returns the index for a specified column id.
35445      * @param {String} id The column id
35446      * @return {Number} the index, or -1 if not found
35447      */
35448     getIndexById : function(id){
35449         for(var i = 0, len = this.config.length; i < len; i++){
35450             if(this.config[i].id == id){
35451                 return i;
35452             }
35453         }
35454         return -1;
35455     },
35456     
35457     /**
35458      * Returns the index for a specified column dataIndex.
35459      * @param {String} dataIndex The column dataIndex
35460      * @return {Number} the index, or -1 if not found
35461      */
35462     
35463     findColumnIndex : function(dataIndex){
35464         for(var i = 0, len = this.config.length; i < len; i++){
35465             if(this.config[i].dataIndex == dataIndex){
35466                 return i;
35467             }
35468         }
35469         return -1;
35470     },
35471     
35472     
35473     moveColumn : function(oldIndex, newIndex){
35474         var c = this.config[oldIndex];
35475         this.config.splice(oldIndex, 1);
35476         this.config.splice(newIndex, 0, c);
35477         this.dataMap = null;
35478         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35479     },
35480
35481     isLocked : function(colIndex){
35482         return this.config[colIndex].locked === true;
35483     },
35484
35485     setLocked : function(colIndex, value, suppressEvent){
35486         if(this.isLocked(colIndex) == value){
35487             return;
35488         }
35489         this.config[colIndex].locked = value;
35490         if(!suppressEvent){
35491             this.fireEvent("columnlockchange", this, colIndex, value);
35492         }
35493     },
35494
35495     getTotalLockedWidth : function(){
35496         var totalWidth = 0;
35497         for(var i = 0; i < this.config.length; i++){
35498             if(this.isLocked(i) && !this.isHidden(i)){
35499                 this.totalWidth += this.getColumnWidth(i);
35500             }
35501         }
35502         return totalWidth;
35503     },
35504
35505     getLockedCount : function(){
35506         for(var i = 0, len = this.config.length; i < len; i++){
35507             if(!this.isLocked(i)){
35508                 return i;
35509             }
35510         }
35511         
35512         return this.config.length;
35513     },
35514
35515     /**
35516      * Returns the number of columns.
35517      * @return {Number}
35518      */
35519     getColumnCount : function(visibleOnly){
35520         if(visibleOnly === true){
35521             var c = 0;
35522             for(var i = 0, len = this.config.length; i < len; i++){
35523                 if(!this.isHidden(i)){
35524                     c++;
35525                 }
35526             }
35527             return c;
35528         }
35529         return this.config.length;
35530     },
35531
35532     /**
35533      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35534      * @param {Function} fn
35535      * @param {Object} scope (optional)
35536      * @return {Array} result
35537      */
35538     getColumnsBy : function(fn, scope){
35539         var r = [];
35540         for(var i = 0, len = this.config.length; i < len; i++){
35541             var c = this.config[i];
35542             if(fn.call(scope||this, c, i) === true){
35543                 r[r.length] = c;
35544             }
35545         }
35546         return r;
35547     },
35548
35549     /**
35550      * Returns true if the specified column is sortable.
35551      * @param {Number} col The column index
35552      * @return {Boolean}
35553      */
35554     isSortable : function(col){
35555         if(typeof this.config[col].sortable == "undefined"){
35556             return this.defaultSortable;
35557         }
35558         return this.config[col].sortable;
35559     },
35560
35561     /**
35562      * Returns the rendering (formatting) function defined for the column.
35563      * @param {Number} col The column index.
35564      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35565      */
35566     getRenderer : function(col){
35567         if(!this.config[col].renderer){
35568             return Roo.grid.ColumnModel.defaultRenderer;
35569         }
35570         return this.config[col].renderer;
35571     },
35572
35573     /**
35574      * Sets the rendering (formatting) function for a column.
35575      * @param {Number} col The column index
35576      * @param {Function} fn The function to use to process the cell's raw data
35577      * to return HTML markup for the grid view. The render function is called with
35578      * the following parameters:<ul>
35579      * <li>Data value.</li>
35580      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35581      * <li>css A CSS style string to apply to the table cell.</li>
35582      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35583      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35584      * <li>Row index</li>
35585      * <li>Column index</li>
35586      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35587      */
35588     setRenderer : function(col, fn){
35589         this.config[col].renderer = fn;
35590     },
35591
35592     /**
35593      * Returns the width for the specified column.
35594      * @param {Number} col The column index
35595      * @param (optional) {String} gridSize bootstrap width size.
35596      * @return {Number}
35597      */
35598     getColumnWidth : function(col, gridSize)
35599         {
35600                 var cfg = this.config[col];
35601                 
35602                 if (typeof(gridSize) == 'undefined') {
35603                         return cfg.width * 1 || this.defaultWidth;
35604                 }
35605                 if (gridSize === false) { // if we set it..
35606                         return cfg.width || false;
35607                 }
35608                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35609                 
35610                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35611                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35612                                 continue;
35613                         }
35614                         return cfg[ sizes[i] ];
35615                 }
35616                 return 1;
35617                 
35618     },
35619
35620     /**
35621      * Sets the width for a column.
35622      * @param {Number} col The column index
35623      * @param {Number} width The new width
35624      */
35625     setColumnWidth : function(col, width, suppressEvent){
35626         this.config[col].width = width;
35627         this.totalWidth = null;
35628         if(!suppressEvent){
35629              this.fireEvent("widthchange", this, col, width);
35630         }
35631     },
35632
35633     /**
35634      * Returns the total width of all columns.
35635      * @param {Boolean} includeHidden True to include hidden column widths
35636      * @return {Number}
35637      */
35638     getTotalWidth : function(includeHidden){
35639         if(!this.totalWidth){
35640             this.totalWidth = 0;
35641             for(var i = 0, len = this.config.length; i < len; i++){
35642                 if(includeHidden || !this.isHidden(i)){
35643                     this.totalWidth += this.getColumnWidth(i);
35644                 }
35645             }
35646         }
35647         return this.totalWidth;
35648     },
35649
35650     /**
35651      * Returns the header for the specified column.
35652      * @param {Number} col The column index
35653      * @return {String}
35654      */
35655     getColumnHeader : function(col){
35656         return this.config[col].header;
35657     },
35658
35659     /**
35660      * Sets the header for a column.
35661      * @param {Number} col The column index
35662      * @param {String} header The new header
35663      */
35664     setColumnHeader : function(col, header){
35665         this.config[col].header = header;
35666         this.fireEvent("headerchange", this, col, header);
35667     },
35668
35669     /**
35670      * Returns the tooltip for the specified column.
35671      * @param {Number} col The column index
35672      * @return {String}
35673      */
35674     getColumnTooltip : function(col){
35675             return this.config[col].tooltip;
35676     },
35677     /**
35678      * Sets the tooltip for a column.
35679      * @param {Number} col The column index
35680      * @param {String} tooltip The new tooltip
35681      */
35682     setColumnTooltip : function(col, tooltip){
35683             this.config[col].tooltip = tooltip;
35684     },
35685
35686     /**
35687      * Returns the dataIndex for the specified column.
35688      * @param {Number} col The column index
35689      * @return {Number}
35690      */
35691     getDataIndex : function(col){
35692         return this.config[col].dataIndex;
35693     },
35694
35695     /**
35696      * Sets the dataIndex for a column.
35697      * @param {Number} col The column index
35698      * @param {Number} dataIndex The new dataIndex
35699      */
35700     setDataIndex : function(col, dataIndex){
35701         this.config[col].dataIndex = dataIndex;
35702     },
35703
35704     
35705     
35706     /**
35707      * Returns true if the cell is editable.
35708      * @param {Number} colIndex The column index
35709      * @param {Number} rowIndex The row index - this is nto actually used..?
35710      * @return {Boolean}
35711      */
35712     isCellEditable : function(colIndex, rowIndex){
35713         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35714     },
35715
35716     /**
35717      * Returns the editor defined for the cell/column.
35718      * return false or null to disable editing.
35719      * @param {Number} colIndex The column index
35720      * @param {Number} rowIndex The row index
35721      * @return {Object}
35722      */
35723     getCellEditor : function(colIndex, rowIndex){
35724         return this.config[colIndex].editor;
35725     },
35726
35727     /**
35728      * Sets if a column is editable.
35729      * @param {Number} col The column index
35730      * @param {Boolean} editable True if the column is editable
35731      */
35732     setEditable : function(col, editable){
35733         this.config[col].editable = editable;
35734     },
35735
35736
35737     /**
35738      * Returns true if the column is hidden.
35739      * @param {Number} colIndex The column index
35740      * @return {Boolean}
35741      */
35742     isHidden : function(colIndex){
35743         return this.config[colIndex].hidden;
35744     },
35745
35746
35747     /**
35748      * Returns true if the column width cannot be changed
35749      */
35750     isFixed : function(colIndex){
35751         return this.config[colIndex].fixed;
35752     },
35753
35754     /**
35755      * Returns true if the column can be resized
35756      * @return {Boolean}
35757      */
35758     isResizable : function(colIndex){
35759         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35760     },
35761     /**
35762      * Sets if a column is hidden.
35763      * @param {Number} colIndex The column index
35764      * @param {Boolean} hidden True if the column is hidden
35765      */
35766     setHidden : function(colIndex, hidden){
35767         this.config[colIndex].hidden = hidden;
35768         this.totalWidth = null;
35769         this.fireEvent("hiddenchange", this, colIndex, hidden);
35770     },
35771
35772     /**
35773      * Sets the editor for a column.
35774      * @param {Number} col The column index
35775      * @param {Object} editor The editor object
35776      */
35777     setEditor : function(col, editor){
35778         this.config[col].editor = editor;
35779     },
35780     /**
35781      * Add a column (experimental...) - defaults to adding to the end..
35782      * @param {Object} config 
35783     */
35784     addColumn : function(c)
35785     {
35786     
35787         var i = this.config.length;
35788         this.config[i] = c;
35789         
35790         if(typeof c.dataIndex == "undefined"){
35791             c.dataIndex = i;
35792         }
35793         if(typeof c.renderer == "string"){
35794             c.renderer = Roo.util.Format[c.renderer];
35795         }
35796         if(typeof c.id == "undefined"){
35797             c.id = Roo.id();
35798         }
35799         if(c.editor && c.editor.xtype){
35800             c.editor  = Roo.factory(c.editor, Roo.grid);
35801         }
35802         if(c.editor && c.editor.isFormField){
35803             c.editor = new Roo.grid.GridEditor(c.editor);
35804         }
35805         this.lookup[c.id] = c;
35806     }
35807     
35808 });
35809
35810 Roo.grid.ColumnModel.defaultRenderer = function(value)
35811 {
35812     if(typeof value == "object") {
35813         return value;
35814     }
35815         if(typeof value == "string" && value.length < 1){
35816             return "&#160;";
35817         }
35818     
35819         return String.format("{0}", value);
35820 };
35821
35822 // Alias for backwards compatibility
35823 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35824 /*
35825  * Based on:
35826  * Ext JS Library 1.1.1
35827  * Copyright(c) 2006-2007, Ext JS, LLC.
35828  *
35829  * Originally Released Under LGPL - original licence link has changed is not relivant.
35830  *
35831  * Fork - LGPL
35832  * <script type="text/javascript">
35833  */
35834
35835 /**
35836  * @class Roo.grid.AbstractSelectionModel
35837  * @extends Roo.util.Observable
35838  * @abstract
35839  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35840  * implemented by descendant classes.  This class should not be directly instantiated.
35841  * @constructor
35842  */
35843 Roo.grid.AbstractSelectionModel = function(){
35844     this.locked = false;
35845     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35846 };
35847
35848 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35849     /** @ignore Called by the grid automatically. Do not call directly. */
35850     init : function(grid){
35851         this.grid = grid;
35852         this.initEvents();
35853     },
35854
35855     /**
35856      * Locks the selections.
35857      */
35858     lock : function(){
35859         this.locked = true;
35860     },
35861
35862     /**
35863      * Unlocks the selections.
35864      */
35865     unlock : function(){
35866         this.locked = false;
35867     },
35868
35869     /**
35870      * Returns true if the selections are locked.
35871      * @return {Boolean}
35872      */
35873     isLocked : function(){
35874         return this.locked;
35875     }
35876 });/*
35877  * Based on:
35878  * Ext JS Library 1.1.1
35879  * Copyright(c) 2006-2007, Ext JS, LLC.
35880  *
35881  * Originally Released Under LGPL - original licence link has changed is not relivant.
35882  *
35883  * Fork - LGPL
35884  * <script type="text/javascript">
35885  */
35886 /**
35887  * @extends Roo.grid.AbstractSelectionModel
35888  * @class Roo.grid.RowSelectionModel
35889  * The default SelectionModel used by {@link Roo.grid.Grid}.
35890  * It supports multiple selections and keyboard selection/navigation. 
35891  * @constructor
35892  * @param {Object} config
35893  */
35894 Roo.grid.RowSelectionModel = function(config){
35895     Roo.apply(this, config);
35896     this.selections = new Roo.util.MixedCollection(false, function(o){
35897         return o.id;
35898     });
35899
35900     this.last = false;
35901     this.lastActive = false;
35902
35903     this.addEvents({
35904         /**
35905         * @event selectionchange
35906         * Fires when the selection changes
35907         * @param {SelectionModel} this
35908         */
35909        "selectionchange" : true,
35910        /**
35911         * @event afterselectionchange
35912         * Fires after the selection changes (eg. by key press or clicking)
35913         * @param {SelectionModel} this
35914         */
35915        "afterselectionchange" : true,
35916        /**
35917         * @event beforerowselect
35918         * Fires when a row is selected being selected, return false to cancel.
35919         * @param {SelectionModel} this
35920         * @param {Number} rowIndex The selected index
35921         * @param {Boolean} keepExisting False if other selections will be cleared
35922         */
35923        "beforerowselect" : true,
35924        /**
35925         * @event rowselect
35926         * Fires when a row is selected.
35927         * @param {SelectionModel} this
35928         * @param {Number} rowIndex The selected index
35929         * @param {Roo.data.Record} r The record
35930         */
35931        "rowselect" : true,
35932        /**
35933         * @event rowdeselect
35934         * Fires when a row is deselected.
35935         * @param {SelectionModel} this
35936         * @param {Number} rowIndex The selected index
35937         */
35938         "rowdeselect" : true
35939     });
35940     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35941     this.locked = false;
35942 };
35943
35944 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35945     /**
35946      * @cfg {Boolean} singleSelect
35947      * True to allow selection of only one row at a time (defaults to false)
35948      */
35949     singleSelect : false,
35950
35951     // private
35952     initEvents : function(){
35953
35954         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35955             this.grid.on("mousedown", this.handleMouseDown, this);
35956         }else{ // allow click to work like normal
35957             this.grid.on("rowclick", this.handleDragableRowClick, this);
35958         }
35959         // bootstrap does not have a view..
35960         var view = this.grid.view ? this.grid.view : this.grid;
35961         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35962             "up" : function(e){
35963                 if(!e.shiftKey){
35964                     this.selectPrevious(e.shiftKey);
35965                 }else if(this.last !== false && this.lastActive !== false){
35966                     var last = this.last;
35967                     this.selectRange(this.last,  this.lastActive-1);
35968                     view.focusRow(this.lastActive);
35969                     if(last !== false){
35970                         this.last = last;
35971                     }
35972                 }else{
35973                     this.selectFirstRow();
35974                 }
35975                 this.fireEvent("afterselectionchange", this);
35976             },
35977             "down" : function(e){
35978                 if(!e.shiftKey){
35979                     this.selectNext(e.shiftKey);
35980                 }else if(this.last !== false && this.lastActive !== false){
35981                     var last = this.last;
35982                     this.selectRange(this.last,  this.lastActive+1);
35983                     view.focusRow(this.lastActive);
35984                     if(last !== false){
35985                         this.last = last;
35986                     }
35987                 }else{
35988                     this.selectFirstRow();
35989                 }
35990                 this.fireEvent("afterselectionchange", this);
35991             },
35992             scope: this
35993         });
35994
35995          
35996         view.on("refresh", this.onRefresh, this);
35997         view.on("rowupdated", this.onRowUpdated, this);
35998         view.on("rowremoved", this.onRemove, this);
35999     },
36000
36001     // private
36002     onRefresh : function(){
36003         var ds = this.grid.ds, i, v = this.grid.view;
36004         var s = this.selections;
36005         s.each(function(r){
36006             if((i = ds.indexOfId(r.id)) != -1){
36007                 v.onRowSelect(i);
36008                 s.add(ds.getAt(i)); // updating the selection relate data
36009             }else{
36010                 s.remove(r);
36011             }
36012         });
36013     },
36014
36015     // private
36016     onRemove : function(v, index, r){
36017         this.selections.remove(r);
36018     },
36019
36020     // private
36021     onRowUpdated : function(v, index, r){
36022         if(this.isSelected(r)){
36023             v.onRowSelect(index);
36024         }
36025     },
36026
36027     /**
36028      * Select records.
36029      * @param {Array} records The records to select
36030      * @param {Boolean} keepExisting (optional) True to keep existing selections
36031      */
36032     selectRecords : function(records, keepExisting){
36033         if(!keepExisting){
36034             this.clearSelections();
36035         }
36036         var ds = this.grid.ds;
36037         for(var i = 0, len = records.length; i < len; i++){
36038             this.selectRow(ds.indexOf(records[i]), true);
36039         }
36040     },
36041
36042     /**
36043      * Gets the number of selected rows.
36044      * @return {Number}
36045      */
36046     getCount : function(){
36047         return this.selections.length;
36048     },
36049
36050     /**
36051      * Selects the first row in the grid.
36052      */
36053     selectFirstRow : function(){
36054         this.selectRow(0);
36055     },
36056
36057     /**
36058      * Select the last row.
36059      * @param {Boolean} keepExisting (optional) True to keep existing selections
36060      */
36061     selectLastRow : function(keepExisting){
36062         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36063     },
36064
36065     /**
36066      * Selects the row immediately following the last selected row.
36067      * @param {Boolean} keepExisting (optional) True to keep existing selections
36068      */
36069     selectNext : function(keepExisting){
36070         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36071             this.selectRow(this.last+1, keepExisting);
36072             var view = this.grid.view ? this.grid.view : this.grid;
36073             view.focusRow(this.last);
36074         }
36075     },
36076
36077     /**
36078      * Selects the row that precedes the last selected row.
36079      * @param {Boolean} keepExisting (optional) True to keep existing selections
36080      */
36081     selectPrevious : function(keepExisting){
36082         if(this.last){
36083             this.selectRow(this.last-1, keepExisting);
36084             var view = this.grid.view ? this.grid.view : this.grid;
36085             view.focusRow(this.last);
36086         }
36087     },
36088
36089     /**
36090      * Returns the selected records
36091      * @return {Array} Array of selected records
36092      */
36093     getSelections : function(){
36094         return [].concat(this.selections.items);
36095     },
36096
36097     /**
36098      * Returns the first selected record.
36099      * @return {Record}
36100      */
36101     getSelected : function(){
36102         return this.selections.itemAt(0);
36103     },
36104
36105
36106     /**
36107      * Clears all selections.
36108      */
36109     clearSelections : function(fast){
36110         if(this.locked) {
36111             return;
36112         }
36113         if(fast !== true){
36114             var ds = this.grid.ds;
36115             var s = this.selections;
36116             s.each(function(r){
36117                 this.deselectRow(ds.indexOfId(r.id));
36118             }, this);
36119             s.clear();
36120         }else{
36121             this.selections.clear();
36122         }
36123         this.last = false;
36124     },
36125
36126
36127     /**
36128      * Selects all rows.
36129      */
36130     selectAll : function(){
36131         if(this.locked) {
36132             return;
36133         }
36134         this.selections.clear();
36135         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36136             this.selectRow(i, true);
36137         }
36138     },
36139
36140     /**
36141      * Returns True if there is a selection.
36142      * @return {Boolean}
36143      */
36144     hasSelection : function(){
36145         return this.selections.length > 0;
36146     },
36147
36148     /**
36149      * Returns True if the specified row is selected.
36150      * @param {Number/Record} record The record or index of the record to check
36151      * @return {Boolean}
36152      */
36153     isSelected : function(index){
36154         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36155         return (r && this.selections.key(r.id) ? true : false);
36156     },
36157
36158     /**
36159      * Returns True if the specified record id is selected.
36160      * @param {String} id The id of record to check
36161      * @return {Boolean}
36162      */
36163     isIdSelected : function(id){
36164         return (this.selections.key(id) ? true : false);
36165     },
36166
36167     // private
36168     handleMouseDown : function(e, t)
36169     {
36170         var view = this.grid.view ? this.grid.view : this.grid;
36171         var rowIndex;
36172         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36173             return;
36174         };
36175         if(e.shiftKey && this.last !== false){
36176             var last = this.last;
36177             this.selectRange(last, rowIndex, e.ctrlKey);
36178             this.last = last; // reset the last
36179             view.focusRow(rowIndex);
36180         }else{
36181             var isSelected = this.isSelected(rowIndex);
36182             if(e.button !== 0 && isSelected){
36183                 view.focusRow(rowIndex);
36184             }else if(e.ctrlKey && isSelected){
36185                 this.deselectRow(rowIndex);
36186             }else if(!isSelected){
36187                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36188                 view.focusRow(rowIndex);
36189             }
36190         }
36191         this.fireEvent("afterselectionchange", this);
36192     },
36193     // private
36194     handleDragableRowClick :  function(grid, rowIndex, e) 
36195     {
36196         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36197             this.selectRow(rowIndex, false);
36198             var view = this.grid.view ? this.grid.view : this.grid;
36199             view.focusRow(rowIndex);
36200              this.fireEvent("afterselectionchange", this);
36201         }
36202     },
36203     
36204     /**
36205      * Selects multiple rows.
36206      * @param {Array} rows Array of the indexes of the row to select
36207      * @param {Boolean} keepExisting (optional) True to keep existing selections
36208      */
36209     selectRows : function(rows, keepExisting){
36210         if(!keepExisting){
36211             this.clearSelections();
36212         }
36213         for(var i = 0, len = rows.length; i < len; i++){
36214             this.selectRow(rows[i], true);
36215         }
36216     },
36217
36218     /**
36219      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36220      * @param {Number} startRow The index of the first row in the range
36221      * @param {Number} endRow The index of the last row in the range
36222      * @param {Boolean} keepExisting (optional) True to retain existing selections
36223      */
36224     selectRange : function(startRow, endRow, keepExisting){
36225         if(this.locked) {
36226             return;
36227         }
36228         if(!keepExisting){
36229             this.clearSelections();
36230         }
36231         if(startRow <= endRow){
36232             for(var i = startRow; i <= endRow; i++){
36233                 this.selectRow(i, true);
36234             }
36235         }else{
36236             for(var i = startRow; i >= endRow; i--){
36237                 this.selectRow(i, true);
36238             }
36239         }
36240     },
36241
36242     /**
36243      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36244      * @param {Number} startRow The index of the first row in the range
36245      * @param {Number} endRow The index of the last row in the range
36246      */
36247     deselectRange : function(startRow, endRow, preventViewNotify){
36248         if(this.locked) {
36249             return;
36250         }
36251         for(var i = startRow; i <= endRow; i++){
36252             this.deselectRow(i, preventViewNotify);
36253         }
36254     },
36255
36256     /**
36257      * Selects a row.
36258      * @param {Number} row The index of the row to select
36259      * @param {Boolean} keepExisting (optional) True to keep existing selections
36260      */
36261     selectRow : function(index, keepExisting, preventViewNotify){
36262         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36263             return;
36264         }
36265         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36266             if(!keepExisting || this.singleSelect){
36267                 this.clearSelections();
36268             }
36269             var r = this.grid.ds.getAt(index);
36270             this.selections.add(r);
36271             this.last = this.lastActive = index;
36272             if(!preventViewNotify){
36273                 var view = this.grid.view ? this.grid.view : this.grid;
36274                 view.onRowSelect(index);
36275             }
36276             this.fireEvent("rowselect", this, index, r);
36277             this.fireEvent("selectionchange", this);
36278         }
36279     },
36280
36281     /**
36282      * Deselects a row.
36283      * @param {Number} row The index of the row to deselect
36284      */
36285     deselectRow : function(index, preventViewNotify){
36286         if(this.locked) {
36287             return;
36288         }
36289         if(this.last == index){
36290             this.last = false;
36291         }
36292         if(this.lastActive == index){
36293             this.lastActive = false;
36294         }
36295         var r = this.grid.ds.getAt(index);
36296         this.selections.remove(r);
36297         if(!preventViewNotify){
36298             var view = this.grid.view ? this.grid.view : this.grid;
36299             view.onRowDeselect(index);
36300         }
36301         this.fireEvent("rowdeselect", this, index);
36302         this.fireEvent("selectionchange", this);
36303     },
36304
36305     // private
36306     restoreLast : function(){
36307         if(this._last){
36308             this.last = this._last;
36309         }
36310     },
36311
36312     // private
36313     acceptsNav : function(row, col, cm){
36314         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36315     },
36316
36317     // private
36318     onEditorKey : function(field, e){
36319         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36320         if(k == e.TAB){
36321             e.stopEvent();
36322             ed.completeEdit();
36323             if(e.shiftKey){
36324                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36325             }else{
36326                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36327             }
36328         }else if(k == e.ENTER && !e.ctrlKey){
36329             e.stopEvent();
36330             ed.completeEdit();
36331             if(e.shiftKey){
36332                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36333             }else{
36334                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36335             }
36336         }else if(k == e.ESC){
36337             ed.cancelEdit();
36338         }
36339         if(newCell){
36340             g.startEditing(newCell[0], newCell[1]);
36341         }
36342     }
36343 });/*
36344  * Based on:
36345  * Ext JS Library 1.1.1
36346  * Copyright(c) 2006-2007, Ext JS, LLC.
36347  *
36348  * Originally Released Under LGPL - original licence link has changed is not relivant.
36349  *
36350  * Fork - LGPL
36351  * <script type="text/javascript">
36352  */
36353 /**
36354  * @class Roo.grid.CellSelectionModel
36355  * @extends Roo.grid.AbstractSelectionModel
36356  * This class provides the basic implementation for cell selection in a grid.
36357  * @constructor
36358  * @param {Object} config The object containing the configuration of this model.
36359  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36360  */
36361 Roo.grid.CellSelectionModel = function(config){
36362     Roo.apply(this, config);
36363
36364     this.selection = null;
36365
36366     this.addEvents({
36367         /**
36368              * @event beforerowselect
36369              * Fires before a cell is selected.
36370              * @param {SelectionModel} this
36371              * @param {Number} rowIndex The selected row index
36372              * @param {Number} colIndex The selected cell index
36373              */
36374             "beforecellselect" : true,
36375         /**
36376              * @event cellselect
36377              * Fires when a cell is selected.
36378              * @param {SelectionModel} this
36379              * @param {Number} rowIndex The selected row index
36380              * @param {Number} colIndex The selected cell index
36381              */
36382             "cellselect" : true,
36383         /**
36384              * @event selectionchange
36385              * Fires when the active selection changes.
36386              * @param {SelectionModel} this
36387              * @param {Object} selection null for no selection or an object (o) with two properties
36388                 <ul>
36389                 <li>o.record: the record object for the row the selection is in</li>
36390                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36391                 </ul>
36392              */
36393             "selectionchange" : true,
36394         /**
36395              * @event tabend
36396              * Fires when the tab (or enter) was pressed on the last editable cell
36397              * You can use this to trigger add new row.
36398              * @param {SelectionModel} this
36399              */
36400             "tabend" : true,
36401          /**
36402              * @event beforeeditnext
36403              * Fires before the next editable sell is made active
36404              * You can use this to skip to another cell or fire the tabend
36405              *    if you set cell to false
36406              * @param {Object} eventdata object : { cell : [ row, col ] } 
36407              */
36408             "beforeeditnext" : true
36409     });
36410     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36411 };
36412
36413 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36414     
36415     enter_is_tab: false,
36416
36417     /** @ignore */
36418     initEvents : function(){
36419         this.grid.on("mousedown", this.handleMouseDown, this);
36420         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36421         var view = this.grid.view;
36422         view.on("refresh", this.onViewChange, this);
36423         view.on("rowupdated", this.onRowUpdated, this);
36424         view.on("beforerowremoved", this.clearSelections, this);
36425         view.on("beforerowsinserted", this.clearSelections, this);
36426         if(this.grid.isEditor){
36427             this.grid.on("beforeedit", this.beforeEdit,  this);
36428         }
36429     },
36430
36431         //private
36432     beforeEdit : function(e){
36433         this.select(e.row, e.column, false, true, e.record);
36434     },
36435
36436         //private
36437     onRowUpdated : function(v, index, r){
36438         if(this.selection && this.selection.record == r){
36439             v.onCellSelect(index, this.selection.cell[1]);
36440         }
36441     },
36442
36443         //private
36444     onViewChange : function(){
36445         this.clearSelections(true);
36446     },
36447
36448         /**
36449          * Returns the currently selected cell,.
36450          * @return {Array} The selected cell (row, column) or null if none selected.
36451          */
36452     getSelectedCell : function(){
36453         return this.selection ? this.selection.cell : null;
36454     },
36455
36456     /**
36457      * Clears all selections.
36458      * @param {Boolean} true to prevent the gridview from being notified about the change.
36459      */
36460     clearSelections : function(preventNotify){
36461         var s = this.selection;
36462         if(s){
36463             if(preventNotify !== true){
36464                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36465             }
36466             this.selection = null;
36467             this.fireEvent("selectionchange", this, null);
36468         }
36469     },
36470
36471     /**
36472      * Returns true if there is a selection.
36473      * @return {Boolean}
36474      */
36475     hasSelection : function(){
36476         return this.selection ? true : false;
36477     },
36478
36479     /** @ignore */
36480     handleMouseDown : function(e, t){
36481         var v = this.grid.getView();
36482         if(this.isLocked()){
36483             return;
36484         };
36485         var row = v.findRowIndex(t);
36486         var cell = v.findCellIndex(t);
36487         if(row !== false && cell !== false){
36488             this.select(row, cell);
36489         }
36490     },
36491
36492     /**
36493      * Selects a cell.
36494      * @param {Number} rowIndex
36495      * @param {Number} collIndex
36496      */
36497     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36498         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36499             this.clearSelections();
36500             r = r || this.grid.dataSource.getAt(rowIndex);
36501             this.selection = {
36502                 record : r,
36503                 cell : [rowIndex, colIndex]
36504             };
36505             if(!preventViewNotify){
36506                 var v = this.grid.getView();
36507                 v.onCellSelect(rowIndex, colIndex);
36508                 if(preventFocus !== true){
36509                     v.focusCell(rowIndex, colIndex);
36510                 }
36511             }
36512             this.fireEvent("cellselect", this, rowIndex, colIndex);
36513             this.fireEvent("selectionchange", this, this.selection);
36514         }
36515     },
36516
36517         //private
36518     isSelectable : function(rowIndex, colIndex, cm){
36519         return !cm.isHidden(colIndex);
36520     },
36521
36522     /** @ignore */
36523     handleKeyDown : function(e){
36524         //Roo.log('Cell Sel Model handleKeyDown');
36525         if(!e.isNavKeyPress()){
36526             return;
36527         }
36528         var g = this.grid, s = this.selection;
36529         if(!s){
36530             e.stopEvent();
36531             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36532             if(cell){
36533                 this.select(cell[0], cell[1]);
36534             }
36535             return;
36536         }
36537         var sm = this;
36538         var walk = function(row, col, step){
36539             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36540         };
36541         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36542         var newCell;
36543
36544       
36545
36546         switch(k){
36547             case e.TAB:
36548                 // handled by onEditorKey
36549                 if (g.isEditor && g.editing) {
36550                     return;
36551                 }
36552                 if(e.shiftKey) {
36553                     newCell = walk(r, c-1, -1);
36554                 } else {
36555                     newCell = walk(r, c+1, 1);
36556                 }
36557                 break;
36558             
36559             case e.DOWN:
36560                newCell = walk(r+1, c, 1);
36561                 break;
36562             
36563             case e.UP:
36564                 newCell = walk(r-1, c, -1);
36565                 break;
36566             
36567             case e.RIGHT:
36568                 newCell = walk(r, c+1, 1);
36569                 break;
36570             
36571             case e.LEFT:
36572                 newCell = walk(r, c-1, -1);
36573                 break;
36574             
36575             case e.ENTER:
36576                 
36577                 if(g.isEditor && !g.editing){
36578                    g.startEditing(r, c);
36579                    e.stopEvent();
36580                    return;
36581                 }
36582                 
36583                 
36584              break;
36585         };
36586         if(newCell){
36587             this.select(newCell[0], newCell[1]);
36588             e.stopEvent();
36589             
36590         }
36591     },
36592
36593     acceptsNav : function(row, col, cm){
36594         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36595     },
36596     /**
36597      * Selects a cell.
36598      * @param {Number} field (not used) - as it's normally used as a listener
36599      * @param {Number} e - event - fake it by using
36600      *
36601      * var e = Roo.EventObjectImpl.prototype;
36602      * e.keyCode = e.TAB
36603      *
36604      * 
36605      */
36606     onEditorKey : function(field, e){
36607         
36608         var k = e.getKey(),
36609             newCell,
36610             g = this.grid,
36611             ed = g.activeEditor,
36612             forward = false;
36613         ///Roo.log('onEditorKey' + k);
36614         
36615         
36616         if (this.enter_is_tab && k == e.ENTER) {
36617             k = e.TAB;
36618         }
36619         
36620         if(k == e.TAB){
36621             if(e.shiftKey){
36622                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36623             }else{
36624                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36625                 forward = true;
36626             }
36627             
36628             e.stopEvent();
36629             
36630         } else if(k == e.ENTER &&  !e.ctrlKey){
36631             ed.completeEdit();
36632             e.stopEvent();
36633             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36634         
36635                 } else if(k == e.ESC){
36636             ed.cancelEdit();
36637         }
36638                 
36639         if (newCell) {
36640             var ecall = { cell : newCell, forward : forward };
36641             this.fireEvent('beforeeditnext', ecall );
36642             newCell = ecall.cell;
36643                         forward = ecall.forward;
36644         }
36645                 
36646         if(newCell){
36647             //Roo.log('next cell after edit');
36648             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36649         } else if (forward) {
36650             // tabbed past last
36651             this.fireEvent.defer(100, this, ['tabend',this]);
36652         }
36653     }
36654 });/*
36655  * Based on:
36656  * Ext JS Library 1.1.1
36657  * Copyright(c) 2006-2007, Ext JS, LLC.
36658  *
36659  * Originally Released Under LGPL - original licence link has changed is not relivant.
36660  *
36661  * Fork - LGPL
36662  * <script type="text/javascript">
36663  */
36664  
36665 /**
36666  * @class Roo.grid.EditorGrid
36667  * @extends Roo.grid.Grid
36668  * Class for creating and editable grid.
36669  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36670  * The container MUST have some type of size defined for the grid to fill. The container will be 
36671  * automatically set to position relative if it isn't already.
36672  * @param {Object} dataSource The data model to bind to
36673  * @param {Object} colModel The column model with info about this grid's columns
36674  */
36675 Roo.grid.EditorGrid = function(container, config){
36676     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36677     this.getGridEl().addClass("xedit-grid");
36678
36679     if(!this.selModel){
36680         this.selModel = new Roo.grid.CellSelectionModel();
36681     }
36682
36683     this.activeEditor = null;
36684
36685         this.addEvents({
36686             /**
36687              * @event beforeedit
36688              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36689              * <ul style="padding:5px;padding-left:16px;">
36690              * <li>grid - This grid</li>
36691              * <li>record - The record being edited</li>
36692              * <li>field - The field name being edited</li>
36693              * <li>value - The value for the field being edited.</li>
36694              * <li>row - The grid row index</li>
36695              * <li>column - The grid column index</li>
36696              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36697              * </ul>
36698              * @param {Object} e An edit event (see above for description)
36699              */
36700             "beforeedit" : true,
36701             /**
36702              * @event afteredit
36703              * Fires after a cell is edited. <br />
36704              * <ul style="padding:5px;padding-left:16px;">
36705              * <li>grid - This grid</li>
36706              * <li>record - The record being edited</li>
36707              * <li>field - The field name being edited</li>
36708              * <li>value - The value being set</li>
36709              * <li>originalValue - The original value for the field, before the edit.</li>
36710              * <li>row - The grid row index</li>
36711              * <li>column - The grid column index</li>
36712              * </ul>
36713              * @param {Object} e An edit event (see above for description)
36714              */
36715             "afteredit" : true,
36716             /**
36717              * @event validateedit
36718              * Fires after a cell is edited, but before the value is set in the record. 
36719          * You can use this to modify the value being set in the field, Return false
36720              * to cancel the change. The edit event object has the following properties <br />
36721              * <ul style="padding:5px;padding-left:16px;">
36722          * <li>editor - This editor</li>
36723              * <li>grid - This grid</li>
36724              * <li>record - The record being edited</li>
36725              * <li>field - The field name being edited</li>
36726              * <li>value - The value being set</li>
36727              * <li>originalValue - The original value for the field, before the edit.</li>
36728              * <li>row - The grid row index</li>
36729              * <li>column - The grid column index</li>
36730              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36731              * </ul>
36732              * @param {Object} e An edit event (see above for description)
36733              */
36734             "validateedit" : true
36735         });
36736     this.on("bodyscroll", this.stopEditing,  this);
36737     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36738 };
36739
36740 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36741     /**
36742      * @cfg {Number} clicksToEdit
36743      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36744      */
36745     clicksToEdit: 2,
36746
36747     // private
36748     isEditor : true,
36749     // private
36750     trackMouseOver: false, // causes very odd FF errors
36751
36752     onCellDblClick : function(g, row, col){
36753         this.startEditing(row, col);
36754     },
36755
36756     onEditComplete : function(ed, value, startValue){
36757         this.editing = false;
36758         this.activeEditor = null;
36759         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36760         var r = ed.record;
36761         var field = this.colModel.getDataIndex(ed.col);
36762         var e = {
36763             grid: this,
36764             record: r,
36765             field: field,
36766             originalValue: startValue,
36767             value: value,
36768             row: ed.row,
36769             column: ed.col,
36770             cancel:false,
36771             editor: ed
36772         };
36773         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36774         cell.show();
36775           
36776         if(String(value) !== String(startValue)){
36777             
36778             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36779                 r.set(field, e.value);
36780                 // if we are dealing with a combo box..
36781                 // then we also set the 'name' colum to be the displayField
36782                 if (ed.field.displayField && ed.field.name) {
36783                     r.set(ed.field.name, ed.field.el.dom.value);
36784                 }
36785                 
36786                 delete e.cancel; //?? why!!!
36787                 this.fireEvent("afteredit", e);
36788             }
36789         } else {
36790             this.fireEvent("afteredit", e); // always fire it!
36791         }
36792         this.view.focusCell(ed.row, ed.col);
36793     },
36794
36795     /**
36796      * Starts editing the specified for the specified row/column
36797      * @param {Number} rowIndex
36798      * @param {Number} colIndex
36799      */
36800     startEditing : function(row, col){
36801         this.stopEditing();
36802         if(this.colModel.isCellEditable(col, row)){
36803             this.view.ensureVisible(row, col, true);
36804           
36805             var r = this.dataSource.getAt(row);
36806             var field = this.colModel.getDataIndex(col);
36807             var cell = Roo.get(this.view.getCell(row,col));
36808             var e = {
36809                 grid: this,
36810                 record: r,
36811                 field: field,
36812                 value: r.data[field],
36813                 row: row,
36814                 column: col,
36815                 cancel:false 
36816             };
36817             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36818                 this.editing = true;
36819                 var ed = this.colModel.getCellEditor(col, row);
36820                 
36821                 if (!ed) {
36822                     return;
36823                 }
36824                 if(!ed.rendered){
36825                     ed.render(ed.parentEl || document.body);
36826                 }
36827                 ed.field.reset();
36828                
36829                 cell.hide();
36830                 
36831                 (function(){ // complex but required for focus issues in safari, ie and opera
36832                     ed.row = row;
36833                     ed.col = col;
36834                     ed.record = r;
36835                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36836                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36837                     this.activeEditor = ed;
36838                     var v = r.data[field];
36839                     ed.startEdit(this.view.getCell(row, col), v);
36840                     // combo's with 'displayField and name set
36841                     if (ed.field.displayField && ed.field.name) {
36842                         ed.field.el.dom.value = r.data[ed.field.name];
36843                     }
36844                     
36845                     
36846                 }).defer(50, this);
36847             }
36848         }
36849     },
36850         
36851     /**
36852      * Stops any active editing
36853      */
36854     stopEditing : function(){
36855         if(this.activeEditor){
36856             this.activeEditor.completeEdit();
36857         }
36858         this.activeEditor = null;
36859     },
36860         
36861          /**
36862      * Called to get grid's drag proxy text, by default returns this.ddText.
36863      * @return {String}
36864      */
36865     getDragDropText : function(){
36866         var count = this.selModel.getSelectedCell() ? 1 : 0;
36867         return String.format(this.ddText, count, count == 1 ? '' : 's');
36868     }
36869         
36870 });/*
36871  * Based on:
36872  * Ext JS Library 1.1.1
36873  * Copyright(c) 2006-2007, Ext JS, LLC.
36874  *
36875  * Originally Released Under LGPL - original licence link has changed is not relivant.
36876  *
36877  * Fork - LGPL
36878  * <script type="text/javascript">
36879  */
36880
36881 // private - not really -- you end up using it !
36882 // This is a support class used internally by the Grid components
36883
36884 /**
36885  * @class Roo.grid.GridEditor
36886  * @extends Roo.Editor
36887  * Class for creating and editable grid elements.
36888  * @param {Object} config any settings (must include field)
36889  */
36890 Roo.grid.GridEditor = function(field, config){
36891     if (!config && field.field) {
36892         config = field;
36893         field = Roo.factory(config.field, Roo.form);
36894     }
36895     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36896     field.monitorTab = false;
36897 };
36898
36899 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36900     
36901     /**
36902      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36903      */
36904     
36905     alignment: "tl-tl",
36906     autoSize: "width",
36907     hideEl : false,
36908     cls: "x-small-editor x-grid-editor",
36909     shim:false,
36910     shadow:"frame"
36911 });/*
36912  * Based on:
36913  * Ext JS Library 1.1.1
36914  * Copyright(c) 2006-2007, Ext JS, LLC.
36915  *
36916  * Originally Released Under LGPL - original licence link has changed is not relivant.
36917  *
36918  * Fork - LGPL
36919  * <script type="text/javascript">
36920  */
36921   
36922
36923   
36924 Roo.grid.PropertyRecord = Roo.data.Record.create([
36925     {name:'name',type:'string'},  'value'
36926 ]);
36927
36928
36929 Roo.grid.PropertyStore = function(grid, source){
36930     this.grid = grid;
36931     this.store = new Roo.data.Store({
36932         recordType : Roo.grid.PropertyRecord
36933     });
36934     this.store.on('update', this.onUpdate,  this);
36935     if(source){
36936         this.setSource(source);
36937     }
36938     Roo.grid.PropertyStore.superclass.constructor.call(this);
36939 };
36940
36941
36942
36943 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36944     setSource : function(o){
36945         this.source = o;
36946         this.store.removeAll();
36947         var data = [];
36948         for(var k in o){
36949             if(this.isEditableValue(o[k])){
36950                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36951             }
36952         }
36953         this.store.loadRecords({records: data}, {}, true);
36954     },
36955
36956     onUpdate : function(ds, record, type){
36957         if(type == Roo.data.Record.EDIT){
36958             var v = record.data['value'];
36959             var oldValue = record.modified['value'];
36960             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36961                 this.source[record.id] = v;
36962                 record.commit();
36963                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36964             }else{
36965                 record.reject();
36966             }
36967         }
36968     },
36969
36970     getProperty : function(row){
36971        return this.store.getAt(row);
36972     },
36973
36974     isEditableValue: function(val){
36975         if(val && val instanceof Date){
36976             return true;
36977         }else if(typeof val == 'object' || typeof val == 'function'){
36978             return false;
36979         }
36980         return true;
36981     },
36982
36983     setValue : function(prop, value){
36984         this.source[prop] = value;
36985         this.store.getById(prop).set('value', value);
36986     },
36987
36988     getSource : function(){
36989         return this.source;
36990     }
36991 });
36992
36993 Roo.grid.PropertyColumnModel = function(grid, store){
36994     this.grid = grid;
36995     var g = Roo.grid;
36996     g.PropertyColumnModel.superclass.constructor.call(this, [
36997         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36998         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36999     ]);
37000     this.store = store;
37001     this.bselect = Roo.DomHelper.append(document.body, {
37002         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37003             {tag: 'option', value: 'true', html: 'true'},
37004             {tag: 'option', value: 'false', html: 'false'}
37005         ]
37006     });
37007     Roo.id(this.bselect);
37008     var f = Roo.form;
37009     this.editors = {
37010         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37011         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37012         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37013         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37014         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37015     };
37016     this.renderCellDelegate = this.renderCell.createDelegate(this);
37017     this.renderPropDelegate = this.renderProp.createDelegate(this);
37018 };
37019
37020 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37021     
37022     
37023     nameText : 'Name',
37024     valueText : 'Value',
37025     
37026     dateFormat : 'm/j/Y',
37027     
37028     
37029     renderDate : function(dateVal){
37030         return dateVal.dateFormat(this.dateFormat);
37031     },
37032
37033     renderBool : function(bVal){
37034         return bVal ? 'true' : 'false';
37035     },
37036
37037     isCellEditable : function(colIndex, rowIndex){
37038         return colIndex == 1;
37039     },
37040
37041     getRenderer : function(col){
37042         return col == 1 ?
37043             this.renderCellDelegate : this.renderPropDelegate;
37044     },
37045
37046     renderProp : function(v){
37047         return this.getPropertyName(v);
37048     },
37049
37050     renderCell : function(val){
37051         var rv = val;
37052         if(val instanceof Date){
37053             rv = this.renderDate(val);
37054         }else if(typeof val == 'boolean'){
37055             rv = this.renderBool(val);
37056         }
37057         return Roo.util.Format.htmlEncode(rv);
37058     },
37059
37060     getPropertyName : function(name){
37061         var pn = this.grid.propertyNames;
37062         return pn && pn[name] ? pn[name] : name;
37063     },
37064
37065     getCellEditor : function(colIndex, rowIndex){
37066         var p = this.store.getProperty(rowIndex);
37067         var n = p.data['name'], val = p.data['value'];
37068         
37069         if(typeof(this.grid.customEditors[n]) == 'string'){
37070             return this.editors[this.grid.customEditors[n]];
37071         }
37072         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37073             return this.grid.customEditors[n];
37074         }
37075         if(val instanceof Date){
37076             return this.editors['date'];
37077         }else if(typeof val == 'number'){
37078             return this.editors['number'];
37079         }else if(typeof val == 'boolean'){
37080             return this.editors['boolean'];
37081         }else{
37082             return this.editors['string'];
37083         }
37084     }
37085 });
37086
37087 /**
37088  * @class Roo.grid.PropertyGrid
37089  * @extends Roo.grid.EditorGrid
37090  * This class represents the  interface of a component based property grid control.
37091  * <br><br>Usage:<pre><code>
37092  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37093       
37094  });
37095  // set any options
37096  grid.render();
37097  * </code></pre>
37098   
37099  * @constructor
37100  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37101  * The container MUST have some type of size defined for the grid to fill. The container will be
37102  * automatically set to position relative if it isn't already.
37103  * @param {Object} config A config object that sets properties on this grid.
37104  */
37105 Roo.grid.PropertyGrid = function(container, config){
37106     config = config || {};
37107     var store = new Roo.grid.PropertyStore(this);
37108     this.store = store;
37109     var cm = new Roo.grid.PropertyColumnModel(this, store);
37110     store.store.sort('name', 'ASC');
37111     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37112         ds: store.store,
37113         cm: cm,
37114         enableColLock:false,
37115         enableColumnMove:false,
37116         stripeRows:false,
37117         trackMouseOver: false,
37118         clicksToEdit:1
37119     }, config));
37120     this.getGridEl().addClass('x-props-grid');
37121     this.lastEditRow = null;
37122     this.on('columnresize', this.onColumnResize, this);
37123     this.addEvents({
37124          /**
37125              * @event beforepropertychange
37126              * Fires before a property changes (return false to stop?)
37127              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37128              * @param {String} id Record Id
37129              * @param {String} newval New Value
37130          * @param {String} oldval Old Value
37131              */
37132         "beforepropertychange": true,
37133         /**
37134              * @event propertychange
37135              * Fires after a property changes
37136              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37137              * @param {String} id Record Id
37138              * @param {String} newval New Value
37139          * @param {String} oldval Old Value
37140              */
37141         "propertychange": true
37142     });
37143     this.customEditors = this.customEditors || {};
37144 };
37145 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37146     
37147      /**
37148      * @cfg {Object} customEditors map of colnames=> custom editors.
37149      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37150      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37151      * false disables editing of the field.
37152          */
37153     
37154       /**
37155      * @cfg {Object} propertyNames map of property Names to their displayed value
37156          */
37157     
37158     render : function(){
37159         Roo.grid.PropertyGrid.superclass.render.call(this);
37160         this.autoSize.defer(100, this);
37161     },
37162
37163     autoSize : function(){
37164         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37165         if(this.view){
37166             this.view.fitColumns();
37167         }
37168     },
37169
37170     onColumnResize : function(){
37171         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37172         this.autoSize();
37173     },
37174     /**
37175      * Sets the data for the Grid
37176      * accepts a Key => Value object of all the elements avaiable.
37177      * @param {Object} data  to appear in grid.
37178      */
37179     setSource : function(source){
37180         this.store.setSource(source);
37181         //this.autoSize();
37182     },
37183     /**
37184      * Gets all the data from the grid.
37185      * @return {Object} data  data stored in grid
37186      */
37187     getSource : function(){
37188         return this.store.getSource();
37189     }
37190 });/*
37191   
37192  * Licence LGPL
37193  
37194  */
37195  
37196 /**
37197  * @class Roo.grid.Calendar
37198  * @extends Roo.util.Grid
37199  * This class extends the Grid to provide a calendar widget
37200  * <br><br>Usage:<pre><code>
37201  var grid = new Roo.grid.Calendar("my-container-id", {
37202      ds: myDataStore,
37203      cm: myColModel,
37204      selModel: mySelectionModel,
37205      autoSizeColumns: true,
37206      monitorWindowResize: false,
37207      trackMouseOver: true
37208      eventstore : real data store..
37209  });
37210  // set any options
37211  grid.render();
37212   
37213   * @constructor
37214  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37215  * The container MUST have some type of size defined for the grid to fill. The container will be
37216  * automatically set to position relative if it isn't already.
37217  * @param {Object} config A config object that sets properties on this grid.
37218  */
37219 Roo.grid.Calendar = function(container, config){
37220         // initialize the container
37221         this.container = Roo.get(container);
37222         this.container.update("");
37223         this.container.setStyle("overflow", "hidden");
37224     this.container.addClass('x-grid-container');
37225
37226     this.id = this.container.id;
37227
37228     Roo.apply(this, config);
37229     // check and correct shorthanded configs
37230     
37231     var rows = [];
37232     var d =1;
37233     for (var r = 0;r < 6;r++) {
37234         
37235         rows[r]=[];
37236         for (var c =0;c < 7;c++) {
37237             rows[r][c]= '';
37238         }
37239     }
37240     if (this.eventStore) {
37241         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37242         this.eventStore.on('load',this.onLoad, this);
37243         this.eventStore.on('beforeload',this.clearEvents, this);
37244          
37245     }
37246     
37247     this.dataSource = new Roo.data.Store({
37248             proxy: new Roo.data.MemoryProxy(rows),
37249             reader: new Roo.data.ArrayReader({}, [
37250                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37251     });
37252
37253     this.dataSource.load();
37254     this.ds = this.dataSource;
37255     this.ds.xmodule = this.xmodule || false;
37256     
37257     
37258     var cellRender = function(v,x,r)
37259     {
37260         return String.format(
37261             '<div class="fc-day  fc-widget-content"><div>' +
37262                 '<div class="fc-event-container"></div>' +
37263                 '<div class="fc-day-number">{0}</div>'+
37264                 
37265                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37266             '</div></div>', v);
37267     
37268     }
37269     
37270     
37271     this.colModel = new Roo.grid.ColumnModel( [
37272         {
37273             xtype: 'ColumnModel',
37274             xns: Roo.grid,
37275             dataIndex : 'weekday0',
37276             header : 'Sunday',
37277             renderer : cellRender
37278         },
37279         {
37280             xtype: 'ColumnModel',
37281             xns: Roo.grid,
37282             dataIndex : 'weekday1',
37283             header : 'Monday',
37284             renderer : cellRender
37285         },
37286         {
37287             xtype: 'ColumnModel',
37288             xns: Roo.grid,
37289             dataIndex : 'weekday2',
37290             header : 'Tuesday',
37291             renderer : cellRender
37292         },
37293         {
37294             xtype: 'ColumnModel',
37295             xns: Roo.grid,
37296             dataIndex : 'weekday3',
37297             header : 'Wednesday',
37298             renderer : cellRender
37299         },
37300         {
37301             xtype: 'ColumnModel',
37302             xns: Roo.grid,
37303             dataIndex : 'weekday4',
37304             header : 'Thursday',
37305             renderer : cellRender
37306         },
37307         {
37308             xtype: 'ColumnModel',
37309             xns: Roo.grid,
37310             dataIndex : 'weekday5',
37311             header : 'Friday',
37312             renderer : cellRender
37313         },
37314         {
37315             xtype: 'ColumnModel',
37316             xns: Roo.grid,
37317             dataIndex : 'weekday6',
37318             header : 'Saturday',
37319             renderer : cellRender
37320         }
37321     ]);
37322     this.cm = this.colModel;
37323     this.cm.xmodule = this.xmodule || false;
37324  
37325         
37326           
37327     //this.selModel = new Roo.grid.CellSelectionModel();
37328     //this.sm = this.selModel;
37329     //this.selModel.init(this);
37330     
37331     
37332     if(this.width){
37333         this.container.setWidth(this.width);
37334     }
37335
37336     if(this.height){
37337         this.container.setHeight(this.height);
37338     }
37339     /** @private */
37340         this.addEvents({
37341         // raw events
37342         /**
37343          * @event click
37344          * The raw click event for the entire grid.
37345          * @param {Roo.EventObject} e
37346          */
37347         "click" : true,
37348         /**
37349          * @event dblclick
37350          * The raw dblclick event for the entire grid.
37351          * @param {Roo.EventObject} e
37352          */
37353         "dblclick" : true,
37354         /**
37355          * @event contextmenu
37356          * The raw contextmenu event for the entire grid.
37357          * @param {Roo.EventObject} e
37358          */
37359         "contextmenu" : true,
37360         /**
37361          * @event mousedown
37362          * The raw mousedown event for the entire grid.
37363          * @param {Roo.EventObject} e
37364          */
37365         "mousedown" : true,
37366         /**
37367          * @event mouseup
37368          * The raw mouseup event for the entire grid.
37369          * @param {Roo.EventObject} e
37370          */
37371         "mouseup" : true,
37372         /**
37373          * @event mouseover
37374          * The raw mouseover event for the entire grid.
37375          * @param {Roo.EventObject} e
37376          */
37377         "mouseover" : true,
37378         /**
37379          * @event mouseout
37380          * The raw mouseout event for the entire grid.
37381          * @param {Roo.EventObject} e
37382          */
37383         "mouseout" : true,
37384         /**
37385          * @event keypress
37386          * The raw keypress event for the entire grid.
37387          * @param {Roo.EventObject} e
37388          */
37389         "keypress" : true,
37390         /**
37391          * @event keydown
37392          * The raw keydown event for the entire grid.
37393          * @param {Roo.EventObject} e
37394          */
37395         "keydown" : true,
37396
37397         // custom events
37398
37399         /**
37400          * @event cellclick
37401          * Fires when a cell is clicked
37402          * @param {Grid} this
37403          * @param {Number} rowIndex
37404          * @param {Number} columnIndex
37405          * @param {Roo.EventObject} e
37406          */
37407         "cellclick" : true,
37408         /**
37409          * @event celldblclick
37410          * Fires when a cell is double clicked
37411          * @param {Grid} this
37412          * @param {Number} rowIndex
37413          * @param {Number} columnIndex
37414          * @param {Roo.EventObject} e
37415          */
37416         "celldblclick" : true,
37417         /**
37418          * @event rowclick
37419          * Fires when a row is clicked
37420          * @param {Grid} this
37421          * @param {Number} rowIndex
37422          * @param {Roo.EventObject} e
37423          */
37424         "rowclick" : true,
37425         /**
37426          * @event rowdblclick
37427          * Fires when a row is double clicked
37428          * @param {Grid} this
37429          * @param {Number} rowIndex
37430          * @param {Roo.EventObject} e
37431          */
37432         "rowdblclick" : true,
37433         /**
37434          * @event headerclick
37435          * Fires when a header is clicked
37436          * @param {Grid} this
37437          * @param {Number} columnIndex
37438          * @param {Roo.EventObject} e
37439          */
37440         "headerclick" : true,
37441         /**
37442          * @event headerdblclick
37443          * Fires when a header cell is double clicked
37444          * @param {Grid} this
37445          * @param {Number} columnIndex
37446          * @param {Roo.EventObject} e
37447          */
37448         "headerdblclick" : true,
37449         /**
37450          * @event rowcontextmenu
37451          * Fires when a row is right clicked
37452          * @param {Grid} this
37453          * @param {Number} rowIndex
37454          * @param {Roo.EventObject} e
37455          */
37456         "rowcontextmenu" : true,
37457         /**
37458          * @event cellcontextmenu
37459          * Fires when a cell is right clicked
37460          * @param {Grid} this
37461          * @param {Number} rowIndex
37462          * @param {Number} cellIndex
37463          * @param {Roo.EventObject} e
37464          */
37465          "cellcontextmenu" : true,
37466         /**
37467          * @event headercontextmenu
37468          * Fires when a header is right clicked
37469          * @param {Grid} this
37470          * @param {Number} columnIndex
37471          * @param {Roo.EventObject} e
37472          */
37473         "headercontextmenu" : true,
37474         /**
37475          * @event bodyscroll
37476          * Fires when the body element is scrolled
37477          * @param {Number} scrollLeft
37478          * @param {Number} scrollTop
37479          */
37480         "bodyscroll" : true,
37481         /**
37482          * @event columnresize
37483          * Fires when the user resizes a column
37484          * @param {Number} columnIndex
37485          * @param {Number} newSize
37486          */
37487         "columnresize" : true,
37488         /**
37489          * @event columnmove
37490          * Fires when the user moves a column
37491          * @param {Number} oldIndex
37492          * @param {Number} newIndex
37493          */
37494         "columnmove" : true,
37495         /**
37496          * @event startdrag
37497          * Fires when row(s) start being dragged
37498          * @param {Grid} this
37499          * @param {Roo.GridDD} dd The drag drop object
37500          * @param {event} e The raw browser event
37501          */
37502         "startdrag" : true,
37503         /**
37504          * @event enddrag
37505          * Fires when a drag operation is complete
37506          * @param {Grid} this
37507          * @param {Roo.GridDD} dd The drag drop object
37508          * @param {event} e The raw browser event
37509          */
37510         "enddrag" : true,
37511         /**
37512          * @event dragdrop
37513          * Fires when dragged row(s) are dropped on a valid DD target
37514          * @param {Grid} this
37515          * @param {Roo.GridDD} dd The drag drop object
37516          * @param {String} targetId The target drag drop object
37517          * @param {event} e The raw browser event
37518          */
37519         "dragdrop" : true,
37520         /**
37521          * @event dragover
37522          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37523          * @param {Grid} this
37524          * @param {Roo.GridDD} dd The drag drop object
37525          * @param {String} targetId The target drag drop object
37526          * @param {event} e The raw browser event
37527          */
37528         "dragover" : true,
37529         /**
37530          * @event dragenter
37531          *  Fires when the dragged row(s) first cross another DD target while being dragged
37532          * @param {Grid} this
37533          * @param {Roo.GridDD} dd The drag drop object
37534          * @param {String} targetId The target drag drop object
37535          * @param {event} e The raw browser event
37536          */
37537         "dragenter" : true,
37538         /**
37539          * @event dragout
37540          * Fires when the dragged row(s) leave another DD target while being dragged
37541          * @param {Grid} this
37542          * @param {Roo.GridDD} dd The drag drop object
37543          * @param {String} targetId The target drag drop object
37544          * @param {event} e The raw browser event
37545          */
37546         "dragout" : true,
37547         /**
37548          * @event rowclass
37549          * Fires when a row is rendered, so you can change add a style to it.
37550          * @param {GridView} gridview   The grid view
37551          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37552          */
37553         'rowclass' : true,
37554
37555         /**
37556          * @event render
37557          * Fires when the grid is rendered
37558          * @param {Grid} grid
37559          */
37560         'render' : true,
37561             /**
37562              * @event select
37563              * Fires when a date is selected
37564              * @param {DatePicker} this
37565              * @param {Date} date The selected date
37566              */
37567         'select': true,
37568         /**
37569              * @event monthchange
37570              * Fires when the displayed month changes 
37571              * @param {DatePicker} this
37572              * @param {Date} date The selected month
37573              */
37574         'monthchange': true,
37575         /**
37576              * @event evententer
37577              * Fires when mouse over an event
37578              * @param {Calendar} this
37579              * @param {event} Event
37580              */
37581         'evententer': true,
37582         /**
37583              * @event eventleave
37584              * Fires when the mouse leaves an
37585              * @param {Calendar} this
37586              * @param {event}
37587              */
37588         'eventleave': true,
37589         /**
37590              * @event eventclick
37591              * Fires when the mouse click an
37592              * @param {Calendar} this
37593              * @param {event}
37594              */
37595         'eventclick': true,
37596         /**
37597              * @event eventrender
37598              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37599              * @param {Calendar} this
37600              * @param {data} data to be modified
37601              */
37602         'eventrender': true
37603         
37604     });
37605
37606     Roo.grid.Grid.superclass.constructor.call(this);
37607     this.on('render', function() {
37608         this.view.el.addClass('x-grid-cal'); 
37609         
37610         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37611
37612     },this);
37613     
37614     if (!Roo.grid.Calendar.style) {
37615         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37616             
37617             
37618             '.x-grid-cal .x-grid-col' :  {
37619                 height: 'auto !important',
37620                 'vertical-align': 'top'
37621             },
37622             '.x-grid-cal  .fc-event-hori' : {
37623                 height: '14px'
37624             }
37625              
37626             
37627         }, Roo.id());
37628     }
37629
37630     
37631     
37632 };
37633 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37634     /**
37635      * @cfg {Store} eventStore The store that loads events.
37636      */
37637     eventStore : 25,
37638
37639      
37640     activeDate : false,
37641     startDay : 0,
37642     autoWidth : true,
37643     monitorWindowResize : false,
37644
37645     
37646     resizeColumns : function() {
37647         var col = (this.view.el.getWidth() / 7) - 3;
37648         // loop through cols, and setWidth
37649         for(var i =0 ; i < 7 ; i++){
37650             this.cm.setColumnWidth(i, col);
37651         }
37652     },
37653      setDate :function(date) {
37654         
37655         Roo.log('setDate?');
37656         
37657         this.resizeColumns();
37658         var vd = this.activeDate;
37659         this.activeDate = date;
37660 //        if(vd && this.el){
37661 //            var t = date.getTime();
37662 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37663 //                Roo.log('using add remove');
37664 //                
37665 //                this.fireEvent('monthchange', this, date);
37666 //                
37667 //                this.cells.removeClass("fc-state-highlight");
37668 //                this.cells.each(function(c){
37669 //                   if(c.dateValue == t){
37670 //                       c.addClass("fc-state-highlight");
37671 //                       setTimeout(function(){
37672 //                            try{c.dom.firstChild.focus();}catch(e){}
37673 //                       }, 50);
37674 //                       return false;
37675 //                   }
37676 //                   return true;
37677 //                });
37678 //                return;
37679 //            }
37680 //        }
37681         
37682         var days = date.getDaysInMonth();
37683         
37684         var firstOfMonth = date.getFirstDateOfMonth();
37685         var startingPos = firstOfMonth.getDay()-this.startDay;
37686         
37687         if(startingPos < this.startDay){
37688             startingPos += 7;
37689         }
37690         
37691         var pm = date.add(Date.MONTH, -1);
37692         var prevStart = pm.getDaysInMonth()-startingPos;
37693 //        
37694         
37695         
37696         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37697         
37698         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37699         //this.cells.addClassOnOver('fc-state-hover');
37700         
37701         var cells = this.cells.elements;
37702         var textEls = this.textNodes;
37703         
37704         //Roo.each(cells, function(cell){
37705         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37706         //});
37707         
37708         days += startingPos;
37709
37710         // convert everything to numbers so it's fast
37711         var day = 86400000;
37712         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37713         //Roo.log(d);
37714         //Roo.log(pm);
37715         //Roo.log(prevStart);
37716         
37717         var today = new Date().clearTime().getTime();
37718         var sel = date.clearTime().getTime();
37719         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37720         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37721         var ddMatch = this.disabledDatesRE;
37722         var ddText = this.disabledDatesText;
37723         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37724         var ddaysText = this.disabledDaysText;
37725         var format = this.format;
37726         
37727         var setCellClass = function(cal, cell){
37728             
37729             //Roo.log('set Cell Class');
37730             cell.title = "";
37731             var t = d.getTime();
37732             
37733             //Roo.log(d);
37734             
37735             
37736             cell.dateValue = t;
37737             if(t == today){
37738                 cell.className += " fc-today";
37739                 cell.className += " fc-state-highlight";
37740                 cell.title = cal.todayText;
37741             }
37742             if(t == sel){
37743                 // disable highlight in other month..
37744                 cell.className += " fc-state-highlight";
37745                 
37746             }
37747             // disabling
37748             if(t < min) {
37749                 //cell.className = " fc-state-disabled";
37750                 cell.title = cal.minText;
37751                 return;
37752             }
37753             if(t > max) {
37754                 //cell.className = " fc-state-disabled";
37755                 cell.title = cal.maxText;
37756                 return;
37757             }
37758             if(ddays){
37759                 if(ddays.indexOf(d.getDay()) != -1){
37760                     // cell.title = ddaysText;
37761                    // cell.className = " fc-state-disabled";
37762                 }
37763             }
37764             if(ddMatch && format){
37765                 var fvalue = d.dateFormat(format);
37766                 if(ddMatch.test(fvalue)){
37767                     cell.title = ddText.replace("%0", fvalue);
37768                    cell.className = " fc-state-disabled";
37769                 }
37770             }
37771             
37772             if (!cell.initialClassName) {
37773                 cell.initialClassName = cell.dom.className;
37774             }
37775             
37776             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37777         };
37778
37779         var i = 0;
37780         
37781         for(; i < startingPos; i++) {
37782             cells[i].dayName =  (++prevStart);
37783             Roo.log(textEls[i]);
37784             d.setDate(d.getDate()+1);
37785             
37786             //cells[i].className = "fc-past fc-other-month";
37787             setCellClass(this, cells[i]);
37788         }
37789         
37790         var intDay = 0;
37791         
37792         for(; i < days; i++){
37793             intDay = i - startingPos + 1;
37794             cells[i].dayName =  (intDay);
37795             d.setDate(d.getDate()+1);
37796             
37797             cells[i].className = ''; // "x-date-active";
37798             setCellClass(this, cells[i]);
37799         }
37800         var extraDays = 0;
37801         
37802         for(; i < 42; i++) {
37803             //textEls[i].innerHTML = (++extraDays);
37804             
37805             d.setDate(d.getDate()+1);
37806             cells[i].dayName = (++extraDays);
37807             cells[i].className = "fc-future fc-other-month";
37808             setCellClass(this, cells[i]);
37809         }
37810         
37811         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37812         
37813         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37814         
37815         // this will cause all the cells to mis
37816         var rows= [];
37817         var i =0;
37818         for (var r = 0;r < 6;r++) {
37819             for (var c =0;c < 7;c++) {
37820                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37821             }    
37822         }
37823         
37824         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37825         for(i=0;i<cells.length;i++) {
37826             
37827             this.cells.elements[i].dayName = cells[i].dayName ;
37828             this.cells.elements[i].className = cells[i].className;
37829             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37830             this.cells.elements[i].title = cells[i].title ;
37831             this.cells.elements[i].dateValue = cells[i].dateValue ;
37832         }
37833         
37834         
37835         
37836         
37837         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37838         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37839         
37840         ////if(totalRows != 6){
37841             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37842            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37843        // }
37844         
37845         this.fireEvent('monthchange', this, date);
37846         
37847         
37848     },
37849  /**
37850      * Returns the grid's SelectionModel.
37851      * @return {SelectionModel}
37852      */
37853     getSelectionModel : function(){
37854         if(!this.selModel){
37855             this.selModel = new Roo.grid.CellSelectionModel();
37856         }
37857         return this.selModel;
37858     },
37859
37860     load: function() {
37861         this.eventStore.load()
37862         
37863         
37864         
37865     },
37866     
37867     findCell : function(dt) {
37868         dt = dt.clearTime().getTime();
37869         var ret = false;
37870         this.cells.each(function(c){
37871             //Roo.log("check " +c.dateValue + '?=' + dt);
37872             if(c.dateValue == dt){
37873                 ret = c;
37874                 return false;
37875             }
37876             return true;
37877         });
37878         
37879         return ret;
37880     },
37881     
37882     findCells : function(rec) {
37883         var s = rec.data.start_dt.clone().clearTime().getTime();
37884        // Roo.log(s);
37885         var e= rec.data.end_dt.clone().clearTime().getTime();
37886        // Roo.log(e);
37887         var ret = [];
37888         this.cells.each(function(c){
37889              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37890             
37891             if(c.dateValue > e){
37892                 return ;
37893             }
37894             if(c.dateValue < s){
37895                 return ;
37896             }
37897             ret.push(c);
37898         });
37899         
37900         return ret;    
37901     },
37902     
37903     findBestRow: function(cells)
37904     {
37905         var ret = 0;
37906         
37907         for (var i =0 ; i < cells.length;i++) {
37908             ret  = Math.max(cells[i].rows || 0,ret);
37909         }
37910         return ret;
37911         
37912     },
37913     
37914     
37915     addItem : function(rec)
37916     {
37917         // look for vertical location slot in
37918         var cells = this.findCells(rec);
37919         
37920         rec.row = this.findBestRow(cells);
37921         
37922         // work out the location.
37923         
37924         var crow = false;
37925         var rows = [];
37926         for(var i =0; i < cells.length; i++) {
37927             if (!crow) {
37928                 crow = {
37929                     start : cells[i],
37930                     end :  cells[i]
37931                 };
37932                 continue;
37933             }
37934             if (crow.start.getY() == cells[i].getY()) {
37935                 // on same row.
37936                 crow.end = cells[i];
37937                 continue;
37938             }
37939             // different row.
37940             rows.push(crow);
37941             crow = {
37942                 start: cells[i],
37943                 end : cells[i]
37944             };
37945             
37946         }
37947         
37948         rows.push(crow);
37949         rec.els = [];
37950         rec.rows = rows;
37951         rec.cells = cells;
37952         for (var i = 0; i < cells.length;i++) {
37953             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37954             
37955         }
37956         
37957         
37958     },
37959     
37960     clearEvents: function() {
37961         
37962         if (!this.eventStore.getCount()) {
37963             return;
37964         }
37965         // reset number of rows in cells.
37966         Roo.each(this.cells.elements, function(c){
37967             c.rows = 0;
37968         });
37969         
37970         this.eventStore.each(function(e) {
37971             this.clearEvent(e);
37972         },this);
37973         
37974     },
37975     
37976     clearEvent : function(ev)
37977     {
37978         if (ev.els) {
37979             Roo.each(ev.els, function(el) {
37980                 el.un('mouseenter' ,this.onEventEnter, this);
37981                 el.un('mouseleave' ,this.onEventLeave, this);
37982                 el.remove();
37983             },this);
37984             ev.els = [];
37985         }
37986     },
37987     
37988     
37989     renderEvent : function(ev,ctr) {
37990         if (!ctr) {
37991              ctr = this.view.el.select('.fc-event-container',true).first();
37992         }
37993         
37994          
37995         this.clearEvent(ev);
37996             //code
37997        
37998         
37999         
38000         ev.els = [];
38001         var cells = ev.cells;
38002         var rows = ev.rows;
38003         this.fireEvent('eventrender', this, ev);
38004         
38005         for(var i =0; i < rows.length; i++) {
38006             
38007             cls = '';
38008             if (i == 0) {
38009                 cls += ' fc-event-start';
38010             }
38011             if ((i+1) == rows.length) {
38012                 cls += ' fc-event-end';
38013             }
38014             
38015             //Roo.log(ev.data);
38016             // how many rows should it span..
38017             var cg = this.eventTmpl.append(ctr,Roo.apply({
38018                 fccls : cls
38019                 
38020             }, ev.data) , true);
38021             
38022             
38023             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38024             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38025             cg.on('click', this.onEventClick, this, ev);
38026             
38027             ev.els.push(cg);
38028             
38029             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38030             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38031             //Roo.log(cg);
38032              
38033             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38034             cg.setWidth(ebox.right - sbox.x -2);
38035         }
38036     },
38037     
38038     renderEvents: function()
38039     {   
38040         // first make sure there is enough space..
38041         
38042         if (!this.eventTmpl) {
38043             this.eventTmpl = new Roo.Template(
38044                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38045                     '<div class="fc-event-inner">' +
38046                         '<span class="fc-event-time">{time}</span>' +
38047                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38048                     '</div>' +
38049                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38050                 '</div>'
38051             );
38052                 
38053         }
38054                
38055         
38056         
38057         this.cells.each(function(c) {
38058             //Roo.log(c.select('.fc-day-content div',true).first());
38059             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38060         });
38061         
38062         var ctr = this.view.el.select('.fc-event-container',true).first();
38063         
38064         var cls;
38065         this.eventStore.each(function(ev){
38066             
38067             this.renderEvent(ev);
38068              
38069              
38070         }, this);
38071         this.view.layout();
38072         
38073     },
38074     
38075     onEventEnter: function (e, el,event,d) {
38076         this.fireEvent('evententer', this, el, event);
38077     },
38078     
38079     onEventLeave: function (e, el,event,d) {
38080         this.fireEvent('eventleave', this, el, event);
38081     },
38082     
38083     onEventClick: function (e, el,event,d) {
38084         this.fireEvent('eventclick', this, el, event);
38085     },
38086     
38087     onMonthChange: function () {
38088         this.store.load();
38089     },
38090     
38091     onLoad: function () {
38092         
38093         //Roo.log('calendar onload');
38094 //         
38095         if(this.eventStore.getCount() > 0){
38096             
38097            
38098             
38099             this.eventStore.each(function(d){
38100                 
38101                 
38102                 // FIXME..
38103                 var add =   d.data;
38104                 if (typeof(add.end_dt) == 'undefined')  {
38105                     Roo.log("Missing End time in calendar data: ");
38106                     Roo.log(d);
38107                     return;
38108                 }
38109                 if (typeof(add.start_dt) == 'undefined')  {
38110                     Roo.log("Missing Start time in calendar data: ");
38111                     Roo.log(d);
38112                     return;
38113                 }
38114                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38115                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38116                 add.id = add.id || d.id;
38117                 add.title = add.title || '??';
38118                 
38119                 this.addItem(d);
38120                 
38121              
38122             },this);
38123         }
38124         
38125         this.renderEvents();
38126     }
38127     
38128
38129 });
38130 /*
38131  grid : {
38132                 xtype: 'Grid',
38133                 xns: Roo.grid,
38134                 listeners : {
38135                     render : function ()
38136                     {
38137                         _this.grid = this;
38138                         
38139                         if (!this.view.el.hasClass('course-timesheet')) {
38140                             this.view.el.addClass('course-timesheet');
38141                         }
38142                         if (this.tsStyle) {
38143                             this.ds.load({});
38144                             return; 
38145                         }
38146                         Roo.log('width');
38147                         Roo.log(_this.grid.view.el.getWidth());
38148                         
38149                         
38150                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38151                             '.course-timesheet .x-grid-row' : {
38152                                 height: '80px'
38153                             },
38154                             '.x-grid-row td' : {
38155                                 'vertical-align' : 0
38156                             },
38157                             '.course-edit-link' : {
38158                                 'color' : 'blue',
38159                                 'text-overflow' : 'ellipsis',
38160                                 'overflow' : 'hidden',
38161                                 'white-space' : 'nowrap',
38162                                 'cursor' : 'pointer'
38163                             },
38164                             '.sub-link' : {
38165                                 'color' : 'green'
38166                             },
38167                             '.de-act-sup-link' : {
38168                                 'color' : 'purple',
38169                                 'text-decoration' : 'line-through'
38170                             },
38171                             '.de-act-link' : {
38172                                 'color' : 'red',
38173                                 'text-decoration' : 'line-through'
38174                             },
38175                             '.course-timesheet .course-highlight' : {
38176                                 'border-top-style': 'dashed !important',
38177                                 'border-bottom-bottom': 'dashed !important'
38178                             },
38179                             '.course-timesheet .course-item' : {
38180                                 'font-family'   : 'tahoma, arial, helvetica',
38181                                 'font-size'     : '11px',
38182                                 'overflow'      : 'hidden',
38183                                 'padding-left'  : '10px',
38184                                 'padding-right' : '10px',
38185                                 'padding-top' : '10px' 
38186                             }
38187                             
38188                         }, Roo.id());
38189                                 this.ds.load({});
38190                     }
38191                 },
38192                 autoWidth : true,
38193                 monitorWindowResize : false,
38194                 cellrenderer : function(v,x,r)
38195                 {
38196                     return v;
38197                 },
38198                 sm : {
38199                     xtype: 'CellSelectionModel',
38200                     xns: Roo.grid
38201                 },
38202                 dataSource : {
38203                     xtype: 'Store',
38204                     xns: Roo.data,
38205                     listeners : {
38206                         beforeload : function (_self, options)
38207                         {
38208                             options.params = options.params || {};
38209                             options.params._month = _this.monthField.getValue();
38210                             options.params.limit = 9999;
38211                             options.params['sort'] = 'when_dt';    
38212                             options.params['dir'] = 'ASC';    
38213                             this.proxy.loadResponse = this.loadResponse;
38214                             Roo.log("load?");
38215                             //this.addColumns();
38216                         },
38217                         load : function (_self, records, options)
38218                         {
38219                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38220                                 // if you click on the translation.. you can edit it...
38221                                 var el = Roo.get(this);
38222                                 var id = el.dom.getAttribute('data-id');
38223                                 var d = el.dom.getAttribute('data-date');
38224                                 var t = el.dom.getAttribute('data-time');
38225                                 //var id = this.child('span').dom.textContent;
38226                                 
38227                                 //Roo.log(this);
38228                                 Pman.Dialog.CourseCalendar.show({
38229                                     id : id,
38230                                     when_d : d,
38231                                     when_t : t,
38232                                     productitem_active : id ? 1 : 0
38233                                 }, function() {
38234                                     _this.grid.ds.load({});
38235                                 });
38236                            
38237                            });
38238                            
38239                            _this.panel.fireEvent('resize', [ '', '' ]);
38240                         }
38241                     },
38242                     loadResponse : function(o, success, response){
38243                             // this is overridden on before load..
38244                             
38245                             Roo.log("our code?");       
38246                             //Roo.log(success);
38247                             //Roo.log(response)
38248                             delete this.activeRequest;
38249                             if(!success){
38250                                 this.fireEvent("loadexception", this, o, response);
38251                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38252                                 return;
38253                             }
38254                             var result;
38255                             try {
38256                                 result = o.reader.read(response);
38257                             }catch(e){
38258                                 Roo.log("load exception?");
38259                                 this.fireEvent("loadexception", this, o, response, e);
38260                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38261                                 return;
38262                             }
38263                             Roo.log("ready...");        
38264                             // loop through result.records;
38265                             // and set this.tdate[date] = [] << array of records..
38266                             _this.tdata  = {};
38267                             Roo.each(result.records, function(r){
38268                                 //Roo.log(r.data);
38269                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38270                                     _this.tdata[r.data.when_dt.format('j')] = [];
38271                                 }
38272                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38273                             });
38274                             
38275                             //Roo.log(_this.tdata);
38276                             
38277                             result.records = [];
38278                             result.totalRecords = 6;
38279                     
38280                             // let's generate some duumy records for the rows.
38281                             //var st = _this.dateField.getValue();
38282                             
38283                             // work out monday..
38284                             //st = st.add(Date.DAY, -1 * st.format('w'));
38285                             
38286                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38287                             
38288                             var firstOfMonth = date.getFirstDayOfMonth();
38289                             var days = date.getDaysInMonth();
38290                             var d = 1;
38291                             var firstAdded = false;
38292                             for (var i = 0; i < result.totalRecords ; i++) {
38293                                 //var d= st.add(Date.DAY, i);
38294                                 var row = {};
38295                                 var added = 0;
38296                                 for(var w = 0 ; w < 7 ; w++){
38297                                     if(!firstAdded && firstOfMonth != w){
38298                                         continue;
38299                                     }
38300                                     if(d > days){
38301                                         continue;
38302                                     }
38303                                     firstAdded = true;
38304                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38305                                     row['weekday'+w] = String.format(
38306                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38307                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38308                                                     d,
38309                                                     date.format('Y-m-')+dd
38310                                                 );
38311                                     added++;
38312                                     if(typeof(_this.tdata[d]) != 'undefined'){
38313                                         Roo.each(_this.tdata[d], function(r){
38314                                             var is_sub = '';
38315                                             var deactive = '';
38316                                             var id = r.id;
38317                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38318                                             if(r.parent_id*1>0){
38319                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38320                                                 id = r.parent_id;
38321                                             }
38322                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38323                                                 deactive = 'de-act-link';
38324                                             }
38325                                             
38326                                             row['weekday'+w] += String.format(
38327                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38328                                                     id, //0
38329                                                     r.product_id_name, //1
38330                                                     r.when_dt.format('h:ia'), //2
38331                                                     is_sub, //3
38332                                                     deactive, //4
38333                                                     desc // 5
38334                                             );
38335                                         });
38336                                     }
38337                                     d++;
38338                                 }
38339                                 
38340                                 // only do this if something added..
38341                                 if(added > 0){ 
38342                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38343                                 }
38344                                 
38345                                 
38346                                 // push it twice. (second one with an hour..
38347                                 
38348                             }
38349                             //Roo.log(result);
38350                             this.fireEvent("load", this, o, o.request.arg);
38351                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38352                         },
38353                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38354                     proxy : {
38355                         xtype: 'HttpProxy',
38356                         xns: Roo.data,
38357                         method : 'GET',
38358                         url : baseURL + '/Roo/Shop_course.php'
38359                     },
38360                     reader : {
38361                         xtype: 'JsonReader',
38362                         xns: Roo.data,
38363                         id : 'id',
38364                         fields : [
38365                             {
38366                                 'name': 'id',
38367                                 'type': 'int'
38368                             },
38369                             {
38370                                 'name': 'when_dt',
38371                                 'type': 'string'
38372                             },
38373                             {
38374                                 'name': 'end_dt',
38375                                 'type': 'string'
38376                             },
38377                             {
38378                                 'name': 'parent_id',
38379                                 'type': 'int'
38380                             },
38381                             {
38382                                 'name': 'product_id',
38383                                 'type': 'int'
38384                             },
38385                             {
38386                                 'name': 'productitem_id',
38387                                 'type': 'int'
38388                             },
38389                             {
38390                                 'name': 'guid',
38391                                 'type': 'int'
38392                             }
38393                         ]
38394                     }
38395                 },
38396                 toolbar : {
38397                     xtype: 'Toolbar',
38398                     xns: Roo,
38399                     items : [
38400                         {
38401                             xtype: 'Button',
38402                             xns: Roo.Toolbar,
38403                             listeners : {
38404                                 click : function (_self, e)
38405                                 {
38406                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38407                                     sd.setMonth(sd.getMonth()-1);
38408                                     _this.monthField.setValue(sd.format('Y-m-d'));
38409                                     _this.grid.ds.load({});
38410                                 }
38411                             },
38412                             text : "Back"
38413                         },
38414                         {
38415                             xtype: 'Separator',
38416                             xns: Roo.Toolbar
38417                         },
38418                         {
38419                             xtype: 'MonthField',
38420                             xns: Roo.form,
38421                             listeners : {
38422                                 render : function (_self)
38423                                 {
38424                                     _this.monthField = _self;
38425                                    // _this.monthField.set  today
38426                                 },
38427                                 select : function (combo, date)
38428                                 {
38429                                     _this.grid.ds.load({});
38430                                 }
38431                             },
38432                             value : (function() { return new Date(); })()
38433                         },
38434                         {
38435                             xtype: 'Separator',
38436                             xns: Roo.Toolbar
38437                         },
38438                         {
38439                             xtype: 'TextItem',
38440                             xns: Roo.Toolbar,
38441                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38442                         },
38443                         {
38444                             xtype: 'Fill',
38445                             xns: Roo.Toolbar
38446                         },
38447                         {
38448                             xtype: 'Button',
38449                             xns: Roo.Toolbar,
38450                             listeners : {
38451                                 click : function (_self, e)
38452                                 {
38453                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38454                                     sd.setMonth(sd.getMonth()+1);
38455                                     _this.monthField.setValue(sd.format('Y-m-d'));
38456                                     _this.grid.ds.load({});
38457                                 }
38458                             },
38459                             text : "Next"
38460                         }
38461                     ]
38462                 },
38463                  
38464             }
38465         };
38466         
38467         *//*
38468  * Based on:
38469  * Ext JS Library 1.1.1
38470  * Copyright(c) 2006-2007, Ext JS, LLC.
38471  *
38472  * Originally Released Under LGPL - original licence link has changed is not relivant.
38473  *
38474  * Fork - LGPL
38475  * <script type="text/javascript">
38476  */
38477  
38478 /**
38479  * @class Roo.LoadMask
38480  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38481  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38482  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38483  * element's UpdateManager load indicator and will be destroyed after the initial load.
38484  * @constructor
38485  * Create a new LoadMask
38486  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38487  * @param {Object} config The config object
38488  */
38489 Roo.LoadMask = function(el, config){
38490     this.el = Roo.get(el);
38491     Roo.apply(this, config);
38492     if(this.store){
38493         this.store.on('beforeload', this.onBeforeLoad, this);
38494         this.store.on('load', this.onLoad, this);
38495         this.store.on('loadexception', this.onLoadException, this);
38496         this.removeMask = false;
38497     }else{
38498         var um = this.el.getUpdateManager();
38499         um.showLoadIndicator = false; // disable the default indicator
38500         um.on('beforeupdate', this.onBeforeLoad, this);
38501         um.on('update', this.onLoad, this);
38502         um.on('failure', this.onLoad, this);
38503         this.removeMask = true;
38504     }
38505 };
38506
38507 Roo.LoadMask.prototype = {
38508     /**
38509      * @cfg {Boolean} removeMask
38510      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38511      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38512      */
38513     removeMask : false,
38514     /**
38515      * @cfg {String} msg
38516      * The text to display in a centered loading message box (defaults to 'Loading...')
38517      */
38518     msg : 'Loading...',
38519     /**
38520      * @cfg {String} msgCls
38521      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38522      */
38523     msgCls : 'x-mask-loading',
38524
38525     /**
38526      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38527      * @type Boolean
38528      */
38529     disabled: false,
38530
38531     /**
38532      * Disables the mask to prevent it from being displayed
38533      */
38534     disable : function(){
38535        this.disabled = true;
38536     },
38537
38538     /**
38539      * Enables the mask so that it can be displayed
38540      */
38541     enable : function(){
38542         this.disabled = false;
38543     },
38544     
38545     onLoadException : function()
38546     {
38547         Roo.log(arguments);
38548         
38549         if (typeof(arguments[3]) != 'undefined') {
38550             Roo.MessageBox.alert("Error loading",arguments[3]);
38551         } 
38552         /*
38553         try {
38554             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38555                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38556             }   
38557         } catch(e) {
38558             
38559         }
38560         */
38561     
38562         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38563     },
38564     // private
38565     onLoad : function()
38566     {
38567         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38568     },
38569
38570     // private
38571     onBeforeLoad : function(){
38572         if(!this.disabled){
38573             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38574         }
38575     },
38576
38577     // private
38578     destroy : function(){
38579         if(this.store){
38580             this.store.un('beforeload', this.onBeforeLoad, this);
38581             this.store.un('load', this.onLoad, this);
38582             this.store.un('loadexception', this.onLoadException, this);
38583         }else{
38584             var um = this.el.getUpdateManager();
38585             um.un('beforeupdate', this.onBeforeLoad, this);
38586             um.un('update', this.onLoad, this);
38587             um.un('failure', this.onLoad, this);
38588         }
38589     }
38590 };/*
38591  * Based on:
38592  * Ext JS Library 1.1.1
38593  * Copyright(c) 2006-2007, Ext JS, LLC.
38594  *
38595  * Originally Released Under LGPL - original licence link has changed is not relivant.
38596  *
38597  * Fork - LGPL
38598  * <script type="text/javascript">
38599  */
38600
38601
38602 /**
38603  * @class Roo.XTemplate
38604  * @extends Roo.Template
38605  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38606 <pre><code>
38607 var t = new Roo.XTemplate(
38608         '&lt;select name="{name}"&gt;',
38609                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38610         '&lt;/select&gt;'
38611 );
38612  
38613 // then append, applying the master template values
38614  </code></pre>
38615  *
38616  * Supported features:
38617  *
38618  *  Tags:
38619
38620 <pre><code>
38621       {a_variable} - output encoded.
38622       {a_variable.format:("Y-m-d")} - call a method on the variable
38623       {a_variable:raw} - unencoded output
38624       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38625       {a_variable:this.method_on_template(...)} - call a method on the template object.
38626  
38627 </code></pre>
38628  *  The tpl tag:
38629 <pre><code>
38630         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38631         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38632         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38633         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38634   
38635         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38636         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38637 </code></pre>
38638  *      
38639  */
38640 Roo.XTemplate = function()
38641 {
38642     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38643     if (this.html) {
38644         this.compile();
38645     }
38646 };
38647
38648
38649 Roo.extend(Roo.XTemplate, Roo.Template, {
38650
38651     /**
38652      * The various sub templates
38653      */
38654     tpls : false,
38655     /**
38656      *
38657      * basic tag replacing syntax
38658      * WORD:WORD()
38659      *
38660      * // you can fake an object call by doing this
38661      *  x.t:(test,tesT) 
38662      * 
38663      */
38664     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38665
38666     /**
38667      * compile the template
38668      *
38669      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38670      *
38671      */
38672     compile: function()
38673     {
38674         var s = this.html;
38675      
38676         s = ['<tpl>', s, '</tpl>'].join('');
38677     
38678         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38679             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38680             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38681             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38682             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38683             m,
38684             id     = 0,
38685             tpls   = [];
38686     
38687         while(true == !!(m = s.match(re))){
38688             var forMatch   = m[0].match(nameRe),
38689                 ifMatch   = m[0].match(ifRe),
38690                 execMatch   = m[0].match(execRe),
38691                 namedMatch   = m[0].match(namedRe),
38692                 
38693                 exp  = null, 
38694                 fn   = null,
38695                 exec = null,
38696                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38697                 
38698             if (ifMatch) {
38699                 // if - puts fn into test..
38700                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38701                 if(exp){
38702                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38703                 }
38704             }
38705             
38706             if (execMatch) {
38707                 // exec - calls a function... returns empty if true is  returned.
38708                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38709                 if(exp){
38710                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38711                 }
38712             }
38713             
38714             
38715             if (name) {
38716                 // for = 
38717                 switch(name){
38718                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38719                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38720                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38721                 }
38722             }
38723             var uid = namedMatch ? namedMatch[1] : id;
38724             
38725             
38726             tpls.push({
38727                 id:     namedMatch ? namedMatch[1] : id,
38728                 target: name,
38729                 exec:   exec,
38730                 test:   fn,
38731                 body:   m[1] || ''
38732             });
38733             if (namedMatch) {
38734                 s = s.replace(m[0], '');
38735             } else { 
38736                 s = s.replace(m[0], '{xtpl'+ id + '}');
38737             }
38738             ++id;
38739         }
38740         this.tpls = [];
38741         for(var i = tpls.length-1; i >= 0; --i){
38742             this.compileTpl(tpls[i]);
38743             this.tpls[tpls[i].id] = tpls[i];
38744         }
38745         this.master = tpls[tpls.length-1];
38746         return this;
38747     },
38748     /**
38749      * same as applyTemplate, except it's done to one of the subTemplates
38750      * when using named templates, you can do:
38751      *
38752      * var str = pl.applySubTemplate('your-name', values);
38753      *
38754      * 
38755      * @param {Number} id of the template
38756      * @param {Object} values to apply to template
38757      * @param {Object} parent (normaly the instance of this object)
38758      */
38759     applySubTemplate : function(id, values, parent)
38760     {
38761         
38762         
38763         var t = this.tpls[id];
38764         
38765         
38766         try { 
38767             if(t.test && !t.test.call(this, values, parent)){
38768                 return '';
38769             }
38770         } catch(e) {
38771             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38772             Roo.log(e.toString());
38773             Roo.log(t.test);
38774             return ''
38775         }
38776         try { 
38777             
38778             if(t.exec && t.exec.call(this, values, parent)){
38779                 return '';
38780             }
38781         } catch(e) {
38782             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38783             Roo.log(e.toString());
38784             Roo.log(t.exec);
38785             return ''
38786         }
38787         try {
38788             var vs = t.target ? t.target.call(this, values, parent) : values;
38789             parent = t.target ? values : parent;
38790             if(t.target && vs instanceof Array){
38791                 var buf = [];
38792                 for(var i = 0, len = vs.length; i < len; i++){
38793                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38794                 }
38795                 return buf.join('');
38796             }
38797             return t.compiled.call(this, vs, parent);
38798         } catch (e) {
38799             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38800             Roo.log(e.toString());
38801             Roo.log(t.compiled);
38802             return '';
38803         }
38804     },
38805
38806     compileTpl : function(tpl)
38807     {
38808         var fm = Roo.util.Format;
38809         var useF = this.disableFormats !== true;
38810         var sep = Roo.isGecko ? "+" : ",";
38811         var undef = function(str) {
38812             Roo.log("Property not found :"  + str);
38813             return '';
38814         };
38815         
38816         var fn = function(m, name, format, args)
38817         {
38818             //Roo.log(arguments);
38819             args = args ? args.replace(/\\'/g,"'") : args;
38820             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38821             if (typeof(format) == 'undefined') {
38822                 format= 'htmlEncode';
38823             }
38824             if (format == 'raw' ) {
38825                 format = false;
38826             }
38827             
38828             if(name.substr(0, 4) == 'xtpl'){
38829                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38830             }
38831             
38832             // build an array of options to determine if value is undefined..
38833             
38834             // basically get 'xxxx.yyyy' then do
38835             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38836             //    (function () { Roo.log("Property not found"); return ''; })() :
38837             //    ......
38838             
38839             var udef_ar = [];
38840             var lookfor = '';
38841             Roo.each(name.split('.'), function(st) {
38842                 lookfor += (lookfor.length ? '.': '') + st;
38843                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38844             });
38845             
38846             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38847             
38848             
38849             if(format && useF){
38850                 
38851                 args = args ? ',' + args : "";
38852                  
38853                 if(format.substr(0, 5) != "this."){
38854                     format = "fm." + format + '(';
38855                 }else{
38856                     format = 'this.call("'+ format.substr(5) + '", ';
38857                     args = ", values";
38858                 }
38859                 
38860                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38861             }
38862              
38863             if (args.length) {
38864                 // called with xxyx.yuu:(test,test)
38865                 // change to ()
38866                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38867             }
38868             // raw.. - :raw modifier..
38869             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38870             
38871         };
38872         var body;
38873         // branched to use + in gecko and [].join() in others
38874         if(Roo.isGecko){
38875             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38876                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38877                     "';};};";
38878         }else{
38879             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38880             body.push(tpl.body.replace(/(\r\n|\n)/g,
38881                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38882             body.push("'].join('');};};");
38883             body = body.join('');
38884         }
38885         
38886         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38887        
38888         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38889         eval(body);
38890         
38891         return this;
38892     },
38893
38894     applyTemplate : function(values){
38895         return this.master.compiled.call(this, values, {});
38896         //var s = this.subs;
38897     },
38898
38899     apply : function(){
38900         return this.applyTemplate.apply(this, arguments);
38901     }
38902
38903  });
38904
38905 Roo.XTemplate.from = function(el){
38906     el = Roo.getDom(el);
38907     return new Roo.XTemplate(el.value || el.innerHTML);
38908 };