roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787     
788      /**
789      * using 'cn' the nested child reader read the child array into it's child stores.
790      * @param {Object} rec The record with a 'children array
791      */
792     loadDataFromChildren : function(rec)
793     {
794         this.loadData(this.reader.toLoadData(rec));
795     },
796     
797
798     /**
799      * Gets the number of cached records.
800      * <p>
801      * <em>If using paging, this may not be the total size of the dataset. If the data object
802      * used by the Reader contains the dataset size, then the getTotalCount() function returns
803      * the data set size</em>
804      */
805     getCount : function(){
806         return this.data.length || 0;
807     },
808
809     /**
810      * Gets the total number of records in the dataset as returned by the server.
811      * <p>
812      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813      * the dataset size</em>
814      */
815     getTotalCount : function(){
816         return this.totalLength || 0;
817     },
818
819     /**
820      * Returns the sort state of the Store as an object with two properties:
821      * <pre><code>
822  field {String} The name of the field by which the Records are sorted
823  direction {String} The sort order, "ASC" or "DESC"
824      * </code></pre>
825      */
826     getSortState : function(){
827         return this.sortInfo;
828     },
829
830     // private
831     applySort : function(){
832         if(this.sortInfo && !this.remoteSort){
833             var s = this.sortInfo, f = s.field;
834             var st = this.fields.get(f).sortType;
835             var fn = function(r1, r2){
836                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
838             };
839             this.data.sort(s.direction, fn);
840             if(this.snapshot && this.snapshot != this.data){
841                 this.snapshot.sort(s.direction, fn);
842             }
843         }
844     },
845
846     /**
847      * Sets the default sort column and order to be used by the next load operation.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     setDefaultSort : function(field, dir){
852         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
853     },
854
855     /**
856      * Sort the Records.
857      * If remote sorting is used, the sort is performed on the server, and the cache is
858      * reloaded. If local sorting is used, the cache is sorted internally.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     sort : function(fieldName, dir){
863         var f = this.fields.get(fieldName);
864         if(!dir){
865             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
866             
867             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
869             }else{
870                 dir = f.sortDir;
871             }
872         }
873         this.sortToggle[f.name] = dir;
874         this.sortInfo = {field: f.name, direction: dir};
875         if(!this.remoteSort){
876             this.applySort();
877             this.fireEvent("datachanged", this);
878         }else{
879             this.load(this.lastOptions);
880         }
881     },
882
883     /**
884      * Calls the specified function for each of the Records in the cache.
885      * @param {Function} fn The function to call. The Record is passed as the first parameter.
886      * Returning <em>false</em> aborts and exits the iteration.
887      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
888      */
889     each : function(fn, scope){
890         this.data.each(fn, scope);
891     },
892
893     /**
894      * Gets all records modified since the last commit.  Modified records are persisted across load operations
895      * (e.g., during paging).
896      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
897      */
898     getModifiedRecords : function(){
899         return this.modified;
900     },
901
902     // private
903     createFilterFn : function(property, value, anyMatch){
904         if(!value.exec){ // not a regex
905             value = String(value);
906             if(value.length == 0){
907                 return false;
908             }
909             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
910         }
911         return function(r){
912             return value.test(r.data[property]);
913         };
914     },
915
916     /**
917      * Sums the value of <i>property</i> for each record between start and end and returns the result.
918      * @param {String} property A field on your records
919      * @param {Number} start The record index to start at (defaults to 0)
920      * @param {Number} end The last record index to include (defaults to length - 1)
921      * @return {Number} The sum
922      */
923     sum : function(property, start, end){
924         var rs = this.data.items, v = 0;
925         start = start || 0;
926         end = (end || end === 0) ? end : rs.length-1;
927
928         for(var i = start; i <= end; i++){
929             v += (rs[i].data[property] || 0);
930         }
931         return v;
932     },
933
934     /**
935      * Filter the records by a specified property.
936      * @param {String} field A field on your records
937      * @param {String/RegExp} value Either a string that the field
938      * should start with or a RegExp to test against the field
939      * @param {Boolean} anyMatch True to match any part not just the beginning
940      */
941     filter : function(property, value, anyMatch){
942         var fn = this.createFilterFn(property, value, anyMatch);
943         return fn ? this.filterBy(fn) : this.clearFilter();
944     },
945
946     /**
947      * Filter by a function. The specified function will be called with each
948      * record in this data source. If the function returns true the record is included,
949      * otherwise it is filtered.
950      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951      * @param {Object} scope (optional) The scope of the function (defaults to this)
952      */
953     filterBy : function(fn, scope){
954         this.snapshot = this.snapshot || this.data;
955         this.data = this.queryBy(fn, scope||this);
956         this.fireEvent("datachanged", this);
957     },
958
959     /**
960      * Query the records by a specified property.
961      * @param {String} field A field on your records
962      * @param {String/RegExp} value Either a string that the field
963      * should start with or a RegExp to test against the field
964      * @param {Boolean} anyMatch True to match any part not just the beginning
965      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
966      */
967     query : function(property, value, anyMatch){
968         var fn = this.createFilterFn(property, value, anyMatch);
969         return fn ? this.queryBy(fn) : this.data.clone();
970     },
971
972     /**
973      * Query by a function. The specified function will be called with each
974      * record in this data source. If the function returns true the record is included
975      * in the results.
976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977      * @param {Object} scope (optional) The scope of the function (defaults to this)
978       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
979      **/
980     queryBy : function(fn, scope){
981         var data = this.snapshot || this.data;
982         return data.filterBy(fn, scope||this);
983     },
984
985     /**
986      * Collects unique values for a particular dataIndex from this store.
987      * @param {String} dataIndex The property to collect
988      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990      * @return {Array} An array of the unique values
991      **/
992     collect : function(dataIndex, allowNull, bypassFilter){
993         var d = (bypassFilter === true && this.snapshot) ?
994                 this.snapshot.items : this.data.items;
995         var v, sv, r = [], l = {};
996         for(var i = 0, len = d.length; i < len; i++){
997             v = d[i].data[dataIndex];
998             sv = String(v);
999             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1000                 l[sv] = true;
1001                 r[r.length] = v;
1002             }
1003         }
1004         return r;
1005     },
1006
1007     /**
1008      * Revert to a view of the Record cache with no filtering applied.
1009      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1010      */
1011     clearFilter : function(suppressEvent){
1012         if(this.snapshot && this.snapshot != this.data){
1013             this.data = this.snapshot;
1014             delete this.snapshot;
1015             if(suppressEvent !== true){
1016                 this.fireEvent("datachanged", this);
1017             }
1018         }
1019     },
1020
1021     // private
1022     afterEdit : function(record){
1023         if(this.modified.indexOf(record) == -1){
1024             this.modified.push(record);
1025         }
1026         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1027     },
1028     
1029     // private
1030     afterReject : function(record){
1031         this.modified.remove(record);
1032         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1033     },
1034
1035     // private
1036     afterCommit : function(record){
1037         this.modified.remove(record);
1038         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1039     },
1040
1041     /**
1042      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1044      */
1045     commitChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].commit();
1050         }
1051     },
1052
1053     /**
1054      * Cancel outstanding changes on all changed records.
1055      */
1056     rejectChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].reject();
1061         }
1062     },
1063
1064     onMetaChange : function(meta, rtype, o){
1065         this.recordType = rtype;
1066         this.fields = rtype.prototype.fields;
1067         delete this.snapshot;
1068         this.sortInfo = meta.sortInfo || this.sortInfo;
1069         this.modified = [];
1070         this.fireEvent('metachange', this, this.reader.meta);
1071     },
1072     
1073     moveIndex : function(data, type)
1074     {
1075         var index = this.indexOf(data);
1076         
1077         var newIndex = index + type;
1078         
1079         this.remove(data);
1080         
1081         this.insert(newIndex, data);
1082         
1083     }
1084 });/*
1085  * Based on:
1086  * Ext JS Library 1.1.1
1087  * Copyright(c) 2006-2007, Ext JS, LLC.
1088  *
1089  * Originally Released Under LGPL - original licence link has changed is not relivant.
1090  *
1091  * Fork - LGPL
1092  * <script type="text/javascript">
1093  */
1094
1095 /**
1096  * @class Roo.data.SimpleStore
1097  * @extends Roo.data.Store
1098  * Small helper class to make creating Stores from Array data easier.
1099  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100  * @cfg {Array} fields An array of field definition objects, or field name strings.
1101  * @cfg {Object} an existing reader (eg. copied from another store)
1102  * @cfg {Array} data The multi-dimensional array of data
1103  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1104  * @cfg {Roo.data.Reader} reader  [not-required] 
1105  * @constructor
1106  * @param {Object} config
1107  */
1108 Roo.data.SimpleStore = function(config)
1109 {
1110     Roo.data.SimpleStore.superclass.constructor.call(this, {
1111         isLocal : true,
1112         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1113                 id: config.id
1114             },
1115             Roo.data.Record.create(config.fields)
1116         ),
1117         proxy : new Roo.data.MemoryProxy(config.data)
1118     });
1119     this.load();
1120 };
1121 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1122  * Based on:
1123  * Ext JS Library 1.1.1
1124  * Copyright(c) 2006-2007, Ext JS, LLC.
1125  *
1126  * Originally Released Under LGPL - original licence link has changed is not relivant.
1127  *
1128  * Fork - LGPL
1129  * <script type="text/javascript">
1130  */
1131
1132 /**
1133 /**
1134  * @extends Roo.data.Store
1135  * @class Roo.data.JsonStore
1136  * Small helper class to make creating Stores for JSON data easier. <br/>
1137 <pre><code>
1138 var store = new Roo.data.JsonStore({
1139     url: 'get-images.php',
1140     root: 'images',
1141     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1142 });
1143 </code></pre>
1144  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1145  * JsonReader and HttpProxy (unless inline data is provided).</b>
1146  * @cfg {Array} fields An array of field definition objects, or field name strings.
1147  * @constructor
1148  * @param {Object} config
1149  */
1150 Roo.data.JsonStore = function(c){
1151     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1152         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1153         reader: new Roo.data.JsonReader(c, c.fields)
1154     }));
1155 };
1156 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1157  * Based on:
1158  * Ext JS Library 1.1.1
1159  * Copyright(c) 2006-2007, Ext JS, LLC.
1160  *
1161  * Originally Released Under LGPL - original licence link has changed is not relivant.
1162  *
1163  * Fork - LGPL
1164  * <script type="text/javascript">
1165  */
1166
1167  
1168 Roo.data.Field = function(config){
1169     if(typeof config == "string"){
1170         config = {name: config};
1171     }
1172     Roo.apply(this, config);
1173     
1174     if(!this.type){
1175         this.type = "auto";
1176     }
1177     
1178     var st = Roo.data.SortTypes;
1179     // named sortTypes are supported, here we look them up
1180     if(typeof this.sortType == "string"){
1181         this.sortType = st[this.sortType];
1182     }
1183     
1184     // set default sortType for strings and dates
1185     if(!this.sortType){
1186         switch(this.type){
1187             case "string":
1188                 this.sortType = st.asUCString;
1189                 break;
1190             case "date":
1191                 this.sortType = st.asDate;
1192                 break;
1193             default:
1194                 this.sortType = st.none;
1195         }
1196     }
1197
1198     // define once
1199     var stripRe = /[\$,%]/g;
1200
1201     // prebuilt conversion function for this field, instead of
1202     // switching every time we're reading a value
1203     if(!this.convert){
1204         var cv, dateFormat = this.dateFormat;
1205         switch(this.type){
1206             case "":
1207             case "auto":
1208             case undefined:
1209                 cv = function(v){ return v; };
1210                 break;
1211             case "string":
1212                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1213                 break;
1214             case "int":
1215                 cv = function(v){
1216                     return v !== undefined && v !== null && v !== '' ?
1217                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1218                     };
1219                 break;
1220             case "float":
1221                 cv = function(v){
1222                     return v !== undefined && v !== null && v !== '' ?
1223                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1224                     };
1225                 break;
1226             case "bool":
1227             case "boolean":
1228                 cv = function(v){ return v === true || v === "true" || v == 1; };
1229                 break;
1230             case "date":
1231                 cv = function(v){
1232                     if(!v){
1233                         return '';
1234                     }
1235                     if(v instanceof Date){
1236                         return v;
1237                     }
1238                     if(dateFormat){
1239                         if(dateFormat == "timestamp"){
1240                             return new Date(v*1000);
1241                         }
1242                         return Date.parseDate(v, dateFormat);
1243                     }
1244                     var parsed = Date.parse(v);
1245                     return parsed ? new Date(parsed) : null;
1246                 };
1247              break;
1248             
1249         }
1250         this.convert = cv;
1251     }
1252 };
1253
1254 Roo.data.Field.prototype = {
1255     dateFormat: null,
1256     defaultValue: "",
1257     mapping: null,
1258     sortType : null,
1259     sortDir : "ASC"
1260 };/*
1261  * Based on:
1262  * Ext JS Library 1.1.1
1263  * Copyright(c) 2006-2007, Ext JS, LLC.
1264  *
1265  * Originally Released Under LGPL - original licence link has changed is not relivant.
1266  *
1267  * Fork - LGPL
1268  * <script type="text/javascript">
1269  */
1270  
1271 // Base class for reading structured data from a data source.  This class is intended to be
1272 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1273
1274 /**
1275  * @class Roo.data.DataReader
1276  * @abstract
1277  * Base class for reading structured data from a data source.  This class is intended to be
1278  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1279  */
1280
1281 Roo.data.DataReader = function(meta, recordType){
1282     
1283     this.meta = meta;
1284     
1285     this.recordType = recordType instanceof Array ? 
1286         Roo.data.Record.create(recordType) : recordType;
1287 };
1288
1289 Roo.data.DataReader.prototype = {
1290     
1291     
1292     readerType : 'Data',
1293      /**
1294      * Create an empty record
1295      * @param {Object} data (optional) - overlay some values
1296      * @return {Roo.data.Record} record created.
1297      */
1298     newRow :  function(d) {
1299         var da =  {};
1300         this.recordType.prototype.fields.each(function(c) {
1301             switch( c.type) {
1302                 case 'int' : da[c.name] = 0; break;
1303                 case 'date' : da[c.name] = new Date(); break;
1304                 case 'float' : da[c.name] = 0.0; break;
1305                 case 'boolean' : da[c.name] = false; break;
1306                 default : da[c.name] = ""; break;
1307             }
1308             
1309         });
1310         return new this.recordType(Roo.apply(da, d));
1311     }
1312     
1313     
1314 };/*
1315  * Based on:
1316  * Ext JS Library 1.1.1
1317  * Copyright(c) 2006-2007, Ext JS, LLC.
1318  *
1319  * Originally Released Under LGPL - original licence link has changed is not relivant.
1320  *
1321  * Fork - LGPL
1322  * <script type="text/javascript">
1323  */
1324
1325 /**
1326  * @class Roo.data.DataProxy
1327  * @extends Roo.data.Observable
1328  * @abstract
1329  * This class is an abstract base class for implementations which provide retrieval of
1330  * unformatted data objects.<br>
1331  * <p>
1332  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1333  * (of the appropriate type which knows how to parse the data object) to provide a block of
1334  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1335  * <p>
1336  * Custom implementations must implement the load method as described in
1337  * {@link Roo.data.HttpProxy#load}.
1338  */
1339 Roo.data.DataProxy = function(){
1340     this.addEvents({
1341         /**
1342          * @event beforeload
1343          * Fires before a network request is made to retrieve a data object.
1344          * @param {Object} This DataProxy object.
1345          * @param {Object} params The params parameter to the load function.
1346          */
1347         beforeload : true,
1348         /**
1349          * @event load
1350          * Fires before the load method's callback is called.
1351          * @param {Object} This DataProxy object.
1352          * @param {Object} o The data object.
1353          * @param {Object} arg The callback argument object passed to the load function.
1354          */
1355         load : true,
1356         /**
1357          * @event loadexception
1358          * Fires if an Exception occurs during data retrieval.
1359          * @param {Object} This DataProxy object.
1360          * @param {Object} o The data object.
1361          * @param {Object} arg The callback argument object passed to the load function.
1362          * @param {Object} e The Exception.
1363          */
1364         loadexception : true
1365     });
1366     Roo.data.DataProxy.superclass.constructor.call(this);
1367 };
1368
1369 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1370
1371     /**
1372      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1373      */
1374 /*
1375  * Based on:
1376  * Ext JS Library 1.1.1
1377  * Copyright(c) 2006-2007, Ext JS, LLC.
1378  *
1379  * Originally Released Under LGPL - original licence link has changed is not relivant.
1380  *
1381  * Fork - LGPL
1382  * <script type="text/javascript">
1383  */
1384 /**
1385  * @class Roo.data.MemoryProxy
1386  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1387  * to the Reader when its load method is called.
1388  * @constructor
1389  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1390  */
1391 Roo.data.MemoryProxy = function(data){
1392     if (data.data) {
1393         data = data.data;
1394     }
1395     Roo.data.MemoryProxy.superclass.constructor.call(this);
1396     this.data = data;
1397 };
1398
1399 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1400     
1401     /**
1402      * Load data from the requested source (in this case an in-memory
1403      * data object passed to the constructor), read the data object into
1404      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1405      * process that block using the passed callback.
1406      * @param {Object} params This parameter is not used by the MemoryProxy class.
1407      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1408      * object into a block of Roo.data.Records.
1409      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1410      * The function must be passed <ul>
1411      * <li>The Record block object</li>
1412      * <li>The "arg" argument from the load function</li>
1413      * <li>A boolean success indicator</li>
1414      * </ul>
1415      * @param {Object} scope The scope in which to call the callback
1416      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1417      */
1418     load : function(params, reader, callback, scope, arg){
1419         params = params || {};
1420         var result;
1421         try {
1422             result = reader.readRecords(params.data ? params.data :this.data);
1423         }catch(e){
1424             this.fireEvent("loadexception", this, arg, null, e);
1425             callback.call(scope, null, arg, false);
1426             return;
1427         }
1428         callback.call(scope, result, arg, true);
1429     },
1430     
1431     // private
1432     update : function(params, records){
1433         
1434     }
1435 });/*
1436  * Based on:
1437  * Ext JS Library 1.1.1
1438  * Copyright(c) 2006-2007, Ext JS, LLC.
1439  *
1440  * Originally Released Under LGPL - original licence link has changed is not relivant.
1441  *
1442  * Fork - LGPL
1443  * <script type="text/javascript">
1444  */
1445 /**
1446  * @class Roo.data.HttpProxy
1447  * @extends Roo.data.DataProxy
1448  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1449  * configured to reference a certain URL.<br><br>
1450  * <p>
1451  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1452  * from which the running page was served.<br><br>
1453  * <p>
1454  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1455  * <p>
1456  * Be aware that to enable the browser to parse an XML document, the server must set
1457  * the Content-Type header in the HTTP response to "text/xml".
1458  * @constructor
1459  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1460  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1461  * will be used to make the request.
1462  */
1463 Roo.data.HttpProxy = function(conn){
1464     Roo.data.HttpProxy.superclass.constructor.call(this);
1465     // is conn a conn config or a real conn?
1466     this.conn = conn;
1467     this.useAjax = !conn || !conn.events;
1468   
1469 };
1470
1471 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1472     // thse are take from connection...
1473     
1474     /**
1475      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1476      */
1477     /**
1478      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1479      * extra parameters to each request made by this object. (defaults to undefined)
1480      */
1481     /**
1482      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1483      *  to each request made by this object. (defaults to undefined)
1484      */
1485     /**
1486      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1487      */
1488     /**
1489      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1490      */
1491      /**
1492      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1493      * @type Boolean
1494      */
1495   
1496
1497     /**
1498      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1499      * @type Boolean
1500      */
1501     /**
1502      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1503      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1504      * a finer-grained basis than the DataProxy events.
1505      */
1506     getConnection : function(){
1507         return this.useAjax ? Roo.Ajax : this.conn;
1508     },
1509
1510     /**
1511      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1512      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1513      * process that block using the passed callback.
1514      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1515      * for the request to the remote server.
1516      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1517      * object into a block of Roo.data.Records.
1518      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1519      * The function must be passed <ul>
1520      * <li>The Record block object</li>
1521      * <li>The "arg" argument from the load function</li>
1522      * <li>A boolean success indicator</li>
1523      * </ul>
1524      * @param {Object} scope The scope in which to call the callback
1525      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1526      */
1527     load : function(params, reader, callback, scope, arg){
1528         if(this.fireEvent("beforeload", this, params) !== false){
1529             var  o = {
1530                 params : params || {},
1531                 request: {
1532                     callback : callback,
1533                     scope : scope,
1534                     arg : arg
1535                 },
1536                 reader: reader,
1537                 callback : this.loadResponse,
1538                 scope: this
1539             };
1540             if(this.useAjax){
1541                 Roo.applyIf(o, this.conn);
1542                 if(this.activeRequest){
1543                     Roo.Ajax.abort(this.activeRequest);
1544                 }
1545                 this.activeRequest = Roo.Ajax.request(o);
1546             }else{
1547                 this.conn.request(o);
1548             }
1549         }else{
1550             callback.call(scope||this, null, arg, false);
1551         }
1552     },
1553
1554     // private
1555     loadResponse : function(o, success, response){
1556         delete this.activeRequest;
1557         if(!success){
1558             this.fireEvent("loadexception", this, o, response);
1559             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1560             return;
1561         }
1562         var result;
1563         try {
1564             result = o.reader.read(response);
1565         }catch(e){
1566             this.fireEvent("loadexception", this, o, response, e);
1567             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1568             return;
1569         }
1570         
1571         this.fireEvent("load", this, o, o.request.arg);
1572         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1573     },
1574
1575     // private
1576     update : function(dataSet){
1577
1578     },
1579
1580     // private
1581     updateResponse : function(dataSet){
1582
1583     }
1584 });/*
1585  * Based on:
1586  * Ext JS Library 1.1.1
1587  * Copyright(c) 2006-2007, Ext JS, LLC.
1588  *
1589  * Originally Released Under LGPL - original licence link has changed is not relivant.
1590  *
1591  * Fork - LGPL
1592  * <script type="text/javascript">
1593  */
1594
1595 /**
1596  * @class Roo.data.ScriptTagProxy
1597  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1598  * other than the originating domain of the running page.<br><br>
1599  * <p>
1600  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1601  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1602  * <p>
1603  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1604  * source code that is used as the source inside a &lt;script> tag.<br><br>
1605  * <p>
1606  * In order for the browser to process the returned data, the server must wrap the data object
1607  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1608  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1609  * depending on whether the callback name was passed:
1610  * <p>
1611  * <pre><code>
1612 boolean scriptTag = false;
1613 String cb = request.getParameter("callback");
1614 if (cb != null) {
1615     scriptTag = true;
1616     response.setContentType("text/javascript");
1617 } else {
1618     response.setContentType("application/x-json");
1619 }
1620 Writer out = response.getWriter();
1621 if (scriptTag) {
1622     out.write(cb + "(");
1623 }
1624 out.print(dataBlock.toJsonString());
1625 if (scriptTag) {
1626     out.write(");");
1627 }
1628 </pre></code>
1629  *
1630  * @constructor
1631  * @param {Object} config A configuration object.
1632  */
1633 Roo.data.ScriptTagProxy = function(config){
1634     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1635     Roo.apply(this, config);
1636     this.head = document.getElementsByTagName("head")[0];
1637 };
1638
1639 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1640
1641 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1642     /**
1643      * @cfg {String} url The URL from which to request the data object.
1644      */
1645     /**
1646      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1647      */
1648     timeout : 30000,
1649     /**
1650      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1651      * the server the name of the callback function set up by the load call to process the returned data object.
1652      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1653      * javascript output which calls this named function passing the data object as its only parameter.
1654      */
1655     callbackParam : "callback",
1656     /**
1657      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1658      * name to the request.
1659      */
1660     nocache : true,
1661
1662     /**
1663      * Load data from the configured URL, read the data object into
1664      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1665      * process that block using the passed callback.
1666      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1667      * for the request to the remote server.
1668      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1669      * object into a block of Roo.data.Records.
1670      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1671      * The function must be passed <ul>
1672      * <li>The Record block object</li>
1673      * <li>The "arg" argument from the load function</li>
1674      * <li>A boolean success indicator</li>
1675      * </ul>
1676      * @param {Object} scope The scope in which to call the callback
1677      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1678      */
1679     load : function(params, reader, callback, scope, arg){
1680         if(this.fireEvent("beforeload", this, params) !== false){
1681
1682             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1683
1684             var url = this.url;
1685             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1686             if(this.nocache){
1687                 url += "&_dc=" + (new Date().getTime());
1688             }
1689             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1690             var trans = {
1691                 id : transId,
1692                 cb : "stcCallback"+transId,
1693                 scriptId : "stcScript"+transId,
1694                 params : params,
1695                 arg : arg,
1696                 url : url,
1697                 callback : callback,
1698                 scope : scope,
1699                 reader : reader
1700             };
1701             var conn = this;
1702
1703             window[trans.cb] = function(o){
1704                 conn.handleResponse(o, trans);
1705             };
1706
1707             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1708
1709             if(this.autoAbort !== false){
1710                 this.abort();
1711             }
1712
1713             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1714
1715             var script = document.createElement("script");
1716             script.setAttribute("src", url);
1717             script.setAttribute("type", "text/javascript");
1718             script.setAttribute("id", trans.scriptId);
1719             this.head.appendChild(script);
1720
1721             this.trans = trans;
1722         }else{
1723             callback.call(scope||this, null, arg, false);
1724         }
1725     },
1726
1727     // private
1728     isLoading : function(){
1729         return this.trans ? true : false;
1730     },
1731
1732     /**
1733      * Abort the current server request.
1734      */
1735     abort : function(){
1736         if(this.isLoading()){
1737             this.destroyTrans(this.trans);
1738         }
1739     },
1740
1741     // private
1742     destroyTrans : function(trans, isLoaded){
1743         this.head.removeChild(document.getElementById(trans.scriptId));
1744         clearTimeout(trans.timeoutId);
1745         if(isLoaded){
1746             window[trans.cb] = undefined;
1747             try{
1748                 delete window[trans.cb];
1749             }catch(e){}
1750         }else{
1751             // if hasn't been loaded, wait for load to remove it to prevent script error
1752             window[trans.cb] = function(){
1753                 window[trans.cb] = undefined;
1754                 try{
1755                     delete window[trans.cb];
1756                 }catch(e){}
1757             };
1758         }
1759     },
1760
1761     // private
1762     handleResponse : function(o, trans){
1763         this.trans = false;
1764         this.destroyTrans(trans, true);
1765         var result;
1766         try {
1767             result = trans.reader.readRecords(o);
1768         }catch(e){
1769             this.fireEvent("loadexception", this, o, trans.arg, e);
1770             trans.callback.call(trans.scope||window, null, trans.arg, false);
1771             return;
1772         }
1773         this.fireEvent("load", this, o, trans.arg);
1774         trans.callback.call(trans.scope||window, result, trans.arg, true);
1775     },
1776
1777     // private
1778     handleFailure : function(trans){
1779         this.trans = false;
1780         this.destroyTrans(trans, false);
1781         this.fireEvent("loadexception", this, null, trans.arg);
1782         trans.callback.call(trans.scope||window, null, trans.arg, false);
1783     }
1784 });/*
1785  * Based on:
1786  * Ext JS Library 1.1.1
1787  * Copyright(c) 2006-2007, Ext JS, LLC.
1788  *
1789  * Originally Released Under LGPL - original licence link has changed is not relivant.
1790  *
1791  * Fork - LGPL
1792  * <script type="text/javascript">
1793  */
1794
1795 /**
1796  * @class Roo.data.JsonReader
1797  * @extends Roo.data.DataReader
1798  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1799  * based on mappings in a provided Roo.data.Record constructor.
1800  * 
1801  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1802  * in the reply previously. 
1803  * 
1804  * <p>
1805  * Example code:
1806  * <pre><code>
1807 var RecordDef = Roo.data.Record.create([
1808     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1809     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1810 ]);
1811 var myReader = new Roo.data.JsonReader({
1812     totalProperty: "results",    // The property which contains the total dataset size (optional)
1813     root: "rows",                // The property which contains an Array of row objects
1814     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1815 }, RecordDef);
1816 </code></pre>
1817  * <p>
1818  * This would consume a JSON file like this:
1819  * <pre><code>
1820 { 'results': 2, 'rows': [
1821     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1822     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1823 }
1824 </code></pre>
1825  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1826  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1827  * paged from the remote server.
1828  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1829  * @cfg {String} root name of the property which contains the Array of row objects.
1830  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1831  * @cfg {Array} fields Array of field definition objects
1832  * @constructor
1833  * Create a new JsonReader
1834  * @param {Object} meta Metadata configuration options
1835  * @param {Object} recordType Either an Array of field definition objects,
1836  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1837  */
1838 Roo.data.JsonReader = function(meta, recordType){
1839     
1840     meta = meta || {};
1841     // set some defaults:
1842     Roo.applyIf(meta, {
1843         totalProperty: 'total',
1844         successProperty : 'success',
1845         root : 'data',
1846         id : 'id'
1847     });
1848     
1849     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1850 };
1851 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1852     
1853     readerType : 'Json',
1854     
1855     /**
1856      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1857      * Used by Store query builder to append _requestMeta to params.
1858      * 
1859      */
1860     metaFromRemote : false,
1861     /**
1862      * This method is only used by a DataProxy which has retrieved data from a remote server.
1863      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1864      * @return {Object} data A data block which is used by an Roo.data.Store object as
1865      * a cache of Roo.data.Records.
1866      */
1867     read : function(response){
1868         var json = response.responseText;
1869        
1870         var o = /* eval:var:o */ eval("("+json+")");
1871         if(!o) {
1872             throw {message: "JsonReader.read: Json object not found"};
1873         }
1874         
1875         if(o.metaData){
1876             
1877             delete this.ef;
1878             this.metaFromRemote = true;
1879             this.meta = o.metaData;
1880             this.recordType = Roo.data.Record.create(o.metaData.fields);
1881             this.onMetaChange(this.meta, this.recordType, o);
1882         }
1883         return this.readRecords(o);
1884     },
1885
1886     // private function a store will implement
1887     onMetaChange : function(meta, recordType, o){
1888
1889     },
1890
1891     /**
1892          * @ignore
1893          */
1894     simpleAccess: function(obj, subsc) {
1895         return obj[subsc];
1896     },
1897
1898         /**
1899          * @ignore
1900          */
1901     getJsonAccessor: function(){
1902         var re = /[\[\.]/;
1903         return function(expr) {
1904             try {
1905                 return(re.test(expr))
1906                     ? new Function("obj", "return obj." + expr)
1907                     : function(obj){
1908                         return obj[expr];
1909                     };
1910             } catch(e){}
1911             return Roo.emptyFn;
1912         };
1913     }(),
1914
1915     /**
1916      * Create a data block containing Roo.data.Records from an XML document.
1917      * @param {Object} o An object which contains an Array of row objects in the property specified
1918      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1919      * which contains the total size of the dataset.
1920      * @return {Object} data A data block which is used by an Roo.data.Store object as
1921      * a cache of Roo.data.Records.
1922      */
1923     readRecords : function(o){
1924         /**
1925          * After any data loads, the raw JSON data is available for further custom processing.
1926          * @type Object
1927          */
1928         this.o = o;
1929         var s = this.meta, Record = this.recordType,
1930             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1931
1932 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1933         if (!this.ef) {
1934             if(s.totalProperty) {
1935                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1936                 }
1937                 if(s.successProperty) {
1938                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1939                 }
1940                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1941                 if (s.id) {
1942                         var g = this.getJsonAccessor(s.id);
1943                         this.getId = function(rec) {
1944                                 var r = g(rec);  
1945                                 return (r === undefined || r === "") ? null : r;
1946                         };
1947                 } else {
1948                         this.getId = function(){return null;};
1949                 }
1950             this.ef = [];
1951             for(var jj = 0; jj < fl; jj++){
1952                 f = fi[jj];
1953                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1954                 this.ef[jj] = this.getJsonAccessor(map);
1955             }
1956         }
1957
1958         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1959         if(s.totalProperty){
1960             var vt = parseInt(this.getTotal(o), 10);
1961             if(!isNaN(vt)){
1962                 totalRecords = vt;
1963             }
1964         }
1965         if(s.successProperty){
1966             var vs = this.getSuccess(o);
1967             if(vs === false || vs === 'false'){
1968                 success = false;
1969             }
1970         }
1971         var records = [];
1972         for(var i = 0; i < c; i++){
1973                 var n = root[i];
1974             var values = {};
1975             var id = this.getId(n);
1976             for(var j = 0; j < fl; j++){
1977                 f = fi[j];
1978             var v = this.ef[j](n);
1979             if (!f.convert) {
1980                 Roo.log('missing convert for ' + f.name);
1981                 Roo.log(f);
1982                 continue;
1983             }
1984             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1985             }
1986             var record = new Record(values, id);
1987             record.json = n;
1988             records[i] = record;
1989         }
1990         return {
1991             raw : o,
1992             success : success,
1993             records : records,
1994             totalRecords : totalRecords
1995         };
1996     },
1997     // used when loading children.. @see loadDataFromChildren
1998     toLoadData: function(rec)
1999     {
2000         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2001         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2002         return { data : data, total : data.length };
2003         
2004     }
2005 });/*
2006  * Based on:
2007  * Ext JS Library 1.1.1
2008  * Copyright(c) 2006-2007, Ext JS, LLC.
2009  *
2010  * Originally Released Under LGPL - original licence link has changed is not relivant.
2011  *
2012  * Fork - LGPL
2013  * <script type="text/javascript">
2014  */
2015
2016 /**
2017  * @class Roo.data.XmlReader
2018  * @extends Roo.data.DataReader
2019  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2020  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2021  * <p>
2022  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2023  * header in the HTTP response must be set to "text/xml".</em>
2024  * <p>
2025  * Example code:
2026  * <pre><code>
2027 var RecordDef = Roo.data.Record.create([
2028    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2029    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2030 ]);
2031 var myReader = new Roo.data.XmlReader({
2032    totalRecords: "results", // The element which contains the total dataset size (optional)
2033    record: "row",           // The repeated element which contains row information
2034    id: "id"                 // The element within the row that provides an ID for the record (optional)
2035 }, RecordDef);
2036 </code></pre>
2037  * <p>
2038  * This would consume an XML file like this:
2039  * <pre><code>
2040 &lt;?xml?>
2041 &lt;dataset>
2042  &lt;results>2&lt;/results>
2043  &lt;row>
2044    &lt;id>1&lt;/id>
2045    &lt;name>Bill&lt;/name>
2046    &lt;occupation>Gardener&lt;/occupation>
2047  &lt;/row>
2048  &lt;row>
2049    &lt;id>2&lt;/id>
2050    &lt;name>Ben&lt;/name>
2051    &lt;occupation>Horticulturalist&lt;/occupation>
2052  &lt;/row>
2053 &lt;/dataset>
2054 </code></pre>
2055  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2056  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2057  * paged from the remote server.
2058  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2059  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2060  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2061  * a record identifier value.
2062  * @constructor
2063  * Create a new XmlReader
2064  * @param {Object} meta Metadata configuration options
2065  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2066  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2067  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2068  */
2069 Roo.data.XmlReader = function(meta, recordType){
2070     meta = meta || {};
2071     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2072 };
2073 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2074     
2075     readerType : 'Xml',
2076     
2077     /**
2078      * This method is only used by a DataProxy which has retrieved data from a remote server.
2079          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2080          * to contain a method called 'responseXML' that returns an XML document object.
2081      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2082      * a cache of Roo.data.Records.
2083      */
2084     read : function(response){
2085         var doc = response.responseXML;
2086         if(!doc) {
2087             throw {message: "XmlReader.read: XML Document not available"};
2088         }
2089         return this.readRecords(doc);
2090     },
2091
2092     /**
2093      * Create a data block containing Roo.data.Records from an XML document.
2094          * @param {Object} doc A parsed XML document.
2095      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2096      * a cache of Roo.data.Records.
2097      */
2098     readRecords : function(doc){
2099         /**
2100          * After any data loads/reads, the raw XML Document is available for further custom processing.
2101          * @type XMLDocument
2102          */
2103         this.xmlData = doc;
2104         var root = doc.documentElement || doc;
2105         var q = Roo.DomQuery;
2106         var recordType = this.recordType, fields = recordType.prototype.fields;
2107         var sid = this.meta.id;
2108         var totalRecords = 0, success = true;
2109         if(this.meta.totalRecords){
2110             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2111         }
2112         
2113         if(this.meta.success){
2114             var sv = q.selectValue(this.meta.success, root, true);
2115             success = sv !== false && sv !== 'false';
2116         }
2117         var records = [];
2118         var ns = q.select(this.meta.record, root);
2119         for(var i = 0, len = ns.length; i < len; i++) {
2120                 var n = ns[i];
2121                 var values = {};
2122                 var id = sid ? q.selectValue(sid, n) : undefined;
2123                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2124                     var f = fields.items[j];
2125                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2126                     v = f.convert(v);
2127                     values[f.name] = v;
2128                 }
2129                 var record = new recordType(values, id);
2130                 record.node = n;
2131                 records[records.length] = record;
2132             }
2133
2134             return {
2135                 success : success,
2136                 records : records,
2137                 totalRecords : totalRecords || records.length
2138             };
2139     }
2140 });/*
2141  * Based on:
2142  * Ext JS Library 1.1.1
2143  * Copyright(c) 2006-2007, Ext JS, LLC.
2144  *
2145  * Originally Released Under LGPL - original licence link has changed is not relivant.
2146  *
2147  * Fork - LGPL
2148  * <script type="text/javascript">
2149  */
2150
2151 /**
2152  * @class Roo.data.ArrayReader
2153  * @extends Roo.data.DataReader
2154  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2155  * Each element of that Array represents a row of data fields. The
2156  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2157  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2158  * <p>
2159  * Example code:.
2160  * <pre><code>
2161 var RecordDef = Roo.data.Record.create([
2162     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2163     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2164 ]);
2165 var myReader = new Roo.data.ArrayReader({
2166     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2167 }, RecordDef);
2168 </code></pre>
2169  * <p>
2170  * This would consume an Array like this:
2171  * <pre><code>
2172 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2173   </code></pre>
2174  
2175  * @constructor
2176  * Create a new JsonReader
2177  * @param {Object} meta Metadata configuration options.
2178  * @param {Object|Array} recordType Either an Array of field definition objects
2179  * 
2180  * @cfg {Array} fields Array of field definition objects
2181  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2182  * as specified to {@link Roo.data.Record#create},
2183  * or an {@link Roo.data.Record} object
2184  *
2185  * 
2186  * created using {@link Roo.data.Record#create}.
2187  */
2188 Roo.data.ArrayReader = function(meta, recordType)
2189 {    
2190     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2191 };
2192
2193 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2194     
2195       /**
2196      * Create a data block containing Roo.data.Records from an XML document.
2197      * @param {Object} o An Array of row objects which represents the dataset.
2198      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2199      * a cache of Roo.data.Records.
2200      */
2201     readRecords : function(o)
2202     {
2203         var sid = this.meta ? this.meta.id : null;
2204         var recordType = this.recordType, fields = recordType.prototype.fields;
2205         var records = [];
2206         var root = o;
2207         for(var i = 0; i < root.length; i++){
2208             var n = root[i];
2209             var values = {};
2210             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2211             for(var j = 0, jlen = fields.length; j < jlen; j++){
2212                 var f = fields.items[j];
2213                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2214                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2215                 v = f.convert(v);
2216                 values[f.name] = v;
2217             }
2218             var record = new recordType(values, id);
2219             record.json = n;
2220             records[records.length] = record;
2221         }
2222         return {
2223             records : records,
2224             totalRecords : records.length
2225         };
2226     },
2227     // used when loading children.. @see loadDataFromChildren
2228     toLoadData: function(rec)
2229     {
2230         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2231         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2232         
2233     }
2234     
2235     
2236 });/*
2237  * Based on:
2238  * Ext JS Library 1.1.1
2239  * Copyright(c) 2006-2007, Ext JS, LLC.
2240  *
2241  * Originally Released Under LGPL - original licence link has changed is not relivant.
2242  *
2243  * Fork - LGPL
2244  * <script type="text/javascript">
2245  */
2246
2247
2248 /**
2249  * @class Roo.data.Tree
2250  * @extends Roo.util.Observable
2251  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2252  * in the tree have most standard DOM functionality.
2253  * @constructor
2254  * @param {Node} root (optional) The root node
2255  */
2256 Roo.data.Tree = function(root){
2257    this.nodeHash = {};
2258    /**
2259     * The root node for this tree
2260     * @type Node
2261     */
2262    this.root = null;
2263    if(root){
2264        this.setRootNode(root);
2265    }
2266    this.addEvents({
2267        /**
2268         * @event append
2269         * Fires when a new child node is appended to a node in this tree.
2270         * @param {Tree} tree The owner tree
2271         * @param {Node} parent The parent node
2272         * @param {Node} node The newly appended node
2273         * @param {Number} index The index of the newly appended node
2274         */
2275        "append" : true,
2276        /**
2277         * @event remove
2278         * Fires when a child node is removed from a node in this tree.
2279         * @param {Tree} tree The owner tree
2280         * @param {Node} parent The parent node
2281         * @param {Node} node The child node removed
2282         */
2283        "remove" : true,
2284        /**
2285         * @event move
2286         * Fires when a node is moved to a new location in the tree
2287         * @param {Tree} tree The owner tree
2288         * @param {Node} node The node moved
2289         * @param {Node} oldParent The old parent of this node
2290         * @param {Node} newParent The new parent of this node
2291         * @param {Number} index The index it was moved to
2292         */
2293        "move" : true,
2294        /**
2295         * @event insert
2296         * Fires when a new child node is inserted in a node in this tree.
2297         * @param {Tree} tree The owner tree
2298         * @param {Node} parent The parent node
2299         * @param {Node} node The child node inserted
2300         * @param {Node} refNode The child node the node was inserted before
2301         */
2302        "insert" : true,
2303        /**
2304         * @event beforeappend
2305         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2306         * @param {Tree} tree The owner tree
2307         * @param {Node} parent The parent node
2308         * @param {Node} node The child node to be appended
2309         */
2310        "beforeappend" : true,
2311        /**
2312         * @event beforeremove
2313         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2314         * @param {Tree} tree The owner tree
2315         * @param {Node} parent The parent node
2316         * @param {Node} node The child node to be removed
2317         */
2318        "beforeremove" : true,
2319        /**
2320         * @event beforemove
2321         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2322         * @param {Tree} tree The owner tree
2323         * @param {Node} node The node being moved
2324         * @param {Node} oldParent The parent of the node
2325         * @param {Node} newParent The new parent the node is moving to
2326         * @param {Number} index The index it is being moved to
2327         */
2328        "beforemove" : true,
2329        /**
2330         * @event beforeinsert
2331         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2332         * @param {Tree} tree The owner tree
2333         * @param {Node} parent The parent node
2334         * @param {Node} node The child node to be inserted
2335         * @param {Node} refNode The child node the node is being inserted before
2336         */
2337        "beforeinsert" : true
2338    });
2339
2340     Roo.data.Tree.superclass.constructor.call(this);
2341 };
2342
2343 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2344     pathSeparator: "/",
2345
2346     proxyNodeEvent : function(){
2347         return this.fireEvent.apply(this, arguments);
2348     },
2349
2350     /**
2351      * Returns the root node for this tree.
2352      * @return {Node}
2353      */
2354     getRootNode : function(){
2355         return this.root;
2356     },
2357
2358     /**
2359      * Sets the root node for this tree.
2360      * @param {Node} node
2361      * @return {Node}
2362      */
2363     setRootNode : function(node){
2364         this.root = node;
2365         node.ownerTree = this;
2366         node.isRoot = true;
2367         this.registerNode(node);
2368         return node;
2369     },
2370
2371     /**
2372      * Gets a node in this tree by its id.
2373      * @param {String} id
2374      * @return {Node}
2375      */
2376     getNodeById : function(id){
2377         return this.nodeHash[id];
2378     },
2379
2380     registerNode : function(node){
2381         this.nodeHash[node.id] = node;
2382     },
2383
2384     unregisterNode : function(node){
2385         delete this.nodeHash[node.id];
2386     },
2387
2388     toString : function(){
2389         return "[Tree"+(this.id?" "+this.id:"")+"]";
2390     }
2391 });
2392
2393 /**
2394  * @class Roo.data.Node
2395  * @extends Roo.util.Observable
2396  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2397  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2398  * @constructor
2399  * @param {Object} attributes The attributes/config for the node
2400  */
2401 Roo.data.Node = function(attributes){
2402     /**
2403      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2404      * @type {Object}
2405      */
2406     this.attributes = attributes || {};
2407     this.leaf = this.attributes.leaf;
2408     /**
2409      * The node id. @type String
2410      */
2411     this.id = this.attributes.id;
2412     if(!this.id){
2413         this.id = Roo.id(null, "ynode-");
2414         this.attributes.id = this.id;
2415     }
2416      
2417     
2418     /**
2419      * All child nodes of this node. @type Array
2420      */
2421     this.childNodes = [];
2422     if(!this.childNodes.indexOf){ // indexOf is a must
2423         this.childNodes.indexOf = function(o){
2424             for(var i = 0, len = this.length; i < len; i++){
2425                 if(this[i] == o) {
2426                     return i;
2427                 }
2428             }
2429             return -1;
2430         };
2431     }
2432     /**
2433      * The parent node for this node. @type Node
2434      */
2435     this.parentNode = null;
2436     /**
2437      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2438      */
2439     this.firstChild = null;
2440     /**
2441      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2442      */
2443     this.lastChild = null;
2444     /**
2445      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2446      */
2447     this.previousSibling = null;
2448     /**
2449      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2450      */
2451     this.nextSibling = null;
2452
2453     this.addEvents({
2454        /**
2455         * @event append
2456         * Fires when a new child node is appended
2457         * @param {Tree} tree The owner tree
2458         * @param {Node} this This node
2459         * @param {Node} node The newly appended node
2460         * @param {Number} index The index of the newly appended node
2461         */
2462        "append" : true,
2463        /**
2464         * @event remove
2465         * Fires when a child node is removed
2466         * @param {Tree} tree The owner tree
2467         * @param {Node} this This node
2468         * @param {Node} node The removed node
2469         */
2470        "remove" : true,
2471        /**
2472         * @event move
2473         * Fires when this node is moved to a new location in the tree
2474         * @param {Tree} tree The owner tree
2475         * @param {Node} this This node
2476         * @param {Node} oldParent The old parent of this node
2477         * @param {Node} newParent The new parent of this node
2478         * @param {Number} index The index it was moved to
2479         */
2480        "move" : true,
2481        /**
2482         * @event insert
2483         * Fires when a new child node is inserted.
2484         * @param {Tree} tree The owner tree
2485         * @param {Node} this This node
2486         * @param {Node} node The child node inserted
2487         * @param {Node} refNode The child node the node was inserted before
2488         */
2489        "insert" : true,
2490        /**
2491         * @event beforeappend
2492         * Fires before a new child is appended, return false to cancel the append.
2493         * @param {Tree} tree The owner tree
2494         * @param {Node} this This node
2495         * @param {Node} node The child node to be appended
2496         */
2497        "beforeappend" : true,
2498        /**
2499         * @event beforeremove
2500         * Fires before a child is removed, return false to cancel the remove.
2501         * @param {Tree} tree The owner tree
2502         * @param {Node} this This node
2503         * @param {Node} node The child node to be removed
2504         */
2505        "beforeremove" : true,
2506        /**
2507         * @event beforemove
2508         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2509         * @param {Tree} tree The owner tree
2510         * @param {Node} this This node
2511         * @param {Node} oldParent The parent of this node
2512         * @param {Node} newParent The new parent this node is moving to
2513         * @param {Number} index The index it is being moved to
2514         */
2515        "beforemove" : true,
2516        /**
2517         * @event beforeinsert
2518         * Fires before a new child is inserted, return false to cancel the insert.
2519         * @param {Tree} tree The owner tree
2520         * @param {Node} this This node
2521         * @param {Node} node The child node to be inserted
2522         * @param {Node} refNode The child node the node is being inserted before
2523         */
2524        "beforeinsert" : true
2525    });
2526     this.listeners = this.attributes.listeners;
2527     Roo.data.Node.superclass.constructor.call(this);
2528 };
2529
2530 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2531     fireEvent : function(evtName){
2532         // first do standard event for this node
2533         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2534             return false;
2535         }
2536         // then bubble it up to the tree if the event wasn't cancelled
2537         var ot = this.getOwnerTree();
2538         if(ot){
2539             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2540                 return false;
2541             }
2542         }
2543         return true;
2544     },
2545
2546     /**
2547      * Returns true if this node is a leaf
2548      * @return {Boolean}
2549      */
2550     isLeaf : function(){
2551         return this.leaf === true;
2552     },
2553
2554     // private
2555     setFirstChild : function(node){
2556         this.firstChild = node;
2557     },
2558
2559     //private
2560     setLastChild : function(node){
2561         this.lastChild = node;
2562     },
2563
2564
2565     /**
2566      * Returns true if this node is the last child of its parent
2567      * @return {Boolean}
2568      */
2569     isLast : function(){
2570        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2571     },
2572
2573     /**
2574      * Returns true if this node is the first child of its parent
2575      * @return {Boolean}
2576      */
2577     isFirst : function(){
2578        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2579     },
2580
2581     hasChildNodes : function(){
2582         return !this.isLeaf() && this.childNodes.length > 0;
2583     },
2584
2585     /**
2586      * Insert node(s) as the last child node of this node.
2587      * @param {Node/Array} node The node or Array of nodes to append
2588      * @return {Node} The appended node if single append, or null if an array was passed
2589      */
2590     appendChild : function(node){
2591         var multi = false;
2592         if(node instanceof Array){
2593             multi = node;
2594         }else if(arguments.length > 1){
2595             multi = arguments;
2596         }
2597         
2598         // if passed an array or multiple args do them one by one
2599         if(multi){
2600             for(var i = 0, len = multi.length; i < len; i++) {
2601                 this.appendChild(multi[i]);
2602             }
2603         }else{
2604             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2605                 return false;
2606             }
2607             var index = this.childNodes.length;
2608             var oldParent = node.parentNode;
2609             // it's a move, make sure we move it cleanly
2610             if(oldParent){
2611                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2612                     return false;
2613                 }
2614                 oldParent.removeChild(node);
2615             }
2616             
2617             index = this.childNodes.length;
2618             if(index == 0){
2619                 this.setFirstChild(node);
2620             }
2621             this.childNodes.push(node);
2622             node.parentNode = this;
2623             var ps = this.childNodes[index-1];
2624             if(ps){
2625                 node.previousSibling = ps;
2626                 ps.nextSibling = node;
2627             }else{
2628                 node.previousSibling = null;
2629             }
2630             node.nextSibling = null;
2631             this.setLastChild(node);
2632             node.setOwnerTree(this.getOwnerTree());
2633             this.fireEvent("append", this.ownerTree, this, node, index);
2634             if(this.ownerTree) {
2635                 this.ownerTree.fireEvent("appendnode", this, node, index);
2636             }
2637             if(oldParent){
2638                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2639             }
2640             return node;
2641         }
2642     },
2643
2644     /**
2645      * Removes a child node from this node.
2646      * @param {Node} node The node to remove
2647      * @return {Node} The removed node
2648      */
2649     removeChild : function(node){
2650         var index = this.childNodes.indexOf(node);
2651         if(index == -1){
2652             return false;
2653         }
2654         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2655             return false;
2656         }
2657
2658         // remove it from childNodes collection
2659         this.childNodes.splice(index, 1);
2660
2661         // update siblings
2662         if(node.previousSibling){
2663             node.previousSibling.nextSibling = node.nextSibling;
2664         }
2665         if(node.nextSibling){
2666             node.nextSibling.previousSibling = node.previousSibling;
2667         }
2668
2669         // update child refs
2670         if(this.firstChild == node){
2671             this.setFirstChild(node.nextSibling);
2672         }
2673         if(this.lastChild == node){
2674             this.setLastChild(node.previousSibling);
2675         }
2676
2677         node.setOwnerTree(null);
2678         // clear any references from the node
2679         node.parentNode = null;
2680         node.previousSibling = null;
2681         node.nextSibling = null;
2682         this.fireEvent("remove", this.ownerTree, this, node);
2683         return node;
2684     },
2685
2686     /**
2687      * Inserts the first node before the second node in this nodes childNodes collection.
2688      * @param {Node} node The node to insert
2689      * @param {Node} refNode The node to insert before (if null the node is appended)
2690      * @return {Node} The inserted node
2691      */
2692     insertBefore : function(node, refNode){
2693         if(!refNode){ // like standard Dom, refNode can be null for append
2694             return this.appendChild(node);
2695         }
2696         // nothing to do
2697         if(node == refNode){
2698             return false;
2699         }
2700
2701         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2702             return false;
2703         }
2704         var index = this.childNodes.indexOf(refNode);
2705         var oldParent = node.parentNode;
2706         var refIndex = index;
2707
2708         // when moving internally, indexes will change after remove
2709         if(oldParent == this && this.childNodes.indexOf(node) < index){
2710             refIndex--;
2711         }
2712
2713         // it's a move, make sure we move it cleanly
2714         if(oldParent){
2715             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2716                 return false;
2717             }
2718             oldParent.removeChild(node);
2719         }
2720         if(refIndex == 0){
2721             this.setFirstChild(node);
2722         }
2723         this.childNodes.splice(refIndex, 0, node);
2724         node.parentNode = this;
2725         var ps = this.childNodes[refIndex-1];
2726         if(ps){
2727             node.previousSibling = ps;
2728             ps.nextSibling = node;
2729         }else{
2730             node.previousSibling = null;
2731         }
2732         node.nextSibling = refNode;
2733         refNode.previousSibling = node;
2734         node.setOwnerTree(this.getOwnerTree());
2735         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2736         if(oldParent){
2737             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2738         }
2739         return node;
2740     },
2741
2742     /**
2743      * Returns the child node at the specified index.
2744      * @param {Number} index
2745      * @return {Node}
2746      */
2747     item : function(index){
2748         return this.childNodes[index];
2749     },
2750
2751     /**
2752      * Replaces one child node in this node with another.
2753      * @param {Node} newChild The replacement node
2754      * @param {Node} oldChild The node to replace
2755      * @return {Node} The replaced node
2756      */
2757     replaceChild : function(newChild, oldChild){
2758         this.insertBefore(newChild, oldChild);
2759         this.removeChild(oldChild);
2760         return oldChild;
2761     },
2762
2763     /**
2764      * Returns the index of a child node
2765      * @param {Node} node
2766      * @return {Number} The index of the node or -1 if it was not found
2767      */
2768     indexOf : function(child){
2769         return this.childNodes.indexOf(child);
2770     },
2771
2772     /**
2773      * Returns the tree this node is in.
2774      * @return {Tree}
2775      */
2776     getOwnerTree : function(){
2777         // if it doesn't have one, look for one
2778         if(!this.ownerTree){
2779             var p = this;
2780             while(p){
2781                 if(p.ownerTree){
2782                     this.ownerTree = p.ownerTree;
2783                     break;
2784                 }
2785                 p = p.parentNode;
2786             }
2787         }
2788         return this.ownerTree;
2789     },
2790
2791     /**
2792      * Returns depth of this node (the root node has a depth of 0)
2793      * @return {Number}
2794      */
2795     getDepth : function(){
2796         var depth = 0;
2797         var p = this;
2798         while(p.parentNode){
2799             ++depth;
2800             p = p.parentNode;
2801         }
2802         return depth;
2803     },
2804
2805     // private
2806     setOwnerTree : function(tree){
2807         // if it's move, we need to update everyone
2808         if(tree != this.ownerTree){
2809             if(this.ownerTree){
2810                 this.ownerTree.unregisterNode(this);
2811             }
2812             this.ownerTree = tree;
2813             var cs = this.childNodes;
2814             for(var i = 0, len = cs.length; i < len; i++) {
2815                 cs[i].setOwnerTree(tree);
2816             }
2817             if(tree){
2818                 tree.registerNode(this);
2819             }
2820         }
2821     },
2822
2823     /**
2824      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2825      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2826      * @return {String} The path
2827      */
2828     getPath : function(attr){
2829         attr = attr || "id";
2830         var p = this.parentNode;
2831         var b = [this.attributes[attr]];
2832         while(p){
2833             b.unshift(p.attributes[attr]);
2834             p = p.parentNode;
2835         }
2836         var sep = this.getOwnerTree().pathSeparator;
2837         return sep + b.join(sep);
2838     },
2839
2840     /**
2841      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2842      * function call will be the scope provided or the current node. The arguments to the function
2843      * will be the args provided or the current node. If the function returns false at any point,
2844      * the bubble is stopped.
2845      * @param {Function} fn The function to call
2846      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2847      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2848      */
2849     bubble : function(fn, scope, args){
2850         var p = this;
2851         while(p){
2852             if(fn.call(scope || p, args || p) === false){
2853                 break;
2854             }
2855             p = p.parentNode;
2856         }
2857     },
2858
2859     /**
2860      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2861      * function call will be the scope provided or the current node. The arguments to the function
2862      * will be the args provided or the current node. If the function returns false at any point,
2863      * the cascade is stopped on that branch.
2864      * @param {Function} fn The function to call
2865      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2866      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2867      */
2868     cascade : function(fn, scope, args){
2869         if(fn.call(scope || this, args || this) !== false){
2870             var cs = this.childNodes;
2871             for(var i = 0, len = cs.length; i < len; i++) {
2872                 cs[i].cascade(fn, scope, args);
2873             }
2874         }
2875     },
2876
2877     /**
2878      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2879      * function call will be the scope provided or the current node. The arguments to the function
2880      * will be the args provided or the current node. If the function returns false at any point,
2881      * the iteration stops.
2882      * @param {Function} fn The function to call
2883      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2884      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2885      */
2886     eachChild : function(fn, scope, args){
2887         var cs = this.childNodes;
2888         for(var i = 0, len = cs.length; i < len; i++) {
2889                 if(fn.call(scope || this, args || cs[i]) === false){
2890                     break;
2891                 }
2892         }
2893     },
2894
2895     /**
2896      * Finds the first child that has the attribute with the specified value.
2897      * @param {String} attribute The attribute name
2898      * @param {Mixed} value The value to search for
2899      * @return {Node} The found child or null if none was found
2900      */
2901     findChild : function(attribute, value){
2902         var cs = this.childNodes;
2903         for(var i = 0, len = cs.length; i < len; i++) {
2904                 if(cs[i].attributes[attribute] == value){
2905                     return cs[i];
2906                 }
2907         }
2908         return null;
2909     },
2910
2911     /**
2912      * Finds the first child by a custom function. The child matches if the function passed
2913      * returns true.
2914      * @param {Function} fn
2915      * @param {Object} scope (optional)
2916      * @return {Node} The found child or null if none was found
2917      */
2918     findChildBy : function(fn, scope){
2919         var cs = this.childNodes;
2920         for(var i = 0, len = cs.length; i < len; i++) {
2921                 if(fn.call(scope||cs[i], cs[i]) === true){
2922                     return cs[i];
2923                 }
2924         }
2925         return null;
2926     },
2927
2928     /**
2929      * Sorts this nodes children using the supplied sort function
2930      * @param {Function} fn
2931      * @param {Object} scope (optional)
2932      */
2933     sort : function(fn, scope){
2934         var cs = this.childNodes;
2935         var len = cs.length;
2936         if(len > 0){
2937             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2938             cs.sort(sortFn);
2939             for(var i = 0; i < len; i++){
2940                 var n = cs[i];
2941                 n.previousSibling = cs[i-1];
2942                 n.nextSibling = cs[i+1];
2943                 if(i == 0){
2944                     this.setFirstChild(n);
2945                 }
2946                 if(i == len-1){
2947                     this.setLastChild(n);
2948                 }
2949             }
2950         }
2951     },
2952
2953     /**
2954      * Returns true if this node is an ancestor (at any point) of the passed node.
2955      * @param {Node} node
2956      * @return {Boolean}
2957      */
2958     contains : function(node){
2959         return node.isAncestor(this);
2960     },
2961
2962     /**
2963      * Returns true if the passed node is an ancestor (at any point) of this node.
2964      * @param {Node} node
2965      * @return {Boolean}
2966      */
2967     isAncestor : function(node){
2968         var p = this.parentNode;
2969         while(p){
2970             if(p == node){
2971                 return true;
2972             }
2973             p = p.parentNode;
2974         }
2975         return false;
2976     },
2977
2978     toString : function(){
2979         return "[Node"+(this.id?" "+this.id:"")+"]";
2980     }
2981 });/*
2982  * Based on:
2983  * Ext JS Library 1.1.1
2984  * Copyright(c) 2006-2007, Ext JS, LLC.
2985  *
2986  * Originally Released Under LGPL - original licence link has changed is not relivant.
2987  *
2988  * Fork - LGPL
2989  * <script type="text/javascript">
2990  */
2991
2992
2993 /**
2994  * @class Roo.Shadow
2995  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
2996  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
2997  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2998  * @constructor
2999  * Create a new Shadow
3000  * @param {Object} config The config object
3001  */
3002 Roo.Shadow = function(config){
3003     Roo.apply(this, config);
3004     if(typeof this.mode != "string"){
3005         this.mode = this.defaultMode;
3006     }
3007     var o = this.offset, a = {h: 0};
3008     var rad = Math.floor(this.offset/2);
3009     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3010         case "drop":
3011             a.w = 0;
3012             a.l = a.t = o;
3013             a.t -= 1;
3014             if(Roo.isIE){
3015                 a.l -= this.offset + rad;
3016                 a.t -= this.offset + rad;
3017                 a.w -= rad;
3018                 a.h -= rad;
3019                 a.t += 1;
3020             }
3021         break;
3022         case "sides":
3023             a.w = (o*2);
3024             a.l = -o;
3025             a.t = o-1;
3026             if(Roo.isIE){
3027                 a.l -= (this.offset - rad);
3028                 a.t -= this.offset + rad;
3029                 a.l += 1;
3030                 a.w -= (this.offset - rad)*2;
3031                 a.w -= rad + 1;
3032                 a.h -= 1;
3033             }
3034         break;
3035         case "frame":
3036             a.w = a.h = (o*2);
3037             a.l = a.t = -o;
3038             a.t += 1;
3039             a.h -= 2;
3040             if(Roo.isIE){
3041                 a.l -= (this.offset - rad);
3042                 a.t -= (this.offset - rad);
3043                 a.l += 1;
3044                 a.w -= (this.offset + rad + 1);
3045                 a.h -= (this.offset + rad);
3046                 a.h += 1;
3047             }
3048         break;
3049     };
3050
3051     this.adjusts = a;
3052 };
3053
3054 Roo.Shadow.prototype = {
3055     /**
3056      * @cfg {String} mode
3057      * The shadow display mode.  Supports the following options:<br />
3058      * sides: Shadow displays on both sides and bottom only<br />
3059      * frame: Shadow displays equally on all four sides<br />
3060      * drop: Traditional bottom-right drop shadow (default)
3061      */
3062     mode: false,
3063     /**
3064      * @cfg {String} offset
3065      * The number of pixels to offset the shadow from the element (defaults to 4)
3066      */
3067     offset: 4,
3068
3069     // private
3070     defaultMode: "drop",
3071
3072     /**
3073      * Displays the shadow under the target element
3074      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3075      */
3076     show : function(target){
3077         target = Roo.get(target);
3078         if(!this.el){
3079             this.el = Roo.Shadow.Pool.pull();
3080             if(this.el.dom.nextSibling != target.dom){
3081                 this.el.insertBefore(target);
3082             }
3083         }
3084         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3085         if(Roo.isIE){
3086             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3087         }
3088         this.realign(
3089             target.getLeft(true),
3090             target.getTop(true),
3091             target.getWidth(),
3092             target.getHeight()
3093         );
3094         this.el.dom.style.display = "block";
3095     },
3096
3097     /**
3098      * Returns true if the shadow is visible, else false
3099      */
3100     isVisible : function(){
3101         return this.el ? true : false;  
3102     },
3103
3104     /**
3105      * Direct alignment when values are already available. Show must be called at least once before
3106      * calling this method to ensure it is initialized.
3107      * @param {Number} left The target element left position
3108      * @param {Number} top The target element top position
3109      * @param {Number} width The target element width
3110      * @param {Number} height The target element height
3111      */
3112     realign : function(l, t, w, h){
3113         if(!this.el){
3114             return;
3115         }
3116         var a = this.adjusts, d = this.el.dom, s = d.style;
3117         var iea = 0;
3118         s.left = (l+a.l)+"px";
3119         s.top = (t+a.t)+"px";
3120         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3121  
3122         if(s.width != sws || s.height != shs){
3123             s.width = sws;
3124             s.height = shs;
3125             if(!Roo.isIE){
3126                 var cn = d.childNodes;
3127                 var sww = Math.max(0, (sw-12))+"px";
3128                 cn[0].childNodes[1].style.width = sww;
3129                 cn[1].childNodes[1].style.width = sww;
3130                 cn[2].childNodes[1].style.width = sww;
3131                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3132             }
3133         }
3134     },
3135
3136     /**
3137      * Hides this shadow
3138      */
3139     hide : function(){
3140         if(this.el){
3141             this.el.dom.style.display = "none";
3142             Roo.Shadow.Pool.push(this.el);
3143             delete this.el;
3144         }
3145     },
3146
3147     /**
3148      * Adjust the z-index of this shadow
3149      * @param {Number} zindex The new z-index
3150      */
3151     setZIndex : function(z){
3152         this.zIndex = z;
3153         if(this.el){
3154             this.el.setStyle("z-index", z);
3155         }
3156     }
3157 };
3158
3159 // Private utility class that manages the internal Shadow cache
3160 Roo.Shadow.Pool = function(){
3161     var p = [];
3162     var markup = Roo.isIE ?
3163                  '<div class="x-ie-shadow"></div>' :
3164                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3165     return {
3166         pull : function(){
3167             var sh = p.shift();
3168             if(!sh){
3169                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3170                 sh.autoBoxAdjust = false;
3171             }
3172             return sh;
3173         },
3174
3175         push : function(sh){
3176             p.push(sh);
3177         }
3178     };
3179 }();/*
3180  * Based on:
3181  * Ext JS Library 1.1.1
3182  * Copyright(c) 2006-2007, Ext JS, LLC.
3183  *
3184  * Originally Released Under LGPL - original licence link has changed is not relivant.
3185  *
3186  * Fork - LGPL
3187  * <script type="text/javascript">
3188  */
3189
3190
3191 /**
3192  * @class Roo.SplitBar
3193  * @extends Roo.util.Observable
3194  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3195  * <br><br>
3196  * Usage:
3197  * <pre><code>
3198 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3199                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3200 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3201 split.minSize = 100;
3202 split.maxSize = 600;
3203 split.animate = true;
3204 split.on('moved', splitterMoved);
3205 </code></pre>
3206  * @constructor
3207  * Create a new SplitBar
3208  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3209  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3210  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3211  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3212                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3213                         position of the SplitBar).
3214  */
3215 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3216     
3217     /** @private */
3218     this.el = Roo.get(dragElement, true);
3219     this.el.dom.unselectable = "on";
3220     /** @private */
3221     this.resizingEl = Roo.get(resizingElement, true);
3222
3223     /**
3224      * @private
3225      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3226      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3227      * @type Number
3228      */
3229     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3230     
3231     /**
3232      * The minimum size of the resizing element. (Defaults to 0)
3233      * @type Number
3234      */
3235     this.minSize = 0;
3236     
3237     /**
3238      * The maximum size of the resizing element. (Defaults to 2000)
3239      * @type Number
3240      */
3241     this.maxSize = 2000;
3242     
3243     /**
3244      * Whether to animate the transition to the new size
3245      * @type Boolean
3246      */
3247     this.animate = false;
3248     
3249     /**
3250      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3251      * @type Boolean
3252      */
3253     this.useShim = false;
3254     
3255     /** @private */
3256     this.shim = null;
3257     
3258     if(!existingProxy){
3259         /** @private */
3260         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3261     }else{
3262         this.proxy = Roo.get(existingProxy).dom;
3263     }
3264     /** @private */
3265     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3266     
3267     /** @private */
3268     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3269     
3270     /** @private */
3271     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3272     
3273     /** @private */
3274     this.dragSpecs = {};
3275     
3276     /**
3277      * @private The adapter to use to positon and resize elements
3278      */
3279     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3280     this.adapter.init(this);
3281     
3282     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3283         /** @private */
3284         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3285         this.el.addClass("x-splitbar-h");
3286     }else{
3287         /** @private */
3288         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3289         this.el.addClass("x-splitbar-v");
3290     }
3291     
3292     this.addEvents({
3293         /**
3294          * @event resize
3295          * Fires when the splitter is moved (alias for {@link #event-moved})
3296          * @param {Roo.SplitBar} this
3297          * @param {Number} newSize the new width or height
3298          */
3299         "resize" : true,
3300         /**
3301          * @event moved
3302          * Fires when the splitter is moved
3303          * @param {Roo.SplitBar} this
3304          * @param {Number} newSize the new width or height
3305          */
3306         "moved" : true,
3307         /**
3308          * @event beforeresize
3309          * Fires before the splitter is dragged
3310          * @param {Roo.SplitBar} this
3311          */
3312         "beforeresize" : true,
3313
3314         "beforeapply" : true
3315     });
3316
3317     Roo.util.Observable.call(this);
3318 };
3319
3320 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3321     onStartProxyDrag : function(x, y){
3322         this.fireEvent("beforeresize", this);
3323         if(!this.overlay){
3324             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3325             o.unselectable();
3326             o.enableDisplayMode("block");
3327             // all splitbars share the same overlay
3328             Roo.SplitBar.prototype.overlay = o;
3329         }
3330         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3331         this.overlay.show();
3332         Roo.get(this.proxy).setDisplayed("block");
3333         var size = this.adapter.getElementSize(this);
3334         this.activeMinSize = this.getMinimumSize();;
3335         this.activeMaxSize = this.getMaximumSize();;
3336         var c1 = size - this.activeMinSize;
3337         var c2 = Math.max(this.activeMaxSize - size, 0);
3338         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3339             this.dd.resetConstraints();
3340             this.dd.setXConstraint(
3341                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3342                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3343             );
3344             this.dd.setYConstraint(0, 0);
3345         }else{
3346             this.dd.resetConstraints();
3347             this.dd.setXConstraint(0, 0);
3348             this.dd.setYConstraint(
3349                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3350                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3351             );
3352          }
3353         this.dragSpecs.startSize = size;
3354         this.dragSpecs.startPoint = [x, y];
3355         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3356     },
3357     
3358     /** 
3359      * @private Called after the drag operation by the DDProxy
3360      */
3361     onEndProxyDrag : function(e){
3362         Roo.get(this.proxy).setDisplayed(false);
3363         var endPoint = Roo.lib.Event.getXY(e);
3364         if(this.overlay){
3365             this.overlay.hide();
3366         }
3367         var newSize;
3368         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3369             newSize = this.dragSpecs.startSize + 
3370                 (this.placement == Roo.SplitBar.LEFT ?
3371                     endPoint[0] - this.dragSpecs.startPoint[0] :
3372                     this.dragSpecs.startPoint[0] - endPoint[0]
3373                 );
3374         }else{
3375             newSize = this.dragSpecs.startSize + 
3376                 (this.placement == Roo.SplitBar.TOP ?
3377                     endPoint[1] - this.dragSpecs.startPoint[1] :
3378                     this.dragSpecs.startPoint[1] - endPoint[1]
3379                 );
3380         }
3381         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3382         if(newSize != this.dragSpecs.startSize){
3383             if(this.fireEvent('beforeapply', this, newSize) !== false){
3384                 this.adapter.setElementSize(this, newSize);
3385                 this.fireEvent("moved", this, newSize);
3386                 this.fireEvent("resize", this, newSize);
3387             }
3388         }
3389     },
3390     
3391     /**
3392      * Get the adapter this SplitBar uses
3393      * @return The adapter object
3394      */
3395     getAdapter : function(){
3396         return this.adapter;
3397     },
3398     
3399     /**
3400      * Set the adapter this SplitBar uses
3401      * @param {Object} adapter A SplitBar adapter object
3402      */
3403     setAdapter : function(adapter){
3404         this.adapter = adapter;
3405         this.adapter.init(this);
3406     },
3407     
3408     /**
3409      * Gets the minimum size for the resizing element
3410      * @return {Number} The minimum size
3411      */
3412     getMinimumSize : function(){
3413         return this.minSize;
3414     },
3415     
3416     /**
3417      * Sets the minimum size for the resizing element
3418      * @param {Number} minSize The minimum size
3419      */
3420     setMinimumSize : function(minSize){
3421         this.minSize = minSize;
3422     },
3423     
3424     /**
3425      * Gets the maximum size for the resizing element
3426      * @return {Number} The maximum size
3427      */
3428     getMaximumSize : function(){
3429         return this.maxSize;
3430     },
3431     
3432     /**
3433      * Sets the maximum size for the resizing element
3434      * @param {Number} maxSize The maximum size
3435      */
3436     setMaximumSize : function(maxSize){
3437         this.maxSize = maxSize;
3438     },
3439     
3440     /**
3441      * Sets the initialize size for the resizing element
3442      * @param {Number} size The initial size
3443      */
3444     setCurrentSize : function(size){
3445         var oldAnimate = this.animate;
3446         this.animate = false;
3447         this.adapter.setElementSize(this, size);
3448         this.animate = oldAnimate;
3449     },
3450     
3451     /**
3452      * Destroy this splitbar. 
3453      * @param {Boolean} removeEl True to remove the element
3454      */
3455     destroy : function(removeEl){
3456         if(this.shim){
3457             this.shim.remove();
3458         }
3459         this.dd.unreg();
3460         this.proxy.parentNode.removeChild(this.proxy);
3461         if(removeEl){
3462             this.el.remove();
3463         }
3464     }
3465 });
3466
3467 /**
3468  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3469  */
3470 Roo.SplitBar.createProxy = function(dir){
3471     var proxy = new Roo.Element(document.createElement("div"));
3472     proxy.unselectable();
3473     var cls = 'x-splitbar-proxy';
3474     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3475     document.body.appendChild(proxy.dom);
3476     return proxy.dom;
3477 };
3478
3479 /** 
3480  * @class Roo.SplitBar.BasicLayoutAdapter
3481  * Default Adapter. It assumes the splitter and resizing element are not positioned
3482  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3483  */
3484 Roo.SplitBar.BasicLayoutAdapter = function(){
3485 };
3486
3487 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3488     // do nothing for now
3489     init : function(s){
3490     
3491     },
3492     /**
3493      * Called before drag operations to get the current size of the resizing element. 
3494      * @param {Roo.SplitBar} s The SplitBar using this adapter
3495      */
3496      getElementSize : function(s){
3497         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3498             return s.resizingEl.getWidth();
3499         }else{
3500             return s.resizingEl.getHeight();
3501         }
3502     },
3503     
3504     /**
3505      * Called after drag operations to set the size of the resizing element.
3506      * @param {Roo.SplitBar} s The SplitBar using this adapter
3507      * @param {Number} newSize The new size to set
3508      * @param {Function} onComplete A function to be invoked when resizing is complete
3509      */
3510     setElementSize : function(s, newSize, onComplete){
3511         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3512             if(!s.animate){
3513                 s.resizingEl.setWidth(newSize);
3514                 if(onComplete){
3515                     onComplete(s, newSize);
3516                 }
3517             }else{
3518                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3519             }
3520         }else{
3521             
3522             if(!s.animate){
3523                 s.resizingEl.setHeight(newSize);
3524                 if(onComplete){
3525                     onComplete(s, newSize);
3526                 }
3527             }else{
3528                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3529             }
3530         }
3531     }
3532 };
3533
3534 /** 
3535  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3536  * @extends Roo.SplitBar.BasicLayoutAdapter
3537  * Adapter that  moves the splitter element to align with the resized sizing element. 
3538  * Used with an absolute positioned SplitBar.
3539  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3540  * document.body, make sure you assign an id to the body element.
3541  */
3542 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3543     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3544     this.container = Roo.get(container);
3545 };
3546
3547 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3548     init : function(s){
3549         this.basic.init(s);
3550     },
3551     
3552     getElementSize : function(s){
3553         return this.basic.getElementSize(s);
3554     },
3555     
3556     setElementSize : function(s, newSize, onComplete){
3557         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3558     },
3559     
3560     moveSplitter : function(s){
3561         var yes = Roo.SplitBar;
3562         switch(s.placement){
3563             case yes.LEFT:
3564                 s.el.setX(s.resizingEl.getRight());
3565                 break;
3566             case yes.RIGHT:
3567                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3568                 break;
3569             case yes.TOP:
3570                 s.el.setY(s.resizingEl.getBottom());
3571                 break;
3572             case yes.BOTTOM:
3573                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3574                 break;
3575         }
3576     }
3577 };
3578
3579 /**
3580  * Orientation constant - Create a vertical SplitBar
3581  * @static
3582  * @type Number
3583  */
3584 Roo.SplitBar.VERTICAL = 1;
3585
3586 /**
3587  * Orientation constant - Create a horizontal SplitBar
3588  * @static
3589  * @type Number
3590  */
3591 Roo.SplitBar.HORIZONTAL = 2;
3592
3593 /**
3594  * Placement constant - The resizing element is to the left of the splitter element
3595  * @static
3596  * @type Number
3597  */
3598 Roo.SplitBar.LEFT = 1;
3599
3600 /**
3601  * Placement constant - The resizing element is to the right of the splitter element
3602  * @static
3603  * @type Number
3604  */
3605 Roo.SplitBar.RIGHT = 2;
3606
3607 /**
3608  * Placement constant - The resizing element is positioned above the splitter element
3609  * @static
3610  * @type Number
3611  */
3612 Roo.SplitBar.TOP = 3;
3613
3614 /**
3615  * Placement constant - The resizing element is positioned under splitter element
3616  * @static
3617  * @type Number
3618  */
3619 Roo.SplitBar.BOTTOM = 4;
3620 /*
3621  * Based on:
3622  * Ext JS Library 1.1.1
3623  * Copyright(c) 2006-2007, Ext JS, LLC.
3624  *
3625  * Originally Released Under LGPL - original licence link has changed is not relivant.
3626  *
3627  * Fork - LGPL
3628  * <script type="text/javascript">
3629  */
3630
3631 /**
3632  * @class Roo.View
3633  * @extends Roo.util.Observable
3634  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3635  * This class also supports single and multi selection modes. <br>
3636  * Create a data model bound view:
3637  <pre><code>
3638  var store = new Roo.data.Store(...);
3639
3640  var view = new Roo.View({
3641     el : "my-element",
3642     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3643  
3644     singleSelect: true,
3645     selectedClass: "ydataview-selected",
3646     store: store
3647  });
3648
3649  // listen for node click?
3650  view.on("click", function(vw, index, node, e){
3651  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3652  });
3653
3654  // load XML data
3655  dataModel.load("foobar.xml");
3656  </code></pre>
3657  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3658  * <br><br>
3659  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3660  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3661  * 
3662  * Note: old style constructor is still suported (container, template, config)
3663  * 
3664  * @constructor
3665  * Create a new View
3666  * @param {Object} config The config object
3667  * 
3668  */
3669 Roo.View = function(config, depreciated_tpl, depreciated_config){
3670     
3671     this.parent = false;
3672     
3673     if (typeof(depreciated_tpl) == 'undefined') {
3674         // new way.. - universal constructor.
3675         Roo.apply(this, config);
3676         this.el  = Roo.get(this.el);
3677     } else {
3678         // old format..
3679         this.el  = Roo.get(config);
3680         this.tpl = depreciated_tpl;
3681         Roo.apply(this, depreciated_config);
3682     }
3683     this.wrapEl  = this.el.wrap().wrap();
3684     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3685     
3686     
3687     if(typeof(this.tpl) == "string"){
3688         this.tpl = new Roo.Template(this.tpl);
3689     } else {
3690         // support xtype ctors..
3691         this.tpl = new Roo.factory(this.tpl, Roo);
3692     }
3693     
3694     
3695     this.tpl.compile();
3696     
3697     /** @private */
3698     this.addEvents({
3699         /**
3700          * @event beforeclick
3701          * Fires before a click is processed. Returns false to cancel the default action.
3702          * @param {Roo.View} this
3703          * @param {Number} index The index of the target node
3704          * @param {HTMLElement} node The target node
3705          * @param {Roo.EventObject} e The raw event object
3706          */
3707             "beforeclick" : true,
3708         /**
3709          * @event click
3710          * Fires when a template node is clicked.
3711          * @param {Roo.View} this
3712          * @param {Number} index The index of the target node
3713          * @param {HTMLElement} node The target node
3714          * @param {Roo.EventObject} e The raw event object
3715          */
3716             "click" : true,
3717         /**
3718          * @event dblclick
3719          * Fires when a template node is double clicked.
3720          * @param {Roo.View} this
3721          * @param {Number} index The index of the target node
3722          * @param {HTMLElement} node The target node
3723          * @param {Roo.EventObject} e The raw event object
3724          */
3725             "dblclick" : true,
3726         /**
3727          * @event contextmenu
3728          * Fires when a template node is right clicked.
3729          * @param {Roo.View} this
3730          * @param {Number} index The index of the target node
3731          * @param {HTMLElement} node The target node
3732          * @param {Roo.EventObject} e The raw event object
3733          */
3734             "contextmenu" : true,
3735         /**
3736          * @event selectionchange
3737          * Fires when the selected nodes change.
3738          * @param {Roo.View} this
3739          * @param {Array} selections Array of the selected nodes
3740          */
3741             "selectionchange" : true,
3742     
3743         /**
3744          * @event beforeselect
3745          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3746          * @param {Roo.View} this
3747          * @param {HTMLElement} node The node to be selected
3748          * @param {Array} selections Array of currently selected nodes
3749          */
3750             "beforeselect" : true,
3751         /**
3752          * @event preparedata
3753          * Fires on every row to render, to allow you to change the data.
3754          * @param {Roo.View} this
3755          * @param {Object} data to be rendered (change this)
3756          */
3757           "preparedata" : true
3758           
3759           
3760         });
3761
3762
3763
3764     this.el.on({
3765         "click": this.onClick,
3766         "dblclick": this.onDblClick,
3767         "contextmenu": this.onContextMenu,
3768         scope:this
3769     });
3770
3771     this.selections = [];
3772     this.nodes = [];
3773     this.cmp = new Roo.CompositeElementLite([]);
3774     if(this.store){
3775         this.store = Roo.factory(this.store, Roo.data);
3776         this.setStore(this.store, true);
3777     }
3778     
3779     if ( this.footer && this.footer.xtype) {
3780            
3781          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3782         
3783         this.footer.dataSource = this.store;
3784         this.footer.container = fctr;
3785         this.footer = Roo.factory(this.footer, Roo);
3786         fctr.insertFirst(this.el);
3787         
3788         // this is a bit insane - as the paging toolbar seems to detach the el..
3789 //        dom.parentNode.parentNode.parentNode
3790          // they get detached?
3791     }
3792     
3793     
3794     Roo.View.superclass.constructor.call(this);
3795     
3796     
3797 };
3798
3799 Roo.extend(Roo.View, Roo.util.Observable, {
3800     
3801      /**
3802      * @cfg {Roo.data.Store} store Data store to load data from.
3803      */
3804     store : false,
3805     
3806     /**
3807      * @cfg {String|Roo.Element} el The container element.
3808      */
3809     el : '',
3810     
3811     /**
3812      * @cfg {String|Roo.Template} tpl The template used by this View 
3813      */
3814     tpl : false,
3815     /**
3816      * @cfg {String} dataName the named area of the template to use as the data area
3817      *                          Works with domtemplates roo-name="name"
3818      */
3819     dataName: false,
3820     /**
3821      * @cfg {String} selectedClass The css class to add to selected nodes
3822      */
3823     selectedClass : "x-view-selected",
3824      /**
3825      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3826      */
3827     emptyText : "",
3828     
3829     /**
3830      * @cfg {String} text to display on mask (default Loading)
3831      */
3832     mask : false,
3833     /**
3834      * @cfg {Boolean} multiSelect Allow multiple selection
3835      */
3836     multiSelect : false,
3837     /**
3838      * @cfg {Boolean} singleSelect Allow single selection
3839      */
3840     singleSelect:  false,
3841     
3842     /**
3843      * @cfg {Boolean} toggleSelect - selecting 
3844      */
3845     toggleSelect : false,
3846     
3847     /**
3848      * @cfg {Boolean} tickable - selecting 
3849      */
3850     tickable : false,
3851     
3852     /**
3853      * Returns the element this view is bound to.
3854      * @return {Roo.Element}
3855      */
3856     getEl : function(){
3857         return this.wrapEl;
3858     },
3859     
3860     
3861
3862     /**
3863      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3864      */
3865     refresh : function(){
3866         //Roo.log('refresh');
3867         var t = this.tpl;
3868         
3869         // if we are using something like 'domtemplate', then
3870         // the what gets used is:
3871         // t.applySubtemplate(NAME, data, wrapping data..)
3872         // the outer template then get' applied with
3873         //     the store 'extra data'
3874         // and the body get's added to the
3875         //      roo-name="data" node?
3876         //      <span class='roo-tpl-{name}'></span> ?????
3877         
3878         
3879         
3880         this.clearSelections();
3881         this.el.update("");
3882         var html = [];
3883         var records = this.store.getRange();
3884         if(records.length < 1) {
3885             
3886             // is this valid??  = should it render a template??
3887             
3888             this.el.update(this.emptyText);
3889             return;
3890         }
3891         var el = this.el;
3892         if (this.dataName) {
3893             this.el.update(t.apply(this.store.meta)); //????
3894             el = this.el.child('.roo-tpl-' + this.dataName);
3895         }
3896         
3897         for(var i = 0, len = records.length; i < len; i++){
3898             var data = this.prepareData(records[i].data, i, records[i]);
3899             this.fireEvent("preparedata", this, data, i, records[i]);
3900             
3901             var d = Roo.apply({}, data);
3902             
3903             if(this.tickable){
3904                 Roo.apply(d, {'roo-id' : Roo.id()});
3905                 
3906                 var _this = this;
3907             
3908                 Roo.each(this.parent.item, function(item){
3909                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3910                         return;
3911                     }
3912                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3913                 });
3914             }
3915             
3916             html[html.length] = Roo.util.Format.trim(
3917                 this.dataName ?
3918                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3919                     t.apply(d)
3920             );
3921         }
3922         
3923         
3924         
3925         el.update(html.join(""));
3926         this.nodes = el.dom.childNodes;
3927         this.updateIndexes(0);
3928     },
3929     
3930
3931     /**
3932      * Function to override to reformat the data that is sent to
3933      * the template for each node.
3934      * DEPRICATED - use the preparedata event handler.
3935      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3936      * a JSON object for an UpdateManager bound view).
3937      */
3938     prepareData : function(data, index, record)
3939     {
3940         this.fireEvent("preparedata", this, data, index, record);
3941         return data;
3942     },
3943
3944     onUpdate : function(ds, record){
3945         // Roo.log('on update');   
3946         this.clearSelections();
3947         var index = this.store.indexOf(record);
3948         var n = this.nodes[index];
3949         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3950         n.parentNode.removeChild(n);
3951         this.updateIndexes(index, index);
3952     },
3953
3954     
3955     
3956 // --------- FIXME     
3957     onAdd : function(ds, records, index)
3958     {
3959         //Roo.log(['on Add', ds, records, index] );        
3960         this.clearSelections();
3961         if(this.nodes.length == 0){
3962             this.refresh();
3963             return;
3964         }
3965         var n = this.nodes[index];
3966         for(var i = 0, len = records.length; i < len; i++){
3967             var d = this.prepareData(records[i].data, i, records[i]);
3968             if(n){
3969                 this.tpl.insertBefore(n, d);
3970             }else{
3971                 
3972                 this.tpl.append(this.el, d);
3973             }
3974         }
3975         this.updateIndexes(index);
3976     },
3977
3978     onRemove : function(ds, record, index){
3979        // Roo.log('onRemove');
3980         this.clearSelections();
3981         var el = this.dataName  ?
3982             this.el.child('.roo-tpl-' + this.dataName) :
3983             this.el; 
3984         
3985         el.dom.removeChild(this.nodes[index]);
3986         this.updateIndexes(index);
3987     },
3988
3989     /**
3990      * Refresh an individual node.
3991      * @param {Number} index
3992      */
3993     refreshNode : function(index){
3994         this.onUpdate(this.store, this.store.getAt(index));
3995     },
3996
3997     updateIndexes : function(startIndex, endIndex){
3998         var ns = this.nodes;
3999         startIndex = startIndex || 0;
4000         endIndex = endIndex || ns.length - 1;
4001         for(var i = startIndex; i <= endIndex; i++){
4002             ns[i].nodeIndex = i;
4003         }
4004     },
4005
4006     /**
4007      * Changes the data store this view uses and refresh the view.
4008      * @param {Store} store
4009      */
4010     setStore : function(store, initial){
4011         if(!initial && this.store){
4012             this.store.un("datachanged", this.refresh);
4013             this.store.un("add", this.onAdd);
4014             this.store.un("remove", this.onRemove);
4015             this.store.un("update", this.onUpdate);
4016             this.store.un("clear", this.refresh);
4017             this.store.un("beforeload", this.onBeforeLoad);
4018             this.store.un("load", this.onLoad);
4019             this.store.un("loadexception", this.onLoad);
4020         }
4021         if(store){
4022           
4023             store.on("datachanged", this.refresh, this);
4024             store.on("add", this.onAdd, this);
4025             store.on("remove", this.onRemove, this);
4026             store.on("update", this.onUpdate, this);
4027             store.on("clear", this.refresh, this);
4028             store.on("beforeload", this.onBeforeLoad, this);
4029             store.on("load", this.onLoad, this);
4030             store.on("loadexception", this.onLoad, this);
4031         }
4032         
4033         if(store){
4034             this.refresh();
4035         }
4036     },
4037     /**
4038      * onbeforeLoad - masks the loading area.
4039      *
4040      */
4041     onBeforeLoad : function(store,opts)
4042     {
4043          //Roo.log('onBeforeLoad');   
4044         if (!opts.add) {
4045             this.el.update("");
4046         }
4047         this.el.mask(this.mask ? this.mask : "Loading" ); 
4048     },
4049     onLoad : function ()
4050     {
4051         this.el.unmask();
4052     },
4053     
4054
4055     /**
4056      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4057      * @param {HTMLElement} node
4058      * @return {HTMLElement} The template node
4059      */
4060     findItemFromChild : function(node){
4061         var el = this.dataName  ?
4062             this.el.child('.roo-tpl-' + this.dataName,true) :
4063             this.el.dom; 
4064         
4065         if(!node || node.parentNode == el){
4066                     return node;
4067             }
4068             var p = node.parentNode;
4069             while(p && p != el){
4070             if(p.parentNode == el){
4071                 return p;
4072             }
4073             p = p.parentNode;
4074         }
4075             return null;
4076     },
4077
4078     /** @ignore */
4079     onClick : function(e){
4080         var item = this.findItemFromChild(e.getTarget());
4081         if(item){
4082             var index = this.indexOf(item);
4083             if(this.onItemClick(item, index, e) !== false){
4084                 this.fireEvent("click", this, index, item, e);
4085             }
4086         }else{
4087             this.clearSelections();
4088         }
4089     },
4090
4091     /** @ignore */
4092     onContextMenu : function(e){
4093         var item = this.findItemFromChild(e.getTarget());
4094         if(item){
4095             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4096         }
4097     },
4098
4099     /** @ignore */
4100     onDblClick : function(e){
4101         var item = this.findItemFromChild(e.getTarget());
4102         if(item){
4103             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4104         }
4105     },
4106
4107     onItemClick : function(item, index, e)
4108     {
4109         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4110             return false;
4111         }
4112         if (this.toggleSelect) {
4113             var m = this.isSelected(item) ? 'unselect' : 'select';
4114             //Roo.log(m);
4115             var _t = this;
4116             _t[m](item, true, false);
4117             return true;
4118         }
4119         if(this.multiSelect || this.singleSelect){
4120             if(this.multiSelect && e.shiftKey && this.lastSelection){
4121                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4122             }else{
4123                 this.select(item, this.multiSelect && e.ctrlKey);
4124                 this.lastSelection = item;
4125             }
4126             
4127             if(!this.tickable){
4128                 e.preventDefault();
4129             }
4130             
4131         }
4132         return true;
4133     },
4134
4135     /**
4136      * Get the number of selected nodes.
4137      * @return {Number}
4138      */
4139     getSelectionCount : function(){
4140         return this.selections.length;
4141     },
4142
4143     /**
4144      * Get the currently selected nodes.
4145      * @return {Array} An array of HTMLElements
4146      */
4147     getSelectedNodes : function(){
4148         return this.selections;
4149     },
4150
4151     /**
4152      * Get the indexes of the selected nodes.
4153      * @return {Array}
4154      */
4155     getSelectedIndexes : function(){
4156         var indexes = [], s = this.selections;
4157         for(var i = 0, len = s.length; i < len; i++){
4158             indexes.push(s[i].nodeIndex);
4159         }
4160         return indexes;
4161     },
4162
4163     /**
4164      * Clear all selections
4165      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4166      */
4167     clearSelections : function(suppressEvent){
4168         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4169             this.cmp.elements = this.selections;
4170             this.cmp.removeClass(this.selectedClass);
4171             this.selections = [];
4172             if(!suppressEvent){
4173                 this.fireEvent("selectionchange", this, this.selections);
4174             }
4175         }
4176     },
4177
4178     /**
4179      * Returns true if the passed node is selected
4180      * @param {HTMLElement/Number} node The node or node index
4181      * @return {Boolean}
4182      */
4183     isSelected : function(node){
4184         var s = this.selections;
4185         if(s.length < 1){
4186             return false;
4187         }
4188         node = this.getNode(node);
4189         return s.indexOf(node) !== -1;
4190     },
4191
4192     /**
4193      * Selects nodes.
4194      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4195      * @param {Boolean} keepExisting (optional) true to keep existing selections
4196      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4197      */
4198     select : function(nodeInfo, keepExisting, suppressEvent){
4199         if(nodeInfo instanceof Array){
4200             if(!keepExisting){
4201                 this.clearSelections(true);
4202             }
4203             for(var i = 0, len = nodeInfo.length; i < len; i++){
4204                 this.select(nodeInfo[i], true, true);
4205             }
4206             return;
4207         } 
4208         var node = this.getNode(nodeInfo);
4209         if(!node || this.isSelected(node)){
4210             return; // already selected.
4211         }
4212         if(!keepExisting){
4213             this.clearSelections(true);
4214         }
4215         
4216         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4217             Roo.fly(node).addClass(this.selectedClass);
4218             this.selections.push(node);
4219             if(!suppressEvent){
4220                 this.fireEvent("selectionchange", this, this.selections);
4221             }
4222         }
4223         
4224         
4225     },
4226       /**
4227      * Unselects nodes.
4228      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4229      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4230      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4231      */
4232     unselect : function(nodeInfo, keepExisting, suppressEvent)
4233     {
4234         if(nodeInfo instanceof Array){
4235             Roo.each(this.selections, function(s) {
4236                 this.unselect(s, nodeInfo);
4237             }, this);
4238             return;
4239         }
4240         var node = this.getNode(nodeInfo);
4241         if(!node || !this.isSelected(node)){
4242             //Roo.log("not selected");
4243             return; // not selected.
4244         }
4245         // fireevent???
4246         var ns = [];
4247         Roo.each(this.selections, function(s) {
4248             if (s == node ) {
4249                 Roo.fly(node).removeClass(this.selectedClass);
4250
4251                 return;
4252             }
4253             ns.push(s);
4254         },this);
4255         
4256         this.selections= ns;
4257         this.fireEvent("selectionchange", this, this.selections);
4258     },
4259
4260     /**
4261      * Gets a template node.
4262      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4263      * @return {HTMLElement} The node or null if it wasn't found
4264      */
4265     getNode : function(nodeInfo){
4266         if(typeof nodeInfo == "string"){
4267             return document.getElementById(nodeInfo);
4268         }else if(typeof nodeInfo == "number"){
4269             return this.nodes[nodeInfo];
4270         }
4271         return nodeInfo;
4272     },
4273
4274     /**
4275      * Gets a range template nodes.
4276      * @param {Number} startIndex
4277      * @param {Number} endIndex
4278      * @return {Array} An array of nodes
4279      */
4280     getNodes : function(start, end){
4281         var ns = this.nodes;
4282         start = start || 0;
4283         end = typeof end == "undefined" ? ns.length - 1 : end;
4284         var nodes = [];
4285         if(start <= end){
4286             for(var i = start; i <= end; i++){
4287                 nodes.push(ns[i]);
4288             }
4289         } else{
4290             for(var i = start; i >= end; i--){
4291                 nodes.push(ns[i]);
4292             }
4293         }
4294         return nodes;
4295     },
4296
4297     /**
4298      * Finds the index of the passed node
4299      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4300      * @return {Number} The index of the node or -1
4301      */
4302     indexOf : function(node){
4303         node = this.getNode(node);
4304         if(typeof node.nodeIndex == "number"){
4305             return node.nodeIndex;
4306         }
4307         var ns = this.nodes;
4308         for(var i = 0, len = ns.length; i < len; i++){
4309             if(ns[i] == node){
4310                 return i;
4311             }
4312         }
4313         return -1;
4314     }
4315 });
4316 /*
4317  * Based on:
4318  * Ext JS Library 1.1.1
4319  * Copyright(c) 2006-2007, Ext JS, LLC.
4320  *
4321  * Originally Released Under LGPL - original licence link has changed is not relivant.
4322  *
4323  * Fork - LGPL
4324  * <script type="text/javascript">
4325  */
4326
4327 /**
4328  * @class Roo.JsonView
4329  * @extends Roo.View
4330  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4331 <pre><code>
4332 var view = new Roo.JsonView({
4333     container: "my-element",
4334     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4335     multiSelect: true, 
4336     jsonRoot: "data" 
4337 });
4338
4339 // listen for node click?
4340 view.on("click", function(vw, index, node, e){
4341     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4342 });
4343
4344 // direct load of JSON data
4345 view.load("foobar.php");
4346
4347 // Example from my blog list
4348 var tpl = new Roo.Template(
4349     '&lt;div class="entry"&gt;' +
4350     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4351     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4352     "&lt;/div&gt;&lt;hr /&gt;"
4353 );
4354
4355 var moreView = new Roo.JsonView({
4356     container :  "entry-list", 
4357     template : tpl,
4358     jsonRoot: "posts"
4359 });
4360 moreView.on("beforerender", this.sortEntries, this);
4361 moreView.load({
4362     url: "/blog/get-posts.php",
4363     params: "allposts=true",
4364     text: "Loading Blog Entries..."
4365 });
4366 </code></pre>
4367
4368 * Note: old code is supported with arguments : (container, template, config)
4369
4370
4371  * @constructor
4372  * Create a new JsonView
4373  * 
4374  * @param {Object} config The config object
4375  * 
4376  */
4377 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4378     
4379     
4380     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4381
4382     var um = this.el.getUpdateManager();
4383     um.setRenderer(this);
4384     um.on("update", this.onLoad, this);
4385     um.on("failure", this.onLoadException, this);
4386
4387     /**
4388      * @event beforerender
4389      * Fires before rendering of the downloaded JSON data.
4390      * @param {Roo.JsonView} this
4391      * @param {Object} data The JSON data loaded
4392      */
4393     /**
4394      * @event load
4395      * Fires when data is loaded.
4396      * @param {Roo.JsonView} this
4397      * @param {Object} data The JSON data loaded
4398      * @param {Object} response The raw Connect response object
4399      */
4400     /**
4401      * @event loadexception
4402      * Fires when loading fails.
4403      * @param {Roo.JsonView} this
4404      * @param {Object} response The raw Connect response object
4405      */
4406     this.addEvents({
4407         'beforerender' : true,
4408         'load' : true,
4409         'loadexception' : true
4410     });
4411 };
4412 Roo.extend(Roo.JsonView, Roo.View, {
4413     /**
4414      * @type {String} The root property in the loaded JSON object that contains the data
4415      */
4416     jsonRoot : "",
4417
4418     /**
4419      * Refreshes the view.
4420      */
4421     refresh : function(){
4422         this.clearSelections();
4423         this.el.update("");
4424         var html = [];
4425         var o = this.jsonData;
4426         if(o && o.length > 0){
4427             for(var i = 0, len = o.length; i < len; i++){
4428                 var data = this.prepareData(o[i], i, o);
4429                 html[html.length] = this.tpl.apply(data);
4430             }
4431         }else{
4432             html.push(this.emptyText);
4433         }
4434         this.el.update(html.join(""));
4435         this.nodes = this.el.dom.childNodes;
4436         this.updateIndexes(0);
4437     },
4438
4439     /**
4440      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4441      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4442      <pre><code>
4443      view.load({
4444          url: "your-url.php",
4445          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4446          callback: yourFunction,
4447          scope: yourObject, //(optional scope)
4448          discardUrl: false,
4449          nocache: false,
4450          text: "Loading...",
4451          timeout: 30,
4452          scripts: false
4453      });
4454      </code></pre>
4455      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4456      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4457      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4458      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4459      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4460      */
4461     load : function(){
4462         var um = this.el.getUpdateManager();
4463         um.update.apply(um, arguments);
4464     },
4465
4466     // note - render is a standard framework call...
4467     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4468     render : function(el, response){
4469         
4470         this.clearSelections();
4471         this.el.update("");
4472         var o;
4473         try{
4474             if (response != '') {
4475                 o = Roo.util.JSON.decode(response.responseText);
4476                 if(this.jsonRoot){
4477                     
4478                     o = o[this.jsonRoot];
4479                 }
4480             }
4481         } catch(e){
4482         }
4483         /**
4484          * The current JSON data or null
4485          */
4486         this.jsonData = o;
4487         this.beforeRender();
4488         this.refresh();
4489     },
4490
4491 /**
4492  * Get the number of records in the current JSON dataset
4493  * @return {Number}
4494  */
4495     getCount : function(){
4496         return this.jsonData ? this.jsonData.length : 0;
4497     },
4498
4499 /**
4500  * Returns the JSON object for the specified node(s)
4501  * @param {HTMLElement/Array} node The node or an array of nodes
4502  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4503  * you get the JSON object for the node
4504  */
4505     getNodeData : function(node){
4506         if(node instanceof Array){
4507             var data = [];
4508             for(var i = 0, len = node.length; i < len; i++){
4509                 data.push(this.getNodeData(node[i]));
4510             }
4511             return data;
4512         }
4513         return this.jsonData[this.indexOf(node)] || null;
4514     },
4515
4516     beforeRender : function(){
4517         this.snapshot = this.jsonData;
4518         if(this.sortInfo){
4519             this.sort.apply(this, this.sortInfo);
4520         }
4521         this.fireEvent("beforerender", this, this.jsonData);
4522     },
4523
4524     onLoad : function(el, o){
4525         this.fireEvent("load", this, this.jsonData, o);
4526     },
4527
4528     onLoadException : function(el, o){
4529         this.fireEvent("loadexception", this, o);
4530     },
4531
4532 /**
4533  * Filter the data by a specific property.
4534  * @param {String} property A property on your JSON objects
4535  * @param {String/RegExp} value Either string that the property values
4536  * should start with, or a RegExp to test against the property
4537  */
4538     filter : function(property, value){
4539         if(this.jsonData){
4540             var data = [];
4541             var ss = this.snapshot;
4542             if(typeof value == "string"){
4543                 var vlen = value.length;
4544                 if(vlen == 0){
4545                     this.clearFilter();
4546                     return;
4547                 }
4548                 value = value.toLowerCase();
4549                 for(var i = 0, len = ss.length; i < len; i++){
4550                     var o = ss[i];
4551                     if(o[property].substr(0, vlen).toLowerCase() == value){
4552                         data.push(o);
4553                     }
4554                 }
4555             } else if(value.exec){ // regex?
4556                 for(var i = 0, len = ss.length; i < len; i++){
4557                     var o = ss[i];
4558                     if(value.test(o[property])){
4559                         data.push(o);
4560                     }
4561                 }
4562             } else{
4563                 return;
4564             }
4565             this.jsonData = data;
4566             this.refresh();
4567         }
4568     },
4569
4570 /**
4571  * Filter by a function. The passed function will be called with each
4572  * object in the current dataset. If the function returns true the value is kept,
4573  * otherwise it is filtered.
4574  * @param {Function} fn
4575  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4576  */
4577     filterBy : function(fn, scope){
4578         if(this.jsonData){
4579             var data = [];
4580             var ss = this.snapshot;
4581             for(var i = 0, len = ss.length; i < len; i++){
4582                 var o = ss[i];
4583                 if(fn.call(scope || this, o)){
4584                     data.push(o);
4585                 }
4586             }
4587             this.jsonData = data;
4588             this.refresh();
4589         }
4590     },
4591
4592 /**
4593  * Clears the current filter.
4594  */
4595     clearFilter : function(){
4596         if(this.snapshot && this.jsonData != this.snapshot){
4597             this.jsonData = this.snapshot;
4598             this.refresh();
4599         }
4600     },
4601
4602
4603 /**
4604  * Sorts the data for this view and refreshes it.
4605  * @param {String} property A property on your JSON objects to sort on
4606  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4607  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4608  */
4609     sort : function(property, dir, sortType){
4610         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4611         if(this.jsonData){
4612             var p = property;
4613             var dsc = dir && dir.toLowerCase() == "desc";
4614             var f = function(o1, o2){
4615                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4616                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4617                 ;
4618                 if(v1 < v2){
4619                     return dsc ? +1 : -1;
4620                 } else if(v1 > v2){
4621                     return dsc ? -1 : +1;
4622                 } else{
4623                     return 0;
4624                 }
4625             };
4626             this.jsonData.sort(f);
4627             this.refresh();
4628             if(this.jsonData != this.snapshot){
4629                 this.snapshot.sort(f);
4630             }
4631         }
4632     }
4633 });/*
4634  * Based on:
4635  * Ext JS Library 1.1.1
4636  * Copyright(c) 2006-2007, Ext JS, LLC.
4637  *
4638  * Originally Released Under LGPL - original licence link has changed is not relivant.
4639  *
4640  * Fork - LGPL
4641  * <script type="text/javascript">
4642  */
4643  
4644
4645 /**
4646  * @class Roo.ColorPalette
4647  * @extends Roo.Component
4648  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4649  * Here's an example of typical usage:
4650  * <pre><code>
4651 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4652 cp.render('my-div');
4653
4654 cp.on('select', function(palette, selColor){
4655     // do something with selColor
4656 });
4657 </code></pre>
4658  * @constructor
4659  * Create a new ColorPalette
4660  * @param {Object} config The config object
4661  */
4662 Roo.ColorPalette = function(config){
4663     Roo.ColorPalette.superclass.constructor.call(this, config);
4664     this.addEvents({
4665         /**
4666              * @event select
4667              * Fires when a color is selected
4668              * @param {ColorPalette} this
4669              * @param {String} color The 6-digit color hex code (without the # symbol)
4670              */
4671         select: true
4672     });
4673
4674     if(this.handler){
4675         this.on("select", this.handler, this.scope, true);
4676     }
4677 };
4678 Roo.extend(Roo.ColorPalette, Roo.Component, {
4679     /**
4680      * @cfg {String} itemCls
4681      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4682      */
4683     itemCls : "x-color-palette",
4684     /**
4685      * @cfg {String} value
4686      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4687      * the hex codes are case-sensitive.
4688      */
4689     value : null,
4690     clickEvent:'click',
4691     // private
4692     ctype: "Roo.ColorPalette",
4693
4694     /**
4695      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4696      */
4697     allowReselect : false,
4698
4699     /**
4700      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4701      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4702      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4703      * of colors with the width setting until the box is symmetrical.</p>
4704      * <p>You can override individual colors if needed:</p>
4705      * <pre><code>
4706 var cp = new Roo.ColorPalette();
4707 cp.colors[0] = "FF0000";  // change the first box to red
4708 </code></pre>
4709
4710 Or you can provide a custom array of your own for complete control:
4711 <pre><code>
4712 var cp = new Roo.ColorPalette();
4713 cp.colors = ["000000", "993300", "333300"];
4714 </code></pre>
4715      * @type Array
4716      */
4717     colors : [
4718         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4719         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4720         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4721         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4722         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4723     ],
4724
4725     // private
4726     onRender : function(container, position){
4727         var t = new Roo.MasterTemplate(
4728             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4729         );
4730         var c = this.colors;
4731         for(var i = 0, len = c.length; i < len; i++){
4732             t.add([c[i]]);
4733         }
4734         var el = document.createElement("div");
4735         el.className = this.itemCls;
4736         t.overwrite(el);
4737         container.dom.insertBefore(el, position);
4738         this.el = Roo.get(el);
4739         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4740         if(this.clickEvent != 'click'){
4741             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4742         }
4743     },
4744
4745     // private
4746     afterRender : function(){
4747         Roo.ColorPalette.superclass.afterRender.call(this);
4748         if(this.value){
4749             var s = this.value;
4750             this.value = null;
4751             this.select(s);
4752         }
4753     },
4754
4755     // private
4756     handleClick : function(e, t){
4757         e.preventDefault();
4758         if(!this.disabled){
4759             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4760             this.select(c.toUpperCase());
4761         }
4762     },
4763
4764     /**
4765      * Selects the specified color in the palette (fires the select event)
4766      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4767      */
4768     select : function(color){
4769         color = color.replace("#", "");
4770         if(color != this.value || this.allowReselect){
4771             var el = this.el;
4772             if(this.value){
4773                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4774             }
4775             el.child("a.color-"+color).addClass("x-color-palette-sel");
4776             this.value = color;
4777             this.fireEvent("select", this, color);
4778         }
4779     }
4780 });/*
4781  * Based on:
4782  * Ext JS Library 1.1.1
4783  * Copyright(c) 2006-2007, Ext JS, LLC.
4784  *
4785  * Originally Released Under LGPL - original licence link has changed is not relivant.
4786  *
4787  * Fork - LGPL
4788  * <script type="text/javascript">
4789  */
4790  
4791 /**
4792  * @class Roo.DatePicker
4793  * @extends Roo.Component
4794  * Simple date picker class.
4795  * @constructor
4796  * Create a new DatePicker
4797  * @param {Object} config The config object
4798  */
4799 Roo.DatePicker = function(config){
4800     Roo.DatePicker.superclass.constructor.call(this, config);
4801
4802     this.value = config && config.value ?
4803                  config.value.clearTime() : new Date().clearTime();
4804
4805     this.addEvents({
4806         /**
4807              * @event select
4808              * Fires when a date is selected
4809              * @param {DatePicker} this
4810              * @param {Date} date The selected date
4811              */
4812         'select': true,
4813         /**
4814              * @event monthchange
4815              * Fires when the displayed month changes 
4816              * @param {DatePicker} this
4817              * @param {Date} date The selected month
4818              */
4819         'monthchange': true
4820     });
4821
4822     if(this.handler){
4823         this.on("select", this.handler,  this.scope || this);
4824     }
4825     // build the disabledDatesRE
4826     if(!this.disabledDatesRE && this.disabledDates){
4827         var dd = this.disabledDates;
4828         var re = "(?:";
4829         for(var i = 0; i < dd.length; i++){
4830             re += dd[i];
4831             if(i != dd.length-1) {
4832                 re += "|";
4833             }
4834         }
4835         this.disabledDatesRE = new RegExp(re + ")");
4836     }
4837 };
4838
4839 Roo.extend(Roo.DatePicker, Roo.Component, {
4840     /**
4841      * @cfg {String} todayText
4842      * The text to display on the button that selects the current date (defaults to "Today")
4843      */
4844     todayText : "Today",
4845     /**
4846      * @cfg {String} okText
4847      * The text to display on the ok button
4848      */
4849     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4850     /**
4851      * @cfg {String} cancelText
4852      * The text to display on the cancel button
4853      */
4854     cancelText : "Cancel",
4855     /**
4856      * @cfg {String} todayTip
4857      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4858      */
4859     todayTip : "{0} (Spacebar)",
4860     /**
4861      * @cfg {Date} minDate
4862      * Minimum allowable date (JavaScript date object, defaults to null)
4863      */
4864     minDate : null,
4865     /**
4866      * @cfg {Date} maxDate
4867      * Maximum allowable date (JavaScript date object, defaults to null)
4868      */
4869     maxDate : null,
4870     /**
4871      * @cfg {String} minText
4872      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4873      */
4874     minText : "This date is before the minimum date",
4875     /**
4876      * @cfg {String} maxText
4877      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4878      */
4879     maxText : "This date is after the maximum date",
4880     /**
4881      * @cfg {String} format
4882      * The default date format string which can be overriden for localization support.  The format must be
4883      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4884      */
4885     format : "m/d/y",
4886     /**
4887      * @cfg {Array} disabledDays
4888      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4889      */
4890     disabledDays : null,
4891     /**
4892      * @cfg {String} disabledDaysText
4893      * The tooltip to display when the date falls on a disabled day (defaults to "")
4894      */
4895     disabledDaysText : "",
4896     /**
4897      * @cfg {RegExp} disabledDatesRE
4898      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4899      */
4900     disabledDatesRE : null,
4901     /**
4902      * @cfg {String} disabledDatesText
4903      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4904      */
4905     disabledDatesText : "",
4906     /**
4907      * @cfg {Boolean} constrainToViewport
4908      * True to constrain the date picker to the viewport (defaults to true)
4909      */
4910     constrainToViewport : true,
4911     /**
4912      * @cfg {Array} monthNames
4913      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4914      */
4915     monthNames : Date.monthNames,
4916     /**
4917      * @cfg {Array} dayNames
4918      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4919      */
4920     dayNames : Date.dayNames,
4921     /**
4922      * @cfg {String} nextText
4923      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4924      */
4925     nextText: 'Next Month (Control+Right)',
4926     /**
4927      * @cfg {String} prevText
4928      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4929      */
4930     prevText: 'Previous Month (Control+Left)',
4931     /**
4932      * @cfg {String} monthYearText
4933      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4934      */
4935     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4936     /**
4937      * @cfg {Number} startDay
4938      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4939      */
4940     startDay : 0,
4941     /**
4942      * @cfg {Bool} showClear
4943      * Show a clear button (usefull for date form elements that can be blank.)
4944      */
4945     
4946     showClear: false,
4947     
4948     /**
4949      * Sets the value of the date field
4950      * @param {Date} value The date to set
4951      */
4952     setValue : function(value){
4953         var old = this.value;
4954         
4955         if (typeof(value) == 'string') {
4956          
4957             value = Date.parseDate(value, this.format);
4958         }
4959         if (!value) {
4960             value = new Date();
4961         }
4962         
4963         this.value = value.clearTime(true);
4964         if(this.el){
4965             this.update(this.value);
4966         }
4967     },
4968
4969     /**
4970      * Gets the current selected value of the date field
4971      * @return {Date} The selected date
4972      */
4973     getValue : function(){
4974         return this.value;
4975     },
4976
4977     // private
4978     focus : function(){
4979         if(this.el){
4980             this.update(this.activeDate);
4981         }
4982     },
4983
4984     // privateval
4985     onRender : function(container, position){
4986         
4987         var m = [
4988              '<table cellspacing="0">',
4989                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
4990                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4991         var dn = this.dayNames;
4992         for(var i = 0; i < 7; i++){
4993             var d = this.startDay+i;
4994             if(d > 6){
4995                 d = d-7;
4996             }
4997             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4998         }
4999         m[m.length] = "</tr></thead><tbody><tr>";
5000         for(var i = 0; i < 42; i++) {
5001             if(i % 7 == 0 && i != 0){
5002                 m[m.length] = "</tr><tr>";
5003             }
5004             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5005         }
5006         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5007             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5008
5009         var el = document.createElement("div");
5010         el.className = "x-date-picker";
5011         el.innerHTML = m.join("");
5012
5013         container.dom.insertBefore(el, position);
5014
5015         this.el = Roo.get(el);
5016         this.eventEl = Roo.get(el.firstChild);
5017
5018         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5019             handler: this.showPrevMonth,
5020             scope: this,
5021             preventDefault:true,
5022             stopDefault:true
5023         });
5024
5025         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5026             handler: this.showNextMonth,
5027             scope: this,
5028             preventDefault:true,
5029             stopDefault:true
5030         });
5031
5032         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5033
5034         this.monthPicker = this.el.down('div.x-date-mp');
5035         this.monthPicker.enableDisplayMode('block');
5036         
5037         var kn = new Roo.KeyNav(this.eventEl, {
5038             "left" : function(e){
5039                 e.ctrlKey ?
5040                     this.showPrevMonth() :
5041                     this.update(this.activeDate.add("d", -1));
5042             },
5043
5044             "right" : function(e){
5045                 e.ctrlKey ?
5046                     this.showNextMonth() :
5047                     this.update(this.activeDate.add("d", 1));
5048             },
5049
5050             "up" : function(e){
5051                 e.ctrlKey ?
5052                     this.showNextYear() :
5053                     this.update(this.activeDate.add("d", -7));
5054             },
5055
5056             "down" : function(e){
5057                 e.ctrlKey ?
5058                     this.showPrevYear() :
5059                     this.update(this.activeDate.add("d", 7));
5060             },
5061
5062             "pageUp" : function(e){
5063                 this.showNextMonth();
5064             },
5065
5066             "pageDown" : function(e){
5067                 this.showPrevMonth();
5068             },
5069
5070             "enter" : function(e){
5071                 e.stopPropagation();
5072                 return true;
5073             },
5074
5075             scope : this
5076         });
5077
5078         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5079
5080         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5081
5082         this.el.unselectable();
5083         
5084         this.cells = this.el.select("table.x-date-inner tbody td");
5085         this.textNodes = this.el.query("table.x-date-inner tbody span");
5086
5087         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5088             text: "&#160;",
5089             tooltip: this.monthYearText
5090         });
5091
5092         this.mbtn.on('click', this.showMonthPicker, this);
5093         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5094
5095
5096         var today = (new Date()).dateFormat(this.format);
5097         
5098         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5099         if (this.showClear) {
5100             baseTb.add( new Roo.Toolbar.Fill());
5101         }
5102         baseTb.add({
5103             text: String.format(this.todayText, today),
5104             tooltip: String.format(this.todayTip, today),
5105             handler: this.selectToday,
5106             scope: this
5107         });
5108         
5109         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5110             
5111         //});
5112         if (this.showClear) {
5113             
5114             baseTb.add( new Roo.Toolbar.Fill());
5115             baseTb.add({
5116                 text: '&#160;',
5117                 cls: 'x-btn-icon x-btn-clear',
5118                 handler: function() {
5119                     //this.value = '';
5120                     this.fireEvent("select", this, '');
5121                 },
5122                 scope: this
5123             });
5124         }
5125         
5126         
5127         if(Roo.isIE){
5128             this.el.repaint();
5129         }
5130         this.update(this.value);
5131     },
5132
5133     createMonthPicker : function(){
5134         if(!this.monthPicker.dom.firstChild){
5135             var buf = ['<table border="0" cellspacing="0">'];
5136             for(var i = 0; i < 6; i++){
5137                 buf.push(
5138                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5139                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5140                     i == 0 ?
5141                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5142                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5143                 );
5144             }
5145             buf.push(
5146                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5147                     this.okText,
5148                     '</button><button type="button" class="x-date-mp-cancel">',
5149                     this.cancelText,
5150                     '</button></td></tr>',
5151                 '</table>'
5152             );
5153             this.monthPicker.update(buf.join(''));
5154             this.monthPicker.on('click', this.onMonthClick, this);
5155             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5156
5157             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5158             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5159
5160             this.mpMonths.each(function(m, a, i){
5161                 i += 1;
5162                 if((i%2) == 0){
5163                     m.dom.xmonth = 5 + Math.round(i * .5);
5164                 }else{
5165                     m.dom.xmonth = Math.round((i-1) * .5);
5166                 }
5167             });
5168         }
5169     },
5170
5171     showMonthPicker : function(){
5172         this.createMonthPicker();
5173         var size = this.el.getSize();
5174         this.monthPicker.setSize(size);
5175         this.monthPicker.child('table').setSize(size);
5176
5177         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5178         this.updateMPMonth(this.mpSelMonth);
5179         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5180         this.updateMPYear(this.mpSelYear);
5181
5182         this.monthPicker.slideIn('t', {duration:.2});
5183     },
5184
5185     updateMPYear : function(y){
5186         this.mpyear = y;
5187         var ys = this.mpYears.elements;
5188         for(var i = 1; i <= 10; i++){
5189             var td = ys[i-1], y2;
5190             if((i%2) == 0){
5191                 y2 = y + Math.round(i * .5);
5192                 td.firstChild.innerHTML = y2;
5193                 td.xyear = y2;
5194             }else{
5195                 y2 = y - (5-Math.round(i * .5));
5196                 td.firstChild.innerHTML = y2;
5197                 td.xyear = y2;
5198             }
5199             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5200         }
5201     },
5202
5203     updateMPMonth : function(sm){
5204         this.mpMonths.each(function(m, a, i){
5205             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5206         });
5207     },
5208
5209     selectMPMonth: function(m){
5210         
5211     },
5212
5213     onMonthClick : function(e, t){
5214         e.stopEvent();
5215         var el = new Roo.Element(t), pn;
5216         if(el.is('button.x-date-mp-cancel')){
5217             this.hideMonthPicker();
5218         }
5219         else if(el.is('button.x-date-mp-ok')){
5220             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5221             this.hideMonthPicker();
5222         }
5223         else if(pn = el.up('td.x-date-mp-month', 2)){
5224             this.mpMonths.removeClass('x-date-mp-sel');
5225             pn.addClass('x-date-mp-sel');
5226             this.mpSelMonth = pn.dom.xmonth;
5227         }
5228         else if(pn = el.up('td.x-date-mp-year', 2)){
5229             this.mpYears.removeClass('x-date-mp-sel');
5230             pn.addClass('x-date-mp-sel');
5231             this.mpSelYear = pn.dom.xyear;
5232         }
5233         else if(el.is('a.x-date-mp-prev')){
5234             this.updateMPYear(this.mpyear-10);
5235         }
5236         else if(el.is('a.x-date-mp-next')){
5237             this.updateMPYear(this.mpyear+10);
5238         }
5239     },
5240
5241     onMonthDblClick : function(e, t){
5242         e.stopEvent();
5243         var el = new Roo.Element(t), pn;
5244         if(pn = el.up('td.x-date-mp-month', 2)){
5245             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5246             this.hideMonthPicker();
5247         }
5248         else if(pn = el.up('td.x-date-mp-year', 2)){
5249             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5250             this.hideMonthPicker();
5251         }
5252     },
5253
5254     hideMonthPicker : function(disableAnim){
5255         if(this.monthPicker){
5256             if(disableAnim === true){
5257                 this.monthPicker.hide();
5258             }else{
5259                 this.monthPicker.slideOut('t', {duration:.2});
5260             }
5261         }
5262     },
5263
5264     // private
5265     showPrevMonth : function(e){
5266         this.update(this.activeDate.add("mo", -1));
5267     },
5268
5269     // private
5270     showNextMonth : function(e){
5271         this.update(this.activeDate.add("mo", 1));
5272     },
5273
5274     // private
5275     showPrevYear : function(){
5276         this.update(this.activeDate.add("y", -1));
5277     },
5278
5279     // private
5280     showNextYear : function(){
5281         this.update(this.activeDate.add("y", 1));
5282     },
5283
5284     // private
5285     handleMouseWheel : function(e){
5286         var delta = e.getWheelDelta();
5287         if(delta > 0){
5288             this.showPrevMonth();
5289             e.stopEvent();
5290         } else if(delta < 0){
5291             this.showNextMonth();
5292             e.stopEvent();
5293         }
5294     },
5295
5296     // private
5297     handleDateClick : function(e, t){
5298         e.stopEvent();
5299         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5300             this.setValue(new Date(t.dateValue));
5301             this.fireEvent("select", this, this.value);
5302         }
5303     },
5304
5305     // private
5306     selectToday : function(){
5307         this.setValue(new Date().clearTime());
5308         this.fireEvent("select", this, this.value);
5309     },
5310
5311     // private
5312     update : function(date)
5313     {
5314         var vd = this.activeDate;
5315         this.activeDate = date;
5316         if(vd && this.el){
5317             var t = date.getTime();
5318             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5319                 this.cells.removeClass("x-date-selected");
5320                 this.cells.each(function(c){
5321                    if(c.dom.firstChild.dateValue == t){
5322                        c.addClass("x-date-selected");
5323                        setTimeout(function(){
5324                             try{c.dom.firstChild.focus();}catch(e){}
5325                        }, 50);
5326                        return false;
5327                    }
5328                 });
5329                 return;
5330             }
5331         }
5332         
5333         var days = date.getDaysInMonth();
5334         var firstOfMonth = date.getFirstDateOfMonth();
5335         var startingPos = firstOfMonth.getDay()-this.startDay;
5336
5337         if(startingPos <= this.startDay){
5338             startingPos += 7;
5339         }
5340
5341         var pm = date.add("mo", -1);
5342         var prevStart = pm.getDaysInMonth()-startingPos;
5343
5344         var cells = this.cells.elements;
5345         var textEls = this.textNodes;
5346         days += startingPos;
5347
5348         // convert everything to numbers so it's fast
5349         var day = 86400000;
5350         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5351         var today = new Date().clearTime().getTime();
5352         var sel = date.clearTime().getTime();
5353         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5354         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5355         var ddMatch = this.disabledDatesRE;
5356         var ddText = this.disabledDatesText;
5357         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5358         var ddaysText = this.disabledDaysText;
5359         var format = this.format;
5360
5361         var setCellClass = function(cal, cell){
5362             cell.title = "";
5363             var t = d.getTime();
5364             cell.firstChild.dateValue = t;
5365             if(t == today){
5366                 cell.className += " x-date-today";
5367                 cell.title = cal.todayText;
5368             }
5369             if(t == sel){
5370                 cell.className += " x-date-selected";
5371                 setTimeout(function(){
5372                     try{cell.firstChild.focus();}catch(e){}
5373                 }, 50);
5374             }
5375             // disabling
5376             if(t < min) {
5377                 cell.className = " x-date-disabled";
5378                 cell.title = cal.minText;
5379                 return;
5380             }
5381             if(t > max) {
5382                 cell.className = " x-date-disabled";
5383                 cell.title = cal.maxText;
5384                 return;
5385             }
5386             if(ddays){
5387                 if(ddays.indexOf(d.getDay()) != -1){
5388                     cell.title = ddaysText;
5389                     cell.className = " x-date-disabled";
5390                 }
5391             }
5392             if(ddMatch && format){
5393                 var fvalue = d.dateFormat(format);
5394                 if(ddMatch.test(fvalue)){
5395                     cell.title = ddText.replace("%0", fvalue);
5396                     cell.className = " x-date-disabled";
5397                 }
5398             }
5399         };
5400
5401         var i = 0;
5402         for(; i < startingPos; i++) {
5403             textEls[i].innerHTML = (++prevStart);
5404             d.setDate(d.getDate()+1);
5405             cells[i].className = "x-date-prevday";
5406             setCellClass(this, cells[i]);
5407         }
5408         for(; i < days; i++){
5409             intDay = i - startingPos + 1;
5410             textEls[i].innerHTML = (intDay);
5411             d.setDate(d.getDate()+1);
5412             cells[i].className = "x-date-active";
5413             setCellClass(this, cells[i]);
5414         }
5415         var extraDays = 0;
5416         for(; i < 42; i++) {
5417              textEls[i].innerHTML = (++extraDays);
5418              d.setDate(d.getDate()+1);
5419              cells[i].className = "x-date-nextday";
5420              setCellClass(this, cells[i]);
5421         }
5422
5423         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5424         this.fireEvent('monthchange', this, date);
5425         
5426         if(!this.internalRender){
5427             var main = this.el.dom.firstChild;
5428             var w = main.offsetWidth;
5429             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5430             Roo.fly(main).setWidth(w);
5431             this.internalRender = true;
5432             // opera does not respect the auto grow header center column
5433             // then, after it gets a width opera refuses to recalculate
5434             // without a second pass
5435             if(Roo.isOpera && !this.secondPass){
5436                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5437                 this.secondPass = true;
5438                 this.update.defer(10, this, [date]);
5439             }
5440         }
5441         
5442         
5443     }
5444 });        /*
5445  * Based on:
5446  * Ext JS Library 1.1.1
5447  * Copyright(c) 2006-2007, Ext JS, LLC.
5448  *
5449  * Originally Released Under LGPL - original licence link has changed is not relivant.
5450  *
5451  * Fork - LGPL
5452  * <script type="text/javascript">
5453  */
5454 /**
5455  * @class Roo.TabPanel
5456  * @extends Roo.util.Observable
5457  * A lightweight tab container.
5458  * <br><br>
5459  * Usage:
5460  * <pre><code>
5461 // basic tabs 1, built from existing content
5462 var tabs = new Roo.TabPanel("tabs1");
5463 tabs.addTab("script", "View Script");
5464 tabs.addTab("markup", "View Markup");
5465 tabs.activate("script");
5466
5467 // more advanced tabs, built from javascript
5468 var jtabs = new Roo.TabPanel("jtabs");
5469 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5470
5471 // set up the UpdateManager
5472 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5473 var updater = tab2.getUpdateManager();
5474 updater.setDefaultUrl("ajax1.htm");
5475 tab2.on('activate', updater.refresh, updater, true);
5476
5477 // Use setUrl for Ajax loading
5478 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5479 tab3.setUrl("ajax2.htm", null, true);
5480
5481 // Disabled tab
5482 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5483 tab4.disable();
5484
5485 jtabs.activate("jtabs-1");
5486  * </code></pre>
5487  * @constructor
5488  * Create a new TabPanel.
5489  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5490  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5491  */
5492 Roo.TabPanel = function(container, config){
5493     /**
5494     * The container element for this TabPanel.
5495     * @type Roo.Element
5496     */
5497     this.el = Roo.get(container, true);
5498     if(config){
5499         if(typeof config == "boolean"){
5500             this.tabPosition = config ? "bottom" : "top";
5501         }else{
5502             Roo.apply(this, config);
5503         }
5504     }
5505     if(this.tabPosition == "bottom"){
5506         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5507         this.el.addClass("x-tabs-bottom");
5508     }
5509     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5510     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5511     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5512     if(Roo.isIE){
5513         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5514     }
5515     if(this.tabPosition != "bottom"){
5516         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5517          * @type Roo.Element
5518          */
5519         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5520         this.el.addClass("x-tabs-top");
5521     }
5522     this.items = [];
5523
5524     this.bodyEl.setStyle("position", "relative");
5525
5526     this.active = null;
5527     this.activateDelegate = this.activate.createDelegate(this);
5528
5529     this.addEvents({
5530         /**
5531          * @event tabchange
5532          * Fires when the active tab changes
5533          * @param {Roo.TabPanel} this
5534          * @param {Roo.TabPanelItem} activePanel The new active tab
5535          */
5536         "tabchange": true,
5537         /**
5538          * @event beforetabchange
5539          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5540          * @param {Roo.TabPanel} this
5541          * @param {Object} e Set cancel to true on this object to cancel the tab change
5542          * @param {Roo.TabPanelItem} tab The tab being changed to
5543          */
5544         "beforetabchange" : true
5545     });
5546
5547     Roo.EventManager.onWindowResize(this.onResize, this);
5548     this.cpad = this.el.getPadding("lr");
5549     this.hiddenCount = 0;
5550
5551
5552     // toolbar on the tabbar support...
5553     if (this.toolbar) {
5554         var tcfg = this.toolbar;
5555         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5556         this.toolbar = new Roo.Toolbar(tcfg);
5557         if (Roo.isSafari) {
5558             var tbl = tcfg.container.child('table', true);
5559             tbl.setAttribute('width', '100%');
5560         }
5561         
5562     }
5563    
5564
5565
5566     Roo.TabPanel.superclass.constructor.call(this);
5567 };
5568
5569 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5570     /*
5571      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5572      */
5573     tabPosition : "top",
5574     /*
5575      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5576      */
5577     currentTabWidth : 0,
5578     /*
5579      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5580      */
5581     minTabWidth : 40,
5582     /*
5583      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5584      */
5585     maxTabWidth : 250,
5586     /*
5587      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5588      */
5589     preferredTabWidth : 175,
5590     /*
5591      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5592      */
5593     resizeTabs : false,
5594     /*
5595      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5596      */
5597     monitorResize : true,
5598     /*
5599      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5600      */
5601     toolbar : false,
5602
5603     /**
5604      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5605      * @param {String} id The id of the div to use <b>or create</b>
5606      * @param {String} text The text for the tab
5607      * @param {String} content (optional) Content to put in the TabPanelItem body
5608      * @param {Boolean} closable (optional) True to create a close icon on the tab
5609      * @return {Roo.TabPanelItem} The created TabPanelItem
5610      */
5611     addTab : function(id, text, content, closable){
5612         var item = new Roo.TabPanelItem(this, id, text, closable);
5613         this.addTabItem(item);
5614         if(content){
5615             item.setContent(content);
5616         }
5617         return item;
5618     },
5619
5620     /**
5621      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5622      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5623      * @return {Roo.TabPanelItem}
5624      */
5625     getTab : function(id){
5626         return this.items[id];
5627     },
5628
5629     /**
5630      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5631      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5632      */
5633     hideTab : function(id){
5634         var t = this.items[id];
5635         if(!t.isHidden()){
5636            t.setHidden(true);
5637            this.hiddenCount++;
5638            this.autoSizeTabs();
5639         }
5640     },
5641
5642     /**
5643      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5644      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5645      */
5646     unhideTab : function(id){
5647         var t = this.items[id];
5648         if(t.isHidden()){
5649            t.setHidden(false);
5650            this.hiddenCount--;
5651            this.autoSizeTabs();
5652         }
5653     },
5654
5655     /**
5656      * Adds an existing {@link Roo.TabPanelItem}.
5657      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5658      */
5659     addTabItem : function(item){
5660         this.items[item.id] = item;
5661         this.items.push(item);
5662         if(this.resizeTabs){
5663            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5664            this.autoSizeTabs();
5665         }else{
5666             item.autoSize();
5667         }
5668     },
5669
5670     /**
5671      * Removes a {@link Roo.TabPanelItem}.
5672      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5673      */
5674     removeTab : function(id){
5675         var items = this.items;
5676         var tab = items[id];
5677         if(!tab) { return; }
5678         var index = items.indexOf(tab);
5679         if(this.active == tab && items.length > 1){
5680             var newTab = this.getNextAvailable(index);
5681             if(newTab) {
5682                 newTab.activate();
5683             }
5684         }
5685         this.stripEl.dom.removeChild(tab.pnode.dom);
5686         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5687             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5688         }
5689         items.splice(index, 1);
5690         delete this.items[tab.id];
5691         tab.fireEvent("close", tab);
5692         tab.purgeListeners();
5693         this.autoSizeTabs();
5694     },
5695
5696     getNextAvailable : function(start){
5697         var items = this.items;
5698         var index = start;
5699         // look for a next tab that will slide over to
5700         // replace the one being removed
5701         while(index < items.length){
5702             var item = items[++index];
5703             if(item && !item.isHidden()){
5704                 return item;
5705             }
5706         }
5707         // if one isn't found select the previous tab (on the left)
5708         index = start;
5709         while(index >= 0){
5710             var item = items[--index];
5711             if(item && !item.isHidden()){
5712                 return item;
5713             }
5714         }
5715         return null;
5716     },
5717
5718     /**
5719      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5720      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5721      */
5722     disableTab : function(id){
5723         var tab = this.items[id];
5724         if(tab && this.active != tab){
5725             tab.disable();
5726         }
5727     },
5728
5729     /**
5730      * Enables a {@link Roo.TabPanelItem} that is disabled.
5731      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5732      */
5733     enableTab : function(id){
5734         var tab = this.items[id];
5735         tab.enable();
5736     },
5737
5738     /**
5739      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5740      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5741      * @return {Roo.TabPanelItem} The TabPanelItem.
5742      */
5743     activate : function(id){
5744         var tab = this.items[id];
5745         if(!tab){
5746             return null;
5747         }
5748         if(tab == this.active || tab.disabled){
5749             return tab;
5750         }
5751         var e = {};
5752         this.fireEvent("beforetabchange", this, e, tab);
5753         if(e.cancel !== true && !tab.disabled){
5754             if(this.active){
5755                 this.active.hide();
5756             }
5757             this.active = this.items[id];
5758             this.active.show();
5759             this.fireEvent("tabchange", this, this.active);
5760         }
5761         return tab;
5762     },
5763
5764     /**
5765      * Gets the active {@link Roo.TabPanelItem}.
5766      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5767      */
5768     getActiveTab : function(){
5769         return this.active;
5770     },
5771
5772     /**
5773      * Updates the tab body element to fit the height of the container element
5774      * for overflow scrolling
5775      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5776      */
5777     syncHeight : function(targetHeight){
5778         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5779         var bm = this.bodyEl.getMargins();
5780         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5781         this.bodyEl.setHeight(newHeight);
5782         return newHeight;
5783     },
5784
5785     onResize : function(){
5786         if(this.monitorResize){
5787             this.autoSizeTabs();
5788         }
5789     },
5790
5791     /**
5792      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5793      */
5794     beginUpdate : function(){
5795         this.updating = true;
5796     },
5797
5798     /**
5799      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5800      */
5801     endUpdate : function(){
5802         this.updating = false;
5803         this.autoSizeTabs();
5804     },
5805
5806     /**
5807      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5808      */
5809     autoSizeTabs : function(){
5810         var count = this.items.length;
5811         var vcount = count - this.hiddenCount;
5812         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5813             return;
5814         }
5815         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5816         var availWidth = Math.floor(w / vcount);
5817         var b = this.stripBody;
5818         if(b.getWidth() > w){
5819             var tabs = this.items;
5820             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5821             if(availWidth < this.minTabWidth){
5822                 /*if(!this.sleft){    // incomplete scrolling code
5823                     this.createScrollButtons();
5824                 }
5825                 this.showScroll();
5826                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5827             }
5828         }else{
5829             if(this.currentTabWidth < this.preferredTabWidth){
5830                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5831             }
5832         }
5833     },
5834
5835     /**
5836      * Returns the number of tabs in this TabPanel.
5837      * @return {Number}
5838      */
5839      getCount : function(){
5840          return this.items.length;
5841      },
5842
5843     /**
5844      * Resizes all the tabs to the passed width
5845      * @param {Number} The new width
5846      */
5847     setTabWidth : function(width){
5848         this.currentTabWidth = width;
5849         for(var i = 0, len = this.items.length; i < len; i++) {
5850                 if(!this.items[i].isHidden()) {
5851                 this.items[i].setWidth(width);
5852             }
5853         }
5854     },
5855
5856     /**
5857      * Destroys this TabPanel
5858      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5859      */
5860     destroy : function(removeEl){
5861         Roo.EventManager.removeResizeListener(this.onResize, this);
5862         for(var i = 0, len = this.items.length; i < len; i++){
5863             this.items[i].purgeListeners();
5864         }
5865         if(removeEl === true){
5866             this.el.update("");
5867             this.el.remove();
5868         }
5869     }
5870 });
5871
5872 /**
5873  * @class Roo.TabPanelItem
5874  * @extends Roo.util.Observable
5875  * Represents an individual item (tab plus body) in a TabPanel.
5876  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5877  * @param {String} id The id of this TabPanelItem
5878  * @param {String} text The text for the tab of this TabPanelItem
5879  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5880  */
5881 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5882     /**
5883      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5884      * @type Roo.TabPanel
5885      */
5886     this.tabPanel = tabPanel;
5887     /**
5888      * The id for this TabPanelItem
5889      * @type String
5890      */
5891     this.id = id;
5892     /** @private */
5893     this.disabled = false;
5894     /** @private */
5895     this.text = text;
5896     /** @private */
5897     this.loaded = false;
5898     this.closable = closable;
5899
5900     /**
5901      * The body element for this TabPanelItem.
5902      * @type Roo.Element
5903      */
5904     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5905     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5906     this.bodyEl.setStyle("display", "block");
5907     this.bodyEl.setStyle("zoom", "1");
5908     this.hideAction();
5909
5910     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5911     /** @private */
5912     this.el = Roo.get(els.el, true);
5913     this.inner = Roo.get(els.inner, true);
5914     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5915     this.pnode = Roo.get(els.el.parentNode, true);
5916     this.el.on("mousedown", this.onTabMouseDown, this);
5917     this.el.on("click", this.onTabClick, this);
5918     /** @private */
5919     if(closable){
5920         var c = Roo.get(els.close, true);
5921         c.dom.title = this.closeText;
5922         c.addClassOnOver("close-over");
5923         c.on("click", this.closeClick, this);
5924      }
5925
5926     this.addEvents({
5927          /**
5928          * @event activate
5929          * Fires when this tab becomes the active tab.
5930          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5931          * @param {Roo.TabPanelItem} this
5932          */
5933         "activate": true,
5934         /**
5935          * @event beforeclose
5936          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5937          * @param {Roo.TabPanelItem} this
5938          * @param {Object} e Set cancel to true on this object to cancel the close.
5939          */
5940         "beforeclose": true,
5941         /**
5942          * @event close
5943          * Fires when this tab is closed.
5944          * @param {Roo.TabPanelItem} this
5945          */
5946          "close": true,
5947         /**
5948          * @event deactivate
5949          * Fires when this tab is no longer the active tab.
5950          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5951          * @param {Roo.TabPanelItem} this
5952          */
5953          "deactivate" : true
5954     });
5955     this.hidden = false;
5956
5957     Roo.TabPanelItem.superclass.constructor.call(this);
5958 };
5959
5960 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5961     purgeListeners : function(){
5962        Roo.util.Observable.prototype.purgeListeners.call(this);
5963        this.el.removeAllListeners();
5964     },
5965     /**
5966      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5967      */
5968     show : function(){
5969         this.pnode.addClass("on");
5970         this.showAction();
5971         if(Roo.isOpera){
5972             this.tabPanel.stripWrap.repaint();
5973         }
5974         this.fireEvent("activate", this.tabPanel, this);
5975     },
5976
5977     /**
5978      * Returns true if this tab is the active tab.
5979      * @return {Boolean}
5980      */
5981     isActive : function(){
5982         return this.tabPanel.getActiveTab() == this;
5983     },
5984
5985     /**
5986      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5987      */
5988     hide : function(){
5989         this.pnode.removeClass("on");
5990         this.hideAction();
5991         this.fireEvent("deactivate", this.tabPanel, this);
5992     },
5993
5994     hideAction : function(){
5995         this.bodyEl.hide();
5996         this.bodyEl.setStyle("position", "absolute");
5997         this.bodyEl.setLeft("-20000px");
5998         this.bodyEl.setTop("-20000px");
5999     },
6000
6001     showAction : function(){
6002         this.bodyEl.setStyle("position", "relative");
6003         this.bodyEl.setTop("");
6004         this.bodyEl.setLeft("");
6005         this.bodyEl.show();
6006     },
6007
6008     /**
6009      * Set the tooltip for the tab.
6010      * @param {String} tooltip The tab's tooltip
6011      */
6012     setTooltip : function(text){
6013         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6014             this.textEl.dom.qtip = text;
6015             this.textEl.dom.removeAttribute('title');
6016         }else{
6017             this.textEl.dom.title = text;
6018         }
6019     },
6020
6021     onTabClick : function(e){
6022         e.preventDefault();
6023         this.tabPanel.activate(this.id);
6024     },
6025
6026     onTabMouseDown : function(e){
6027         e.preventDefault();
6028         this.tabPanel.activate(this.id);
6029     },
6030
6031     getWidth : function(){
6032         return this.inner.getWidth();
6033     },
6034
6035     setWidth : function(width){
6036         var iwidth = width - this.pnode.getPadding("lr");
6037         this.inner.setWidth(iwidth);
6038         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6039         this.pnode.setWidth(width);
6040     },
6041
6042     /**
6043      * Show or hide the tab
6044      * @param {Boolean} hidden True to hide or false to show.
6045      */
6046     setHidden : function(hidden){
6047         this.hidden = hidden;
6048         this.pnode.setStyle("display", hidden ? "none" : "");
6049     },
6050
6051     /**
6052      * Returns true if this tab is "hidden"
6053      * @return {Boolean}
6054      */
6055     isHidden : function(){
6056         return this.hidden;
6057     },
6058
6059     /**
6060      * Returns the text for this tab
6061      * @return {String}
6062      */
6063     getText : function(){
6064         return this.text;
6065     },
6066
6067     autoSize : function(){
6068         //this.el.beginMeasure();
6069         this.textEl.setWidth(1);
6070         /*
6071          *  #2804 [new] Tabs in Roojs
6072          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6073          */
6074         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6075         //this.el.endMeasure();
6076     },
6077
6078     /**
6079      * Sets the text for the tab (Note: this also sets the tooltip text)
6080      * @param {String} text The tab's text and tooltip
6081      */
6082     setText : function(text){
6083         this.text = text;
6084         this.textEl.update(text);
6085         this.setTooltip(text);
6086         if(!this.tabPanel.resizeTabs){
6087             this.autoSize();
6088         }
6089     },
6090     /**
6091      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6092      */
6093     activate : function(){
6094         this.tabPanel.activate(this.id);
6095     },
6096
6097     /**
6098      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6099      */
6100     disable : function(){
6101         if(this.tabPanel.active != this){
6102             this.disabled = true;
6103             this.pnode.addClass("disabled");
6104         }
6105     },
6106
6107     /**
6108      * Enables this TabPanelItem if it was previously disabled.
6109      */
6110     enable : function(){
6111         this.disabled = false;
6112         this.pnode.removeClass("disabled");
6113     },
6114
6115     /**
6116      * Sets the content for this TabPanelItem.
6117      * @param {String} content The content
6118      * @param {Boolean} loadScripts true to look for and load scripts
6119      */
6120     setContent : function(content, loadScripts){
6121         this.bodyEl.update(content, loadScripts);
6122     },
6123
6124     /**
6125      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6126      * @return {Roo.UpdateManager} The UpdateManager
6127      */
6128     getUpdateManager : function(){
6129         return this.bodyEl.getUpdateManager();
6130     },
6131
6132     /**
6133      * Set a URL to be used to load the content for this TabPanelItem.
6134      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6135      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6136      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6137      * @return {Roo.UpdateManager} The UpdateManager
6138      */
6139     setUrl : function(url, params, loadOnce){
6140         if(this.refreshDelegate){
6141             this.un('activate', this.refreshDelegate);
6142         }
6143         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6144         this.on("activate", this.refreshDelegate);
6145         return this.bodyEl.getUpdateManager();
6146     },
6147
6148     /** @private */
6149     _handleRefresh : function(url, params, loadOnce){
6150         if(!loadOnce || !this.loaded){
6151             var updater = this.bodyEl.getUpdateManager();
6152             updater.update(url, params, this._setLoaded.createDelegate(this));
6153         }
6154     },
6155
6156     /**
6157      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6158      *   Will fail silently if the setUrl method has not been called.
6159      *   This does not activate the panel, just updates its content.
6160      */
6161     refresh : function(){
6162         if(this.refreshDelegate){
6163            this.loaded = false;
6164            this.refreshDelegate();
6165         }
6166     },
6167
6168     /** @private */
6169     _setLoaded : function(){
6170         this.loaded = true;
6171     },
6172
6173     /** @private */
6174     closeClick : function(e){
6175         var o = {};
6176         e.stopEvent();
6177         this.fireEvent("beforeclose", this, o);
6178         if(o.cancel !== true){
6179             this.tabPanel.removeTab(this.id);
6180         }
6181     },
6182     /**
6183      * The text displayed in the tooltip for the close icon.
6184      * @type String
6185      */
6186     closeText : "Close this tab"
6187 });
6188
6189 /** @private */
6190 Roo.TabPanel.prototype.createStrip = function(container){
6191     var strip = document.createElement("div");
6192     strip.className = "x-tabs-wrap";
6193     container.appendChild(strip);
6194     return strip;
6195 };
6196 /** @private */
6197 Roo.TabPanel.prototype.createStripList = function(strip){
6198     // div wrapper for retard IE
6199     // returns the "tr" element.
6200     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6201         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6202         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6203     return strip.firstChild.firstChild.firstChild.firstChild;
6204 };
6205 /** @private */
6206 Roo.TabPanel.prototype.createBody = function(container){
6207     var body = document.createElement("div");
6208     Roo.id(body, "tab-body");
6209     Roo.fly(body).addClass("x-tabs-body");
6210     container.appendChild(body);
6211     return body;
6212 };
6213 /** @private */
6214 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6215     var body = Roo.getDom(id);
6216     if(!body){
6217         body = document.createElement("div");
6218         body.id = id;
6219     }
6220     Roo.fly(body).addClass("x-tabs-item-body");
6221     bodyEl.insertBefore(body, bodyEl.firstChild);
6222     return body;
6223 };
6224 /** @private */
6225 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6226     var td = document.createElement("td");
6227     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6228     //stripEl.appendChild(td);
6229     if(closable){
6230         td.className = "x-tabs-closable";
6231         if(!this.closeTpl){
6232             this.closeTpl = new Roo.Template(
6233                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6234                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6235                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6236             );
6237         }
6238         var el = this.closeTpl.overwrite(td, {"text": text});
6239         var close = el.getElementsByTagName("div")[0];
6240         var inner = el.getElementsByTagName("em")[0];
6241         return {"el": el, "close": close, "inner": inner};
6242     } else {
6243         if(!this.tabTpl){
6244             this.tabTpl = new Roo.Template(
6245                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6246                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6247             );
6248         }
6249         var el = this.tabTpl.overwrite(td, {"text": text});
6250         var inner = el.getElementsByTagName("em")[0];
6251         return {"el": el, "inner": inner};
6252     }
6253 };/*
6254  * Based on:
6255  * Ext JS Library 1.1.1
6256  * Copyright(c) 2006-2007, Ext JS, LLC.
6257  *
6258  * Originally Released Under LGPL - original licence link has changed is not relivant.
6259  *
6260  * Fork - LGPL
6261  * <script type="text/javascript">
6262  */
6263
6264 /**
6265  * @class Roo.Button
6266  * @extends Roo.util.Observable
6267  * Simple Button class
6268  * @cfg {String} text The button text
6269  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6270  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6271  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6272  * @cfg {Object} scope The scope of the handler
6273  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6274  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6275  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6276  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6277  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6278  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6279    applies if enableToggle = true)
6280  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6281  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6282   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6283  * @constructor
6284  * Create a new button
6285  * @param {Object} config The config object
6286  */
6287 Roo.Button = function(renderTo, config)
6288 {
6289     if (!config) {
6290         config = renderTo;
6291         renderTo = config.renderTo || false;
6292     }
6293     
6294     Roo.apply(this, config);
6295     this.addEvents({
6296         /**
6297              * @event click
6298              * Fires when this button is clicked
6299              * @param {Button} this
6300              * @param {EventObject} e The click event
6301              */
6302             "click" : true,
6303         /**
6304              * @event toggle
6305              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6306              * @param {Button} this
6307              * @param {Boolean} pressed
6308              */
6309             "toggle" : true,
6310         /**
6311              * @event mouseover
6312              * Fires when the mouse hovers over the button
6313              * @param {Button} this
6314              * @param {Event} e The event object
6315              */
6316         'mouseover' : true,
6317         /**
6318              * @event mouseout
6319              * Fires when the mouse exits the button
6320              * @param {Button} this
6321              * @param {Event} e The event object
6322              */
6323         'mouseout': true,
6324          /**
6325              * @event render
6326              * Fires when the button is rendered
6327              * @param {Button} this
6328              */
6329         'render': true
6330     });
6331     if(this.menu){
6332         this.menu = Roo.menu.MenuMgr.get(this.menu);
6333     }
6334     // register listeners first!!  - so render can be captured..
6335     Roo.util.Observable.call(this);
6336     if(renderTo){
6337         this.render(renderTo);
6338     }
6339     
6340   
6341 };
6342
6343 Roo.extend(Roo.Button, Roo.util.Observable, {
6344     /**
6345      * 
6346      */
6347     
6348     /**
6349      * Read-only. True if this button is hidden
6350      * @type Boolean
6351      */
6352     hidden : false,
6353     /**
6354      * Read-only. True if this button is disabled
6355      * @type Boolean
6356      */
6357     disabled : false,
6358     /**
6359      * Read-only. True if this button is pressed (only if enableToggle = true)
6360      * @type Boolean
6361      */
6362     pressed : false,
6363
6364     /**
6365      * @cfg {Number} tabIndex 
6366      * The DOM tabIndex for this button (defaults to undefined)
6367      */
6368     tabIndex : undefined,
6369
6370     /**
6371      * @cfg {Boolean} enableToggle
6372      * True to enable pressed/not pressed toggling (defaults to false)
6373      */
6374     enableToggle: false,
6375     /**
6376      * @cfg {Roo.menu.Menu} menu
6377      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6378      */
6379     menu : undefined,
6380     /**
6381      * @cfg {String} menuAlign
6382      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6383      */
6384     menuAlign : "tl-bl?",
6385
6386     /**
6387      * @cfg {String} iconCls
6388      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6389      */
6390     iconCls : undefined,
6391     /**
6392      * @cfg {String} type
6393      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6394      */
6395     type : 'button',
6396
6397     // private
6398     menuClassTarget: 'tr',
6399
6400     /**
6401      * @cfg {String} clickEvent
6402      * The type of event to map to the button's event handler (defaults to 'click')
6403      */
6404     clickEvent : 'click',
6405
6406     /**
6407      * @cfg {Boolean} handleMouseEvents
6408      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6409      */
6410     handleMouseEvents : true,
6411
6412     /**
6413      * @cfg {String} tooltipType
6414      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6415      */
6416     tooltipType : 'qtip',
6417
6418     /**
6419      * @cfg {String} cls
6420      * A CSS class to apply to the button's main element.
6421      */
6422     
6423     /**
6424      * @cfg {Roo.Template} template (Optional)
6425      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6426      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6427      * require code modifications if required elements (e.g. a button) aren't present.
6428      */
6429
6430     // private
6431     render : function(renderTo){
6432         var btn;
6433         if(this.hideParent){
6434             this.parentEl = Roo.get(renderTo);
6435         }
6436         if(!this.dhconfig){
6437             if(!this.template){
6438                 if(!Roo.Button.buttonTemplate){
6439                     // hideous table template
6440                     Roo.Button.buttonTemplate = new Roo.Template(
6441                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6442                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
6443                         "</tr></tbody></table>");
6444                 }
6445                 this.template = Roo.Button.buttonTemplate;
6446             }
6447             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6448             var btnEl = btn.child("button:first");
6449             btnEl.on('focus', this.onFocus, this);
6450             btnEl.on('blur', this.onBlur, this);
6451             if(this.cls){
6452                 btn.addClass(this.cls);
6453             }
6454             if(this.icon){
6455                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6456             }
6457             if(this.iconCls){
6458                 btnEl.addClass(this.iconCls);
6459                 if(!this.cls){
6460                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6461                 }
6462             }
6463             if(this.tabIndex !== undefined){
6464                 btnEl.dom.tabIndex = this.tabIndex;
6465             }
6466             if(this.tooltip){
6467                 if(typeof this.tooltip == 'object'){
6468                     Roo.QuickTips.tips(Roo.apply({
6469                           target: btnEl.id
6470                     }, this.tooltip));
6471                 } else {
6472                     btnEl.dom[this.tooltipType] = this.tooltip;
6473                 }
6474             }
6475         }else{
6476             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6477         }
6478         this.el = btn;
6479         if(this.id){
6480             this.el.dom.id = this.el.id = this.id;
6481         }
6482         if(this.menu){
6483             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6484             this.menu.on("show", this.onMenuShow, this);
6485             this.menu.on("hide", this.onMenuHide, this);
6486         }
6487         btn.addClass("x-btn");
6488         if(Roo.isIE && !Roo.isIE7){
6489             this.autoWidth.defer(1, this);
6490         }else{
6491             this.autoWidth();
6492         }
6493         if(this.handleMouseEvents){
6494             btn.on("mouseover", this.onMouseOver, this);
6495             btn.on("mouseout", this.onMouseOut, this);
6496             btn.on("mousedown", this.onMouseDown, this);
6497         }
6498         btn.on(this.clickEvent, this.onClick, this);
6499         //btn.on("mouseup", this.onMouseUp, this);
6500         if(this.hidden){
6501             this.hide();
6502         }
6503         if(this.disabled){
6504             this.disable();
6505         }
6506         Roo.ButtonToggleMgr.register(this);
6507         if(this.pressed){
6508             this.el.addClass("x-btn-pressed");
6509         }
6510         if(this.repeat){
6511             var repeater = new Roo.util.ClickRepeater(btn,
6512                 typeof this.repeat == "object" ? this.repeat : {}
6513             );
6514             repeater.on("click", this.onClick,  this);
6515         }
6516         
6517         this.fireEvent('render', this);
6518         
6519     },
6520     /**
6521      * Returns the button's underlying element
6522      * @return {Roo.Element} The element
6523      */
6524     getEl : function(){
6525         return this.el;  
6526     },
6527     
6528     /**
6529      * Destroys this Button and removes any listeners.
6530      */
6531     destroy : function(){
6532         Roo.ButtonToggleMgr.unregister(this);
6533         this.el.removeAllListeners();
6534         this.purgeListeners();
6535         this.el.remove();
6536     },
6537
6538     // private
6539     autoWidth : function(){
6540         if(this.el){
6541             this.el.setWidth("auto");
6542             if(Roo.isIE7 && Roo.isStrict){
6543                 var ib = this.el.child('button');
6544                 if(ib && ib.getWidth() > 20){
6545                     ib.clip();
6546                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6547                 }
6548             }
6549             if(this.minWidth){
6550                 if(this.hidden){
6551                     this.el.beginMeasure();
6552                 }
6553                 if(this.el.getWidth() < this.minWidth){
6554                     this.el.setWidth(this.minWidth);
6555                 }
6556                 if(this.hidden){
6557                     this.el.endMeasure();
6558                 }
6559             }
6560         }
6561     },
6562
6563     /**
6564      * Assigns this button's click handler
6565      * @param {Function} handler The function to call when the button is clicked
6566      * @param {Object} scope (optional) Scope for the function passed in
6567      */
6568     setHandler : function(handler, scope){
6569         this.handler = handler;
6570         this.scope = scope;  
6571     },
6572     
6573     /**
6574      * Sets this button's text
6575      * @param {String} text The button text
6576      */
6577     setText : function(text){
6578         this.text = text;
6579         if(this.el){
6580             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6581         }
6582         this.autoWidth();
6583     },
6584     
6585     /**
6586      * Gets the text for this button
6587      * @return {String} The button text
6588      */
6589     getText : function(){
6590         return this.text;  
6591     },
6592     
6593     /**
6594      * Show this button
6595      */
6596     show: function(){
6597         this.hidden = false;
6598         if(this.el){
6599             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6600         }
6601     },
6602     
6603     /**
6604      * Hide this button
6605      */
6606     hide: function(){
6607         this.hidden = true;
6608         if(this.el){
6609             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6610         }
6611     },
6612     
6613     /**
6614      * Convenience function for boolean show/hide
6615      * @param {Boolean} visible True to show, false to hide
6616      */
6617     setVisible: function(visible){
6618         if(visible) {
6619             this.show();
6620         }else{
6621             this.hide();
6622         }
6623     },
6624     
6625     /**
6626      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6627      * @param {Boolean} state (optional) Force a particular state
6628      */
6629     toggle : function(state){
6630         state = state === undefined ? !this.pressed : state;
6631         if(state != this.pressed){
6632             if(state){
6633                 this.el.addClass("x-btn-pressed");
6634                 this.pressed = true;
6635                 this.fireEvent("toggle", this, true);
6636             }else{
6637                 this.el.removeClass("x-btn-pressed");
6638                 this.pressed = false;
6639                 this.fireEvent("toggle", this, false);
6640             }
6641             if(this.toggleHandler){
6642                 this.toggleHandler.call(this.scope || this, this, state);
6643             }
6644         }
6645     },
6646     
6647     /**
6648      * Focus the button
6649      */
6650     focus : function(){
6651         this.el.child('button:first').focus();
6652     },
6653     
6654     /**
6655      * Disable this button
6656      */
6657     disable : function(){
6658         if(this.el){
6659             this.el.addClass("x-btn-disabled");
6660         }
6661         this.disabled = true;
6662     },
6663     
6664     /**
6665      * Enable this button
6666      */
6667     enable : function(){
6668         if(this.el){
6669             this.el.removeClass("x-btn-disabled");
6670         }
6671         this.disabled = false;
6672     },
6673
6674     /**
6675      * Convenience function for boolean enable/disable
6676      * @param {Boolean} enabled True to enable, false to disable
6677      */
6678     setDisabled : function(v){
6679         this[v !== true ? "enable" : "disable"]();
6680     },
6681
6682     // private
6683     onClick : function(e)
6684     {
6685         if(e){
6686             e.preventDefault();
6687         }
6688         if(e.button != 0){
6689             return;
6690         }
6691         if(!this.disabled){
6692             if(this.enableToggle){
6693                 this.toggle();
6694             }
6695             if(this.menu && !this.menu.isVisible()){
6696                 this.menu.show(this.el, this.menuAlign);
6697             }
6698             this.fireEvent("click", this, e);
6699             if(this.handler){
6700                 this.el.removeClass("x-btn-over");
6701                 this.handler.call(this.scope || this, this, e);
6702             }
6703         }
6704     },
6705     // private
6706     onMouseOver : function(e){
6707         if(!this.disabled){
6708             this.el.addClass("x-btn-over");
6709             this.fireEvent('mouseover', this, e);
6710         }
6711     },
6712     // private
6713     onMouseOut : function(e){
6714         if(!e.within(this.el,  true)){
6715             this.el.removeClass("x-btn-over");
6716             this.fireEvent('mouseout', this, e);
6717         }
6718     },
6719     // private
6720     onFocus : function(e){
6721         if(!this.disabled){
6722             this.el.addClass("x-btn-focus");
6723         }
6724     },
6725     // private
6726     onBlur : function(e){
6727         this.el.removeClass("x-btn-focus");
6728     },
6729     // private
6730     onMouseDown : function(e){
6731         if(!this.disabled && e.button == 0){
6732             this.el.addClass("x-btn-click");
6733             Roo.get(document).on('mouseup', this.onMouseUp, this);
6734         }
6735     },
6736     // private
6737     onMouseUp : function(e){
6738         if(e.button == 0){
6739             this.el.removeClass("x-btn-click");
6740             Roo.get(document).un('mouseup', this.onMouseUp, this);
6741         }
6742     },
6743     // private
6744     onMenuShow : function(e){
6745         this.el.addClass("x-btn-menu-active");
6746     },
6747     // private
6748     onMenuHide : function(e){
6749         this.el.removeClass("x-btn-menu-active");
6750     }   
6751 });
6752
6753 // Private utility class used by Button
6754 Roo.ButtonToggleMgr = function(){
6755    var groups = {};
6756    
6757    function toggleGroup(btn, state){
6758        if(state){
6759            var g = groups[btn.toggleGroup];
6760            for(var i = 0, l = g.length; i < l; i++){
6761                if(g[i] != btn){
6762                    g[i].toggle(false);
6763                }
6764            }
6765        }
6766    }
6767    
6768    return {
6769        register : function(btn){
6770            if(!btn.toggleGroup){
6771                return;
6772            }
6773            var g = groups[btn.toggleGroup];
6774            if(!g){
6775                g = groups[btn.toggleGroup] = [];
6776            }
6777            g.push(btn);
6778            btn.on("toggle", toggleGroup);
6779        },
6780        
6781        unregister : function(btn){
6782            if(!btn.toggleGroup){
6783                return;
6784            }
6785            var g = groups[btn.toggleGroup];
6786            if(g){
6787                g.remove(btn);
6788                btn.un("toggle", toggleGroup);
6789            }
6790        }
6791    };
6792 }();/*
6793  * Based on:
6794  * Ext JS Library 1.1.1
6795  * Copyright(c) 2006-2007, Ext JS, LLC.
6796  *
6797  * Originally Released Under LGPL - original licence link has changed is not relivant.
6798  *
6799  * Fork - LGPL
6800  * <script type="text/javascript">
6801  */
6802  
6803 /**
6804  * @class Roo.SplitButton
6805  * @extends Roo.Button
6806  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6807  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6808  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6809  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6810  * @cfg {String} arrowTooltip The title attribute of the arrow
6811  * @constructor
6812  * Create a new menu button
6813  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6814  * @param {Object} config The config object
6815  */
6816 Roo.SplitButton = function(renderTo, config){
6817     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6818     /**
6819      * @event arrowclick
6820      * Fires when this button's arrow is clicked
6821      * @param {SplitButton} this
6822      * @param {EventObject} e The click event
6823      */
6824     this.addEvents({"arrowclick":true});
6825 };
6826
6827 Roo.extend(Roo.SplitButton, Roo.Button, {
6828     render : function(renderTo){
6829         // this is one sweet looking template!
6830         var tpl = new Roo.Template(
6831             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6832             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6833             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
6834             "</tbody></table></td><td>",
6835             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6836             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
6837             "</tbody></table></td></tr></table>"
6838         );
6839         var btn = tpl.append(renderTo, [this.text, this.type], true);
6840         var btnEl = btn.child("button");
6841         if(this.cls){
6842             btn.addClass(this.cls);
6843         }
6844         if(this.icon){
6845             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6846         }
6847         if(this.iconCls){
6848             btnEl.addClass(this.iconCls);
6849             if(!this.cls){
6850                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6851             }
6852         }
6853         this.el = btn;
6854         if(this.handleMouseEvents){
6855             btn.on("mouseover", this.onMouseOver, this);
6856             btn.on("mouseout", this.onMouseOut, this);
6857             btn.on("mousedown", this.onMouseDown, this);
6858             btn.on("mouseup", this.onMouseUp, this);
6859         }
6860         btn.on(this.clickEvent, this.onClick, this);
6861         if(this.tooltip){
6862             if(typeof this.tooltip == 'object'){
6863                 Roo.QuickTips.tips(Roo.apply({
6864                       target: btnEl.id
6865                 }, this.tooltip));
6866             } else {
6867                 btnEl.dom[this.tooltipType] = this.tooltip;
6868             }
6869         }
6870         if(this.arrowTooltip){
6871             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6872         }
6873         if(this.hidden){
6874             this.hide();
6875         }
6876         if(this.disabled){
6877             this.disable();
6878         }
6879         if(this.pressed){
6880             this.el.addClass("x-btn-pressed");
6881         }
6882         if(Roo.isIE && !Roo.isIE7){
6883             this.autoWidth.defer(1, this);
6884         }else{
6885             this.autoWidth();
6886         }
6887         if(this.menu){
6888             this.menu.on("show", this.onMenuShow, this);
6889             this.menu.on("hide", this.onMenuHide, this);
6890         }
6891         this.fireEvent('render', this);
6892     },
6893
6894     // private
6895     autoWidth : function(){
6896         if(this.el){
6897             var tbl = this.el.child("table:first");
6898             var tbl2 = this.el.child("table:last");
6899             this.el.setWidth("auto");
6900             tbl.setWidth("auto");
6901             if(Roo.isIE7 && Roo.isStrict){
6902                 var ib = this.el.child('button:first');
6903                 if(ib && ib.getWidth() > 20){
6904                     ib.clip();
6905                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6906                 }
6907             }
6908             if(this.minWidth){
6909                 if(this.hidden){
6910                     this.el.beginMeasure();
6911                 }
6912                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6913                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6914                 }
6915                 if(this.hidden){
6916                     this.el.endMeasure();
6917                 }
6918             }
6919             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6920         } 
6921     },
6922     /**
6923      * Sets this button's click handler
6924      * @param {Function} handler The function to call when the button is clicked
6925      * @param {Object} scope (optional) Scope for the function passed above
6926      */
6927     setHandler : function(handler, scope){
6928         this.handler = handler;
6929         this.scope = scope;  
6930     },
6931     
6932     /**
6933      * Sets this button's arrow click handler
6934      * @param {Function} handler The function to call when the arrow is clicked
6935      * @param {Object} scope (optional) Scope for the function passed above
6936      */
6937     setArrowHandler : function(handler, scope){
6938         this.arrowHandler = handler;
6939         this.scope = scope;  
6940     },
6941     
6942     /**
6943      * Focus the button
6944      */
6945     focus : function(){
6946         if(this.el){
6947             this.el.child("button:first").focus();
6948         }
6949     },
6950
6951     // private
6952     onClick : function(e){
6953         e.preventDefault();
6954         if(!this.disabled){
6955             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6956                 if(this.menu && !this.menu.isVisible()){
6957                     this.menu.show(this.el, this.menuAlign);
6958                 }
6959                 this.fireEvent("arrowclick", this, e);
6960                 if(this.arrowHandler){
6961                     this.arrowHandler.call(this.scope || this, this, e);
6962                 }
6963             }else{
6964                 this.fireEvent("click", this, e);
6965                 if(this.handler){
6966                     this.handler.call(this.scope || this, this, e);
6967                 }
6968             }
6969         }
6970     },
6971     // private
6972     onMouseDown : function(e){
6973         if(!this.disabled){
6974             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6975         }
6976     },
6977     // private
6978     onMouseUp : function(e){
6979         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6980     }   
6981 });
6982
6983
6984 // backwards compat
6985 Roo.MenuButton = Roo.SplitButton;/*
6986  * Based on:
6987  * Ext JS Library 1.1.1
6988  * Copyright(c) 2006-2007, Ext JS, LLC.
6989  *
6990  * Originally Released Under LGPL - original licence link has changed is not relivant.
6991  *
6992  * Fork - LGPL
6993  * <script type="text/javascript">
6994  */
6995
6996 /**
6997  * @class Roo.Toolbar
6998  * @children   Roo.Toolbar.Item Roo.form.Field
6999  * Basic Toolbar class.
7000  * @constructor
7001  * Creates a new Toolbar
7002  * @param {Object} container The config object
7003  */ 
7004 Roo.Toolbar = function(container, buttons, config)
7005 {
7006     /// old consturctor format still supported..
7007     if(container instanceof Array){ // omit the container for later rendering
7008         buttons = container;
7009         config = buttons;
7010         container = null;
7011     }
7012     if (typeof(container) == 'object' && container.xtype) {
7013         config = container;
7014         container = config.container;
7015         buttons = config.buttons || []; // not really - use items!!
7016     }
7017     var xitems = [];
7018     if (config && config.items) {
7019         xitems = config.items;
7020         delete config.items;
7021     }
7022     Roo.apply(this, config);
7023     this.buttons = buttons;
7024     
7025     if(container){
7026         this.render(container);
7027     }
7028     this.xitems = xitems;
7029     Roo.each(xitems, function(b) {
7030         this.add(b);
7031     }, this);
7032     
7033 };
7034
7035 Roo.Toolbar.prototype = {
7036     /**
7037      * @cfg {Array} items
7038      * array of button configs or elements to add (will be converted to a MixedCollection)
7039      */
7040     items: false,
7041     /**
7042      * @cfg {String/HTMLElement/Element} container
7043      * The id or element that will contain the toolbar
7044      */
7045     // private
7046     render : function(ct){
7047         this.el = Roo.get(ct);
7048         if(this.cls){
7049             this.el.addClass(this.cls);
7050         }
7051         // using a table allows for vertical alignment
7052         // 100% width is needed by Safari...
7053         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7054         this.tr = this.el.child("tr", true);
7055         var autoId = 0;
7056         this.items = new Roo.util.MixedCollection(false, function(o){
7057             return o.id || ("item" + (++autoId));
7058         });
7059         if(this.buttons){
7060             this.add.apply(this, this.buttons);
7061             delete this.buttons;
7062         }
7063     },
7064
7065     /**
7066      * Adds element(s) to the toolbar -- this function takes a variable number of 
7067      * arguments of mixed type and adds them to the toolbar.
7068      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7069      * <ul>
7070      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7071      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7072      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7073      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7074      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7075      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7076      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7077      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7078      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7079      * </ul>
7080      * @param {Mixed} arg2
7081      * @param {Mixed} etc.
7082      */
7083     add : function(){
7084         var a = arguments, l = a.length;
7085         for(var i = 0; i < l; i++){
7086             this._add(a[i]);
7087         }
7088     },
7089     // private..
7090     _add : function(el) {
7091         
7092         if (el.xtype) {
7093             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7094         }
7095         
7096         if (el.applyTo){ // some kind of form field
7097             return this.addField(el);
7098         } 
7099         if (el.render){ // some kind of Toolbar.Item
7100             return this.addItem(el);
7101         }
7102         if (typeof el == "string"){ // string
7103             if(el == "separator" || el == "-"){
7104                 return this.addSeparator();
7105             }
7106             if (el == " "){
7107                 return this.addSpacer();
7108             }
7109             if(el == "->"){
7110                 return this.addFill();
7111             }
7112             return this.addText(el);
7113             
7114         }
7115         if(el.tagName){ // element
7116             return this.addElement(el);
7117         }
7118         if(typeof el == "object"){ // must be button config?
7119             return this.addButton(el);
7120         }
7121         // and now what?!?!
7122         return false;
7123         
7124     },
7125     
7126     /**
7127      * Add an Xtype element
7128      * @param {Object} xtype Xtype Object
7129      * @return {Object} created Object
7130      */
7131     addxtype : function(e){
7132         return this.add(e);  
7133     },
7134     
7135     /**
7136      * Returns the Element for this toolbar.
7137      * @return {Roo.Element}
7138      */
7139     getEl : function(){
7140         return this.el;  
7141     },
7142     
7143     /**
7144      * Adds a separator
7145      * @return {Roo.Toolbar.Item} The separator item
7146      */
7147     addSeparator : function(){
7148         return this.addItem(new Roo.Toolbar.Separator());
7149     },
7150
7151     /**
7152      * Adds a spacer element
7153      * @return {Roo.Toolbar.Spacer} The spacer item
7154      */
7155     addSpacer : function(){
7156         return this.addItem(new Roo.Toolbar.Spacer());
7157     },
7158
7159     /**
7160      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7161      * @return {Roo.Toolbar.Fill} The fill item
7162      */
7163     addFill : function(){
7164         return this.addItem(new Roo.Toolbar.Fill());
7165     },
7166
7167     /**
7168      * Adds any standard HTML element to the toolbar
7169      * @param {String/HTMLElement/Element} el The element or id of the element to add
7170      * @return {Roo.Toolbar.Item} The element's item
7171      */
7172     addElement : function(el){
7173         return this.addItem(new Roo.Toolbar.Item(el));
7174     },
7175     /**
7176      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7177      * @type Roo.util.MixedCollection  
7178      */
7179     items : false,
7180      
7181     /**
7182      * Adds any Toolbar.Item or subclass
7183      * @param {Roo.Toolbar.Item} item
7184      * @return {Roo.Toolbar.Item} The item
7185      */
7186     addItem : function(item){
7187         var td = this.nextBlock();
7188         item.render(td);
7189         this.items.add(item);
7190         return item;
7191     },
7192     
7193     /**
7194      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7195      * @param {Object/Array} config A button config or array of configs
7196      * @return {Roo.Toolbar.Button/Array}
7197      */
7198     addButton : function(config){
7199         if(config instanceof Array){
7200             var buttons = [];
7201             for(var i = 0, len = config.length; i < len; i++) {
7202                 buttons.push(this.addButton(config[i]));
7203             }
7204             return buttons;
7205         }
7206         var b = config;
7207         if(!(config instanceof Roo.Toolbar.Button)){
7208             b = config.split ?
7209                 new Roo.Toolbar.SplitButton(config) :
7210                 new Roo.Toolbar.Button(config);
7211         }
7212         var td = this.nextBlock();
7213         b.render(td);
7214         this.items.add(b);
7215         return b;
7216     },
7217     
7218     /**
7219      * Adds text to the toolbar
7220      * @param {String} text The text to add
7221      * @return {Roo.Toolbar.Item} The element's item
7222      */
7223     addText : function(text){
7224         return this.addItem(new Roo.Toolbar.TextItem(text));
7225     },
7226     
7227     /**
7228      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7229      * @param {Number} index The index where the item is to be inserted
7230      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7231      * @return {Roo.Toolbar.Button/Item}
7232      */
7233     insertButton : function(index, item){
7234         if(item instanceof Array){
7235             var buttons = [];
7236             for(var i = 0, len = item.length; i < len; i++) {
7237                buttons.push(this.insertButton(index + i, item[i]));
7238             }
7239             return buttons;
7240         }
7241         if (!(item instanceof Roo.Toolbar.Button)){
7242            item = new Roo.Toolbar.Button(item);
7243         }
7244         var td = document.createElement("td");
7245         this.tr.insertBefore(td, this.tr.childNodes[index]);
7246         item.render(td);
7247         this.items.insert(index, item);
7248         return item;
7249     },
7250     
7251     /**
7252      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7253      * @param {Object} config
7254      * @return {Roo.Toolbar.Item} The element's item
7255      */
7256     addDom : function(config, returnEl){
7257         var td = this.nextBlock();
7258         Roo.DomHelper.overwrite(td, config);
7259         var ti = new Roo.Toolbar.Item(td.firstChild);
7260         ti.render(td);
7261         this.items.add(ti);
7262         return ti;
7263     },
7264
7265     /**
7266      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7267      * @type Roo.util.MixedCollection  
7268      */
7269     fields : false,
7270     
7271     /**
7272      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7273      * Note: the field should not have been rendered yet. For a field that has already been
7274      * rendered, use {@link #addElement}.
7275      * @param {Roo.form.Field} field
7276      * @return {Roo.ToolbarItem}
7277      */
7278      
7279       
7280     addField : function(field) {
7281         if (!this.fields) {
7282             var autoId = 0;
7283             this.fields = new Roo.util.MixedCollection(false, function(o){
7284                 return o.id || ("item" + (++autoId));
7285             });
7286
7287         }
7288         
7289         var td = this.nextBlock();
7290         field.render(td);
7291         var ti = new Roo.Toolbar.Item(td.firstChild);
7292         ti.render(td);
7293         this.items.add(ti);
7294         this.fields.add(field);
7295         return ti;
7296     },
7297     /**
7298      * Hide the toolbar
7299      * @method hide
7300      */
7301      
7302       
7303     hide : function()
7304     {
7305         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7306         this.el.child('div').hide();
7307     },
7308     /**
7309      * Show the toolbar
7310      * @method show
7311      */
7312     show : function()
7313     {
7314         this.el.child('div').show();
7315     },
7316       
7317     // private
7318     nextBlock : function(){
7319         var td = document.createElement("td");
7320         this.tr.appendChild(td);
7321         return td;
7322     },
7323
7324     // private
7325     destroy : function(){
7326         if(this.items){ // rendered?
7327             Roo.destroy.apply(Roo, this.items.items);
7328         }
7329         if(this.fields){ // rendered?
7330             Roo.destroy.apply(Roo, this.fields.items);
7331         }
7332         Roo.Element.uncache(this.el, this.tr);
7333     }
7334 };
7335
7336 /**
7337  * @class Roo.Toolbar.Item
7338  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7339  * @constructor
7340  * Creates a new Item
7341  * @param {HTMLElement} el 
7342  */
7343 Roo.Toolbar.Item = function(el){
7344     var cfg = {};
7345     if (typeof (el.xtype) != 'undefined') {
7346         cfg = el;
7347         el = cfg.el;
7348     }
7349     
7350     this.el = Roo.getDom(el);
7351     this.id = Roo.id(this.el);
7352     this.hidden = false;
7353     
7354     this.addEvents({
7355          /**
7356              * @event render
7357              * Fires when the button is rendered
7358              * @param {Button} this
7359              */
7360         'render': true
7361     });
7362     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7363 };
7364 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7365 //Roo.Toolbar.Item.prototype = {
7366     
7367     /**
7368      * Get this item's HTML Element
7369      * @return {HTMLElement}
7370      */
7371     getEl : function(){
7372        return this.el;  
7373     },
7374
7375     // private
7376     render : function(td){
7377         
7378          this.td = td;
7379         td.appendChild(this.el);
7380         
7381         this.fireEvent('render', this);
7382     },
7383     
7384     /**
7385      * Removes and destroys this item.
7386      */
7387     destroy : function(){
7388         this.td.parentNode.removeChild(this.td);
7389     },
7390     
7391     /**
7392      * Shows this item.
7393      */
7394     show: function(){
7395         this.hidden = false;
7396         this.td.style.display = "";
7397     },
7398     
7399     /**
7400      * Hides this item.
7401      */
7402     hide: function(){
7403         this.hidden = true;
7404         this.td.style.display = "none";
7405     },
7406     
7407     /**
7408      * Convenience function for boolean show/hide.
7409      * @param {Boolean} visible true to show/false to hide
7410      */
7411     setVisible: function(visible){
7412         if(visible) {
7413             this.show();
7414         }else{
7415             this.hide();
7416         }
7417     },
7418     
7419     /**
7420      * Try to focus this item.
7421      */
7422     focus : function(){
7423         Roo.fly(this.el).focus();
7424     },
7425     
7426     /**
7427      * Disables this item.
7428      */
7429     disable : function(){
7430         Roo.fly(this.td).addClass("x-item-disabled");
7431         this.disabled = true;
7432         this.el.disabled = true;
7433     },
7434     
7435     /**
7436      * Enables this item.
7437      */
7438     enable : function(){
7439         Roo.fly(this.td).removeClass("x-item-disabled");
7440         this.disabled = false;
7441         this.el.disabled = false;
7442     }
7443 });
7444
7445
7446 /**
7447  * @class Roo.Toolbar.Separator
7448  * @extends Roo.Toolbar.Item
7449  * A simple toolbar separator class
7450  * @constructor
7451  * Creates a new Separator
7452  */
7453 Roo.Toolbar.Separator = function(cfg){
7454     
7455     var s = document.createElement("span");
7456     s.className = "ytb-sep";
7457     if (cfg) {
7458         cfg.el = s;
7459     }
7460     
7461     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7462 };
7463 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7464     enable:Roo.emptyFn,
7465     disable:Roo.emptyFn,
7466     focus:Roo.emptyFn
7467 });
7468
7469 /**
7470  * @class Roo.Toolbar.Spacer
7471  * @extends Roo.Toolbar.Item
7472  * A simple element that adds extra horizontal space to a toolbar.
7473  * @constructor
7474  * Creates a new Spacer
7475  */
7476 Roo.Toolbar.Spacer = function(cfg){
7477     var s = document.createElement("div");
7478     s.className = "ytb-spacer";
7479     if (cfg) {
7480         cfg.el = s;
7481     }
7482     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7483 };
7484 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7485     enable:Roo.emptyFn,
7486     disable:Roo.emptyFn,
7487     focus:Roo.emptyFn
7488 });
7489
7490 /**
7491  * @class Roo.Toolbar.Fill
7492  * @extends Roo.Toolbar.Spacer
7493  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7494  * @constructor
7495  * Creates a new Spacer
7496  */
7497 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7498     // private
7499     render : function(td){
7500         td.style.width = '100%';
7501         Roo.Toolbar.Fill.superclass.render.call(this, td);
7502     }
7503 });
7504
7505 /**
7506  * @class Roo.Toolbar.TextItem
7507  * @extends Roo.Toolbar.Item
7508  * A simple class that renders text directly into a toolbar.
7509  * @constructor
7510  * Creates a new TextItem
7511  * @cfg {string} text 
7512  */
7513 Roo.Toolbar.TextItem = function(cfg){
7514     var  text = cfg || "";
7515     if (typeof(cfg) == 'object') {
7516         text = cfg.text || "";
7517     }  else {
7518         cfg = null;
7519     }
7520     var s = document.createElement("span");
7521     s.className = "ytb-text";
7522     s.innerHTML = text;
7523     if (cfg) {
7524         cfg.el  = s;
7525     }
7526     
7527     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7528 };
7529 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7530     
7531      
7532     enable:Roo.emptyFn,
7533     disable:Roo.emptyFn,
7534     focus:Roo.emptyFn
7535 });
7536
7537 /**
7538  * @class Roo.Toolbar.Button
7539  * @extends Roo.Button
7540  * A button that renders into a toolbar.
7541  * @constructor
7542  * Creates a new Button
7543  * @param {Object} config A standard {@link Roo.Button} config object
7544  */
7545 Roo.Toolbar.Button = function(config){
7546     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7547 };
7548 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7549 {
7550     
7551     
7552     render : function(td){
7553         this.td = td;
7554         Roo.Toolbar.Button.superclass.render.call(this, td);
7555     },
7556     
7557     /**
7558      * Removes and destroys this button
7559      */
7560     destroy : function(){
7561         Roo.Toolbar.Button.superclass.destroy.call(this);
7562         this.td.parentNode.removeChild(this.td);
7563     },
7564     
7565     /**
7566      * Shows this button
7567      */
7568     show: function(){
7569         this.hidden = false;
7570         this.td.style.display = "";
7571     },
7572     
7573     /**
7574      * Hides this button
7575      */
7576     hide: function(){
7577         this.hidden = true;
7578         this.td.style.display = "none";
7579     },
7580
7581     /**
7582      * Disables this item
7583      */
7584     disable : function(){
7585         Roo.fly(this.td).addClass("x-item-disabled");
7586         this.disabled = true;
7587     },
7588
7589     /**
7590      * Enables this item
7591      */
7592     enable : function(){
7593         Roo.fly(this.td).removeClass("x-item-disabled");
7594         this.disabled = false;
7595     }
7596 });
7597 // backwards compat
7598 Roo.ToolbarButton = Roo.Toolbar.Button;
7599
7600 /**
7601  * @class Roo.Toolbar.SplitButton
7602  * @extends Roo.SplitButton
7603  * A menu button that renders into a toolbar.
7604  * @constructor
7605  * Creates a new SplitButton
7606  * @param {Object} config A standard {@link Roo.SplitButton} config object
7607  */
7608 Roo.Toolbar.SplitButton = function(config){
7609     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7610 };
7611 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7612     render : function(td){
7613         this.td = td;
7614         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7615     },
7616     
7617     /**
7618      * Removes and destroys this button
7619      */
7620     destroy : function(){
7621         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7622         this.td.parentNode.removeChild(this.td);
7623     },
7624     
7625     /**
7626      * Shows this button
7627      */
7628     show: function(){
7629         this.hidden = false;
7630         this.td.style.display = "";
7631     },
7632     
7633     /**
7634      * Hides this button
7635      */
7636     hide: function(){
7637         this.hidden = true;
7638         this.td.style.display = "none";
7639     }
7640 });
7641
7642 // backwards compat
7643 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7644  * Based on:
7645  * Ext JS Library 1.1.1
7646  * Copyright(c) 2006-2007, Ext JS, LLC.
7647  *
7648  * Originally Released Under LGPL - original licence link has changed is not relivant.
7649  *
7650  * Fork - LGPL
7651  * <script type="text/javascript">
7652  */
7653  
7654 /**
7655  * @class Roo.PagingToolbar
7656  * @extends Roo.Toolbar
7657  * @children   Roo.Toolbar.Item Roo.form.Field
7658  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7659  * @constructor
7660  * Create a new PagingToolbar
7661  * @param {Object} config The config object
7662  */
7663 Roo.PagingToolbar = function(el, ds, config)
7664 {
7665     // old args format still supported... - xtype is prefered..
7666     if (typeof(el) == 'object' && el.xtype) {
7667         // created from xtype...
7668         config = el;
7669         ds = el.dataSource;
7670         el = config.container;
7671     }
7672     var items = [];
7673     if (config.items) {
7674         items = config.items;
7675         config.items = [];
7676     }
7677     
7678     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7679     this.ds = ds;
7680     this.cursor = 0;
7681     this.renderButtons(this.el);
7682     this.bind(ds);
7683     
7684     // supprot items array.
7685    
7686     Roo.each(items, function(e) {
7687         this.add(Roo.factory(e));
7688     },this);
7689     
7690 };
7691
7692 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7693     /**
7694      * @cfg {Roo.data.Store} dataSource
7695      * The underlying data store providing the paged data
7696      */
7697     /**
7698      * @cfg {String/HTMLElement/Element} container
7699      * container The id or element that will contain the toolbar
7700      */
7701     /**
7702      * @cfg {Boolean} displayInfo
7703      * True to display the displayMsg (defaults to false)
7704      */
7705     /**
7706      * @cfg {Number} pageSize
7707      * The number of records to display per page (defaults to 20)
7708      */
7709     pageSize: 20,
7710     /**
7711      * @cfg {String} displayMsg
7712      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7713      */
7714     displayMsg : 'Displaying {0} - {1} of {2}',
7715     /**
7716      * @cfg {String} emptyMsg
7717      * The message to display when no records are found (defaults to "No data to display")
7718      */
7719     emptyMsg : 'No data to display',
7720     /**
7721      * Customizable piece of the default paging text (defaults to "Page")
7722      * @type String
7723      */
7724     beforePageText : "Page",
7725     /**
7726      * Customizable piece of the default paging text (defaults to "of %0")
7727      * @type String
7728      */
7729     afterPageText : "of {0}",
7730     /**
7731      * Customizable piece of the default paging text (defaults to "First Page")
7732      * @type String
7733      */
7734     firstText : "First Page",
7735     /**
7736      * Customizable piece of the default paging text (defaults to "Previous Page")
7737      * @type String
7738      */
7739     prevText : "Previous Page",
7740     /**
7741      * Customizable piece of the default paging text (defaults to "Next Page")
7742      * @type String
7743      */
7744     nextText : "Next Page",
7745     /**
7746      * Customizable piece of the default paging text (defaults to "Last Page")
7747      * @type String
7748      */
7749     lastText : "Last Page",
7750     /**
7751      * Customizable piece of the default paging text (defaults to "Refresh")
7752      * @type String
7753      */
7754     refreshText : "Refresh",
7755
7756     // private
7757     renderButtons : function(el){
7758         Roo.PagingToolbar.superclass.render.call(this, el);
7759         this.first = this.addButton({
7760             tooltip: this.firstText,
7761             cls: "x-btn-icon x-grid-page-first",
7762             disabled: true,
7763             handler: this.onClick.createDelegate(this, ["first"])
7764         });
7765         this.prev = this.addButton({
7766             tooltip: this.prevText,
7767             cls: "x-btn-icon x-grid-page-prev",
7768             disabled: true,
7769             handler: this.onClick.createDelegate(this, ["prev"])
7770         });
7771         //this.addSeparator();
7772         this.add(this.beforePageText);
7773         this.field = Roo.get(this.addDom({
7774            tag: "input",
7775            type: "text",
7776            size: "3",
7777            value: "1",
7778            cls: "x-grid-page-number"
7779         }).el);
7780         this.field.on("keydown", this.onPagingKeydown, this);
7781         this.field.on("focus", function(){this.dom.select();});
7782         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7783         this.field.setHeight(18);
7784         //this.addSeparator();
7785         this.next = this.addButton({
7786             tooltip: this.nextText,
7787             cls: "x-btn-icon x-grid-page-next",
7788             disabled: true,
7789             handler: this.onClick.createDelegate(this, ["next"])
7790         });
7791         this.last = this.addButton({
7792             tooltip: this.lastText,
7793             cls: "x-btn-icon x-grid-page-last",
7794             disabled: true,
7795             handler: this.onClick.createDelegate(this, ["last"])
7796         });
7797         //this.addSeparator();
7798         this.loading = this.addButton({
7799             tooltip: this.refreshText,
7800             cls: "x-btn-icon x-grid-loading",
7801             handler: this.onClick.createDelegate(this, ["refresh"])
7802         });
7803
7804         if(this.displayInfo){
7805             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7806         }
7807     },
7808
7809     // private
7810     updateInfo : function(){
7811         if(this.displayEl){
7812             var count = this.ds.getCount();
7813             var msg = count == 0 ?
7814                 this.emptyMsg :
7815                 String.format(
7816                     this.displayMsg,
7817                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7818                 );
7819             this.displayEl.update(msg);
7820         }
7821     },
7822
7823     // private
7824     onLoad : function(ds, r, o){
7825        this.cursor = o.params ? o.params.start : 0;
7826        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7827
7828        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7829        this.field.dom.value = ap;
7830        this.first.setDisabled(ap == 1);
7831        this.prev.setDisabled(ap == 1);
7832        this.next.setDisabled(ap == ps);
7833        this.last.setDisabled(ap == ps);
7834        this.loading.enable();
7835        this.updateInfo();
7836     },
7837
7838     // private
7839     getPageData : function(){
7840         var total = this.ds.getTotalCount();
7841         return {
7842             total : total,
7843             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7844             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7845         };
7846     },
7847
7848     // private
7849     onLoadError : function(){
7850         this.loading.enable();
7851     },
7852
7853     // private
7854     onPagingKeydown : function(e){
7855         var k = e.getKey();
7856         var d = this.getPageData();
7857         if(k == e.RETURN){
7858             var v = this.field.dom.value, pageNum;
7859             if(!v || isNaN(pageNum = parseInt(v, 10))){
7860                 this.field.dom.value = d.activePage;
7861                 return;
7862             }
7863             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7864             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7865             e.stopEvent();
7866         }
7867         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
7868         {
7869           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7870           this.field.dom.value = pageNum;
7871           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7872           e.stopEvent();
7873         }
7874         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7875         {
7876           var v = this.field.dom.value, pageNum; 
7877           var increment = (e.shiftKey) ? 10 : 1;
7878           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7879             increment *= -1;
7880           }
7881           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7882             this.field.dom.value = d.activePage;
7883             return;
7884           }
7885           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7886           {
7887             this.field.dom.value = parseInt(v, 10) + increment;
7888             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7889             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7890           }
7891           e.stopEvent();
7892         }
7893     },
7894
7895     // private
7896     beforeLoad : function(){
7897         if(this.loading){
7898             this.loading.disable();
7899         }
7900     },
7901
7902     // private
7903     onClick : function(which){
7904         var ds = this.ds;
7905         switch(which){
7906             case "first":
7907                 ds.load({params:{start: 0, limit: this.pageSize}});
7908             break;
7909             case "prev":
7910                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7911             break;
7912             case "next":
7913                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7914             break;
7915             case "last":
7916                 var total = ds.getTotalCount();
7917                 var extra = total % this.pageSize;
7918                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7919                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7920             break;
7921             case "refresh":
7922                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7923             break;
7924         }
7925     },
7926
7927     /**
7928      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7929      * @param {Roo.data.Store} store The data store to unbind
7930      */
7931     unbind : function(ds){
7932         ds.un("beforeload", this.beforeLoad, this);
7933         ds.un("load", this.onLoad, this);
7934         ds.un("loadexception", this.onLoadError, this);
7935         ds.un("remove", this.updateInfo, this);
7936         ds.un("add", this.updateInfo, this);
7937         this.ds = undefined;
7938     },
7939
7940     /**
7941      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7942      * @param {Roo.data.Store} store The data store to bind
7943      */
7944     bind : function(ds){
7945         ds.on("beforeload", this.beforeLoad, this);
7946         ds.on("load", this.onLoad, this);
7947         ds.on("loadexception", this.onLoadError, this);
7948         ds.on("remove", this.updateInfo, this);
7949         ds.on("add", this.updateInfo, this);
7950         this.ds = ds;
7951     }
7952 });/*
7953  * Based on:
7954  * Ext JS Library 1.1.1
7955  * Copyright(c) 2006-2007, Ext JS, LLC.
7956  *
7957  * Originally Released Under LGPL - original licence link has changed is not relivant.
7958  *
7959  * Fork - LGPL
7960  * <script type="text/javascript">
7961  */
7962
7963 /**
7964  * @class Roo.Resizable
7965  * @extends Roo.util.Observable
7966  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7967  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7968  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
7969  * the element will be wrapped for you automatically.</p>
7970  * <p>Here is the list of valid resize handles:</p>
7971  * <pre>
7972 Value   Description
7973 ------  -------------------
7974  'n'     north
7975  's'     south
7976  'e'     east
7977  'w'     west
7978  'nw'    northwest
7979  'sw'    southwest
7980  'se'    southeast
7981  'ne'    northeast
7982  'hd'    horizontal drag
7983  'all'   all
7984 </pre>
7985  * <p>Here's an example showing the creation of a typical Resizable:</p>
7986  * <pre><code>
7987 var resizer = new Roo.Resizable("element-id", {
7988     handles: 'all',
7989     minWidth: 200,
7990     minHeight: 100,
7991     maxWidth: 500,
7992     maxHeight: 400,
7993     pinned: true
7994 });
7995 resizer.on("resize", myHandler);
7996 </code></pre>
7997  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7998  * resizer.east.setDisplayed(false);</p>
7999  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8000  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8001  * resize operation's new size (defaults to [0, 0])
8002  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8003  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8004  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8005  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8006  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8007  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8008  * @cfg {Number} width The width of the element in pixels (defaults to null)
8009  * @cfg {Number} height The height of the element in pixels (defaults to null)
8010  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8011  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8012  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8013  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8014  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8015  * in favor of the handles config option (defaults to false)
8016  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8017  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8018  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8019  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8020  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8021  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8022  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8023  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8024  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8025  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8026  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8027  * @constructor
8028  * Create a new resizable component
8029  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8030  * @param {Object} config configuration options
8031   */
8032 Roo.Resizable = function(el, config)
8033 {
8034     this.el = Roo.get(el);
8035
8036     if(config && config.wrap){
8037         config.resizeChild = this.el;
8038         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8039         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8040         this.el.setStyle("overflow", "hidden");
8041         this.el.setPositioning(config.resizeChild.getPositioning());
8042         config.resizeChild.clearPositioning();
8043         if(!config.width || !config.height){
8044             var csize = config.resizeChild.getSize();
8045             this.el.setSize(csize.width, csize.height);
8046         }
8047         if(config.pinned && !config.adjustments){
8048             config.adjustments = "auto";
8049         }
8050     }
8051
8052     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8053     this.proxy.unselectable();
8054     this.proxy.enableDisplayMode('block');
8055
8056     Roo.apply(this, config);
8057
8058     if(this.pinned){
8059         this.disableTrackOver = true;
8060         this.el.addClass("x-resizable-pinned");
8061     }
8062     // if the element isn't positioned, make it relative
8063     var position = this.el.getStyle("position");
8064     if(position != "absolute" && position != "fixed"){
8065         this.el.setStyle("position", "relative");
8066     }
8067     if(!this.handles){ // no handles passed, must be legacy style
8068         this.handles = 's,e,se';
8069         if(this.multiDirectional){
8070             this.handles += ',n,w';
8071         }
8072     }
8073     if(this.handles == "all"){
8074         this.handles = "n s e w ne nw se sw";
8075     }
8076     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8077     var ps = Roo.Resizable.positions;
8078     for(var i = 0, len = hs.length; i < len; i++){
8079         if(hs[i] && ps[hs[i]]){
8080             var pos = ps[hs[i]];
8081             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8082         }
8083     }
8084     // legacy
8085     this.corner = this.southeast;
8086     
8087     // updateBox = the box can move..
8088     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8089         this.updateBox = true;
8090     }
8091
8092     this.activeHandle = null;
8093
8094     if(this.resizeChild){
8095         if(typeof this.resizeChild == "boolean"){
8096             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8097         }else{
8098             this.resizeChild = Roo.get(this.resizeChild, true);
8099         }
8100     }
8101     
8102     if(this.adjustments == "auto"){
8103         var rc = this.resizeChild;
8104         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8105         if(rc && (hw || hn)){
8106             rc.position("relative");
8107             rc.setLeft(hw ? hw.el.getWidth() : 0);
8108             rc.setTop(hn ? hn.el.getHeight() : 0);
8109         }
8110         this.adjustments = [
8111             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8112             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8113         ];
8114     }
8115
8116     if(this.draggable){
8117         this.dd = this.dynamic ?
8118             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8119         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8120     }
8121
8122     // public events
8123     this.addEvents({
8124         /**
8125          * @event beforeresize
8126          * Fired before resize is allowed. Set enabled to false to cancel resize.
8127          * @param {Roo.Resizable} this
8128          * @param {Roo.EventObject} e The mousedown event
8129          */
8130         "beforeresize" : true,
8131         /**
8132          * @event resizing
8133          * Fired a resizing.
8134          * @param {Roo.Resizable} this
8135          * @param {Number} x The new x position
8136          * @param {Number} y The new y position
8137          * @param {Number} w The new w width
8138          * @param {Number} h The new h hight
8139          * @param {Roo.EventObject} e The mouseup event
8140          */
8141         "resizing" : true,
8142         /**
8143          * @event resize
8144          * Fired after a resize.
8145          * @param {Roo.Resizable} this
8146          * @param {Number} width The new width
8147          * @param {Number} height The new height
8148          * @param {Roo.EventObject} e The mouseup event
8149          */
8150         "resize" : true
8151     });
8152
8153     if(this.width !== null && this.height !== null){
8154         this.resizeTo(this.width, this.height);
8155     }else{
8156         this.updateChildSize();
8157     }
8158     if(Roo.isIE){
8159         this.el.dom.style.zoom = 1;
8160     }
8161     Roo.Resizable.superclass.constructor.call(this);
8162 };
8163
8164 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8165         resizeChild : false,
8166         adjustments : [0, 0],
8167         minWidth : 5,
8168         minHeight : 5,
8169         maxWidth : 10000,
8170         maxHeight : 10000,
8171         enabled : true,
8172         animate : false,
8173         duration : .35,
8174         dynamic : false,
8175         handles : false,
8176         multiDirectional : false,
8177         disableTrackOver : false,
8178         easing : 'easeOutStrong',
8179         widthIncrement : 0,
8180         heightIncrement : 0,
8181         pinned : false,
8182         width : null,
8183         height : null,
8184         preserveRatio : false,
8185         transparent: false,
8186         minX: 0,
8187         minY: 0,
8188         draggable: false,
8189
8190         /**
8191          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8192          */
8193         constrainTo: undefined,
8194         /**
8195          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8196          */
8197         resizeRegion: undefined,
8198
8199
8200     /**
8201      * Perform a manual resize
8202      * @param {Number} width
8203      * @param {Number} height
8204      */
8205     resizeTo : function(width, height){
8206         this.el.setSize(width, height);
8207         this.updateChildSize();
8208         this.fireEvent("resize", this, width, height, null);
8209     },
8210
8211     // private
8212     startSizing : function(e, handle){
8213         this.fireEvent("beforeresize", this, e);
8214         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8215
8216             if(!this.overlay){
8217                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8218                 this.overlay.unselectable();
8219                 this.overlay.enableDisplayMode("block");
8220                 this.overlay.on("mousemove", this.onMouseMove, this);
8221                 this.overlay.on("mouseup", this.onMouseUp, this);
8222             }
8223             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8224
8225             this.resizing = true;
8226             this.startBox = this.el.getBox();
8227             this.startPoint = e.getXY();
8228             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8229                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8230
8231             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8232             this.overlay.show();
8233
8234             if(this.constrainTo) {
8235                 var ct = Roo.get(this.constrainTo);
8236                 this.resizeRegion = ct.getRegion().adjust(
8237                     ct.getFrameWidth('t'),
8238                     ct.getFrameWidth('l'),
8239                     -ct.getFrameWidth('b'),
8240                     -ct.getFrameWidth('r')
8241                 );
8242             }
8243
8244             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8245             this.proxy.show();
8246             this.proxy.setBox(this.startBox);
8247             if(!this.dynamic){
8248                 this.proxy.setStyle('visibility', 'visible');
8249             }
8250         }
8251     },
8252
8253     // private
8254     onMouseDown : function(handle, e){
8255         if(this.enabled){
8256             e.stopEvent();
8257             this.activeHandle = handle;
8258             this.startSizing(e, handle);
8259         }
8260     },
8261
8262     // private
8263     onMouseUp : function(e){
8264         var size = this.resizeElement();
8265         this.resizing = false;
8266         this.handleOut();
8267         this.overlay.hide();
8268         this.proxy.hide();
8269         this.fireEvent("resize", this, size.width, size.height, e);
8270     },
8271
8272     // private
8273     updateChildSize : function(){
8274         
8275         if(this.resizeChild){
8276             var el = this.el;
8277             var child = this.resizeChild;
8278             var adj = this.adjustments;
8279             if(el.dom.offsetWidth){
8280                 var b = el.getSize(true);
8281                 child.setSize(b.width+adj[0], b.height+adj[1]);
8282             }
8283             // Second call here for IE
8284             // The first call enables instant resizing and
8285             // the second call corrects scroll bars if they
8286             // exist
8287             if(Roo.isIE){
8288                 setTimeout(function(){
8289                     if(el.dom.offsetWidth){
8290                         var b = el.getSize(true);
8291                         child.setSize(b.width+adj[0], b.height+adj[1]);
8292                     }
8293                 }, 10);
8294             }
8295         }
8296     },
8297
8298     // private
8299     snap : function(value, inc, min){
8300         if(!inc || !value) {
8301             return value;
8302         }
8303         var newValue = value;
8304         var m = value % inc;
8305         if(m > 0){
8306             if(m > (inc/2)){
8307                 newValue = value + (inc-m);
8308             }else{
8309                 newValue = value - m;
8310             }
8311         }
8312         return Math.max(min, newValue);
8313     },
8314
8315     // private
8316     resizeElement : function(){
8317         var box = this.proxy.getBox();
8318         if(this.updateBox){
8319             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8320         }else{
8321             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8322         }
8323         this.updateChildSize();
8324         if(!this.dynamic){
8325             this.proxy.hide();
8326         }
8327         return box;
8328     },
8329
8330     // private
8331     constrain : function(v, diff, m, mx){
8332         if(v - diff < m){
8333             diff = v - m;
8334         }else if(v - diff > mx){
8335             diff = mx - v;
8336         }
8337         return diff;
8338     },
8339
8340     // private
8341     onMouseMove : function(e){
8342         
8343         if(this.enabled){
8344             try{// try catch so if something goes wrong the user doesn't get hung
8345
8346             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8347                 return;
8348             }
8349
8350             //var curXY = this.startPoint;
8351             var curSize = this.curSize || this.startBox;
8352             var x = this.startBox.x, y = this.startBox.y;
8353             var ox = x, oy = y;
8354             var w = curSize.width, h = curSize.height;
8355             var ow = w, oh = h;
8356             var mw = this.minWidth, mh = this.minHeight;
8357             var mxw = this.maxWidth, mxh = this.maxHeight;
8358             var wi = this.widthIncrement;
8359             var hi = this.heightIncrement;
8360
8361             var eventXY = e.getXY();
8362             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8363             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8364
8365             var pos = this.activeHandle.position;
8366
8367             switch(pos){
8368                 case "east":
8369                     w += diffX;
8370                     w = Math.min(Math.max(mw, w), mxw);
8371                     break;
8372              
8373                 case "south":
8374                     h += diffY;
8375                     h = Math.min(Math.max(mh, h), mxh);
8376                     break;
8377                 case "southeast":
8378                     w += diffX;
8379                     h += diffY;
8380                     w = Math.min(Math.max(mw, w), mxw);
8381                     h = Math.min(Math.max(mh, h), mxh);
8382                     break;
8383                 case "north":
8384                     diffY = this.constrain(h, diffY, mh, mxh);
8385                     y += diffY;
8386                     h -= diffY;
8387                     break;
8388                 case "hdrag":
8389                     
8390                     if (wi) {
8391                         var adiffX = Math.abs(diffX);
8392                         var sub = (adiffX % wi); // how much 
8393                         if (sub > (wi/2)) { // far enough to snap
8394                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8395                         } else {
8396                             // remove difference.. 
8397                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8398                         }
8399                     }
8400                     x += diffX;
8401                     x = Math.max(this.minX, x);
8402                     break;
8403                 case "west":
8404                     diffX = this.constrain(w, diffX, mw, mxw);
8405                     x += diffX;
8406                     w -= diffX;
8407                     break;
8408                 case "northeast":
8409                     w += diffX;
8410                     w = Math.min(Math.max(mw, w), mxw);
8411                     diffY = this.constrain(h, diffY, mh, mxh);
8412                     y += diffY;
8413                     h -= diffY;
8414                     break;
8415                 case "northwest":
8416                     diffX = this.constrain(w, diffX, mw, mxw);
8417                     diffY = this.constrain(h, diffY, mh, mxh);
8418                     y += diffY;
8419                     h -= diffY;
8420                     x += diffX;
8421                     w -= diffX;
8422                     break;
8423                case "southwest":
8424                     diffX = this.constrain(w, diffX, mw, mxw);
8425                     h += diffY;
8426                     h = Math.min(Math.max(mh, h), mxh);
8427                     x += diffX;
8428                     w -= diffX;
8429                     break;
8430             }
8431
8432             var sw = this.snap(w, wi, mw);
8433             var sh = this.snap(h, hi, mh);
8434             if(sw != w || sh != h){
8435                 switch(pos){
8436                     case "northeast":
8437                         y -= sh - h;
8438                     break;
8439                     case "north":
8440                         y -= sh - h;
8441                         break;
8442                     case "southwest":
8443                         x -= sw - w;
8444                     break;
8445                     case "west":
8446                         x -= sw - w;
8447                         break;
8448                     case "northwest":
8449                         x -= sw - w;
8450                         y -= sh - h;
8451                     break;
8452                 }
8453                 w = sw;
8454                 h = sh;
8455             }
8456
8457             if(this.preserveRatio){
8458                 switch(pos){
8459                     case "southeast":
8460                     case "east":
8461                         h = oh * (w/ow);
8462                         h = Math.min(Math.max(mh, h), mxh);
8463                         w = ow * (h/oh);
8464                        break;
8465                     case "south":
8466                         w = ow * (h/oh);
8467                         w = Math.min(Math.max(mw, w), mxw);
8468                         h = oh * (w/ow);
8469                         break;
8470                     case "northeast":
8471                         w = ow * (h/oh);
8472                         w = Math.min(Math.max(mw, w), mxw);
8473                         h = oh * (w/ow);
8474                     break;
8475                     case "north":
8476                         var tw = w;
8477                         w = ow * (h/oh);
8478                         w = Math.min(Math.max(mw, w), mxw);
8479                         h = oh * (w/ow);
8480                         x += (tw - w) / 2;
8481                         break;
8482                     case "southwest":
8483                         h = oh * (w/ow);
8484                         h = Math.min(Math.max(mh, h), mxh);
8485                         var tw = w;
8486                         w = ow * (h/oh);
8487                         x += tw - w;
8488                         break;
8489                     case "west":
8490                         var th = h;
8491                         h = oh * (w/ow);
8492                         h = Math.min(Math.max(mh, h), mxh);
8493                         y += (th - h) / 2;
8494                         var tw = w;
8495                         w = ow * (h/oh);
8496                         x += tw - w;
8497                        break;
8498                     case "northwest":
8499                         var tw = w;
8500                         var th = h;
8501                         h = oh * (w/ow);
8502                         h = Math.min(Math.max(mh, h), mxh);
8503                         w = ow * (h/oh);
8504                         y += th - h;
8505                         x += tw - w;
8506                        break;
8507
8508                 }
8509             }
8510             if (pos == 'hdrag') {
8511                 w = ow;
8512             }
8513             this.proxy.setBounds(x, y, w, h);
8514             if(this.dynamic){
8515                 this.resizeElement();
8516             }
8517             }catch(e){}
8518         }
8519         this.fireEvent("resizing", this, x, y, w, h, e);
8520     },
8521
8522     // private
8523     handleOver : function(){
8524         if(this.enabled){
8525             this.el.addClass("x-resizable-over");
8526         }
8527     },
8528
8529     // private
8530     handleOut : function(){
8531         if(!this.resizing){
8532             this.el.removeClass("x-resizable-over");
8533         }
8534     },
8535
8536     /**
8537      * Returns the element this component is bound to.
8538      * @return {Roo.Element}
8539      */
8540     getEl : function(){
8541         return this.el;
8542     },
8543
8544     /**
8545      * Returns the resizeChild element (or null).
8546      * @return {Roo.Element}
8547      */
8548     getResizeChild : function(){
8549         return this.resizeChild;
8550     },
8551     groupHandler : function()
8552     {
8553         
8554     },
8555     /**
8556      * Destroys this resizable. If the element was wrapped and
8557      * removeEl is not true then the element remains.
8558      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8559      */
8560     destroy : function(removeEl){
8561         this.proxy.remove();
8562         if(this.overlay){
8563             this.overlay.removeAllListeners();
8564             this.overlay.remove();
8565         }
8566         var ps = Roo.Resizable.positions;
8567         for(var k in ps){
8568             if(typeof ps[k] != "function" && this[ps[k]]){
8569                 var h = this[ps[k]];
8570                 h.el.removeAllListeners();
8571                 h.el.remove();
8572             }
8573         }
8574         if(removeEl){
8575             this.el.update("");
8576             this.el.remove();
8577         }
8578     }
8579 });
8580
8581 // private
8582 // hash to map config positions to true positions
8583 Roo.Resizable.positions = {
8584     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8585     hd: "hdrag"
8586 };
8587
8588 // private
8589 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8590     if(!this.tpl){
8591         // only initialize the template if resizable is used
8592         var tpl = Roo.DomHelper.createTemplate(
8593             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8594         );
8595         tpl.compile();
8596         Roo.Resizable.Handle.prototype.tpl = tpl;
8597     }
8598     this.position = pos;
8599     this.rz = rz;
8600     // show north drag fro topdra
8601     var handlepos = pos == 'hdrag' ? 'north' : pos;
8602     
8603     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8604     if (pos == 'hdrag') {
8605         this.el.setStyle('cursor', 'pointer');
8606     }
8607     this.el.unselectable();
8608     if(transparent){
8609         this.el.setOpacity(0);
8610     }
8611     this.el.on("mousedown", this.onMouseDown, this);
8612     if(!disableTrackOver){
8613         this.el.on("mouseover", this.onMouseOver, this);
8614         this.el.on("mouseout", this.onMouseOut, this);
8615     }
8616 };
8617
8618 // private
8619 Roo.Resizable.Handle.prototype = {
8620     afterResize : function(rz){
8621         Roo.log('after?');
8622         // do nothing
8623     },
8624     // private
8625     onMouseDown : function(e){
8626         this.rz.onMouseDown(this, e);
8627     },
8628     // private
8629     onMouseOver : function(e){
8630         this.rz.handleOver(this, e);
8631     },
8632     // private
8633     onMouseOut : function(e){
8634         this.rz.handleOut(this, e);
8635     }
8636 };/*
8637  * Based on:
8638  * Ext JS Library 1.1.1
8639  * Copyright(c) 2006-2007, Ext JS, LLC.
8640  *
8641  * Originally Released Under LGPL - original licence link has changed is not relivant.
8642  *
8643  * Fork - LGPL
8644  * <script type="text/javascript">
8645  */
8646
8647 /**
8648  * @class Roo.Editor
8649  * @extends Roo.Component
8650  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8651  * @constructor
8652  * Create a new Editor
8653  * @param {Roo.form.Field} field The Field object (or descendant)
8654  * @param {Object} config The config object
8655  */
8656 Roo.Editor = function(field, config){
8657     Roo.Editor.superclass.constructor.call(this, config);
8658     this.field = field;
8659     this.addEvents({
8660         /**
8661              * @event beforestartedit
8662              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8663              * false from the handler of this event.
8664              * @param {Editor} this
8665              * @param {Roo.Element} boundEl The underlying element bound to this editor
8666              * @param {Mixed} value The field value being set
8667              */
8668         "beforestartedit" : true,
8669         /**
8670              * @event startedit
8671              * Fires when this editor is displayed
8672              * @param {Roo.Element} boundEl The underlying element bound to this editor
8673              * @param {Mixed} value The starting field value
8674              */
8675         "startedit" : true,
8676         /**
8677              * @event beforecomplete
8678              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8679              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8680              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8681              * event will not fire since no edit actually occurred.
8682              * @param {Editor} this
8683              * @param {Mixed} value The current field value
8684              * @param {Mixed} startValue The original field value
8685              */
8686         "beforecomplete" : true,
8687         /**
8688              * @event complete
8689              * Fires after editing is complete and any changed value has been written to the underlying field.
8690              * @param {Editor} this
8691              * @param {Mixed} value The current field value
8692              * @param {Mixed} startValue The original field value
8693              */
8694         "complete" : true,
8695         /**
8696          * @event specialkey
8697          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8698          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8699          * @param {Roo.form.Field} this
8700          * @param {Roo.EventObject} e The event object
8701          */
8702         "specialkey" : true
8703     });
8704 };
8705
8706 Roo.extend(Roo.Editor, Roo.Component, {
8707     /**
8708      * @cfg {Boolean/String} autosize
8709      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8710      * or "height" to adopt the height only (defaults to false)
8711      */
8712     /**
8713      * @cfg {Boolean} revertInvalid
8714      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8715      * validation fails (defaults to true)
8716      */
8717     /**
8718      * @cfg {Boolean} ignoreNoChange
8719      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8720      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8721      * will never be ignored.
8722      */
8723     /**
8724      * @cfg {Boolean} hideEl
8725      * False to keep the bound element visible while the editor is displayed (defaults to true)
8726      */
8727     /**
8728      * @cfg {Mixed} value
8729      * The data value of the underlying field (defaults to "")
8730      */
8731     value : "",
8732     /**
8733      * @cfg {String} alignment
8734      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8735      */
8736     alignment: "c-c?",
8737     /**
8738      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8739      * for bottom-right shadow (defaults to "frame")
8740      */
8741     shadow : "frame",
8742     /**
8743      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8744      */
8745     constrain : false,
8746     /**
8747      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8748      */
8749     completeOnEnter : false,
8750     /**
8751      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8752      */
8753     cancelOnEsc : false,
8754     /**
8755      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8756      */
8757     updateEl : false,
8758
8759     // private
8760     onRender : function(ct, position){
8761         this.el = new Roo.Layer({
8762             shadow: this.shadow,
8763             cls: "x-editor",
8764             parentEl : ct,
8765             shim : this.shim,
8766             shadowOffset:4,
8767             id: this.id,
8768             constrain: this.constrain
8769         });
8770         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8771         if(this.field.msgTarget != 'title'){
8772             this.field.msgTarget = 'qtip';
8773         }
8774         this.field.render(this.el);
8775         if(Roo.isGecko){
8776             this.field.el.dom.setAttribute('autocomplete', 'off');
8777         }
8778         this.field.on("specialkey", this.onSpecialKey, this);
8779         if(this.swallowKeys){
8780             this.field.el.swallowEvent(['keydown','keypress']);
8781         }
8782         this.field.show();
8783         this.field.on("blur", this.onBlur, this);
8784         if(this.field.grow){
8785             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8786         }
8787     },
8788
8789     onSpecialKey : function(field, e)
8790     {
8791         //Roo.log('editor onSpecialKey');
8792         if(this.completeOnEnter && e.getKey() == e.ENTER){
8793             e.stopEvent();
8794             this.completeEdit();
8795             return;
8796         }
8797         // do not fire special key otherwise it might hide close the editor...
8798         if(e.getKey() == e.ENTER){    
8799             return;
8800         }
8801         if(this.cancelOnEsc && e.getKey() == e.ESC){
8802             this.cancelEdit();
8803             return;
8804         } 
8805         this.fireEvent('specialkey', field, e);
8806     
8807     },
8808
8809     /**
8810      * Starts the editing process and shows the editor.
8811      * @param {String/HTMLElement/Element} el The element to edit
8812      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8813       * to the innerHTML of el.
8814      */
8815     startEdit : function(el, value){
8816         if(this.editing){
8817             this.completeEdit();
8818         }
8819         this.boundEl = Roo.get(el);
8820         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8821         if(!this.rendered){
8822             this.render(this.parentEl || document.body);
8823         }
8824         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8825             return;
8826         }
8827         this.startValue = v;
8828         this.field.setValue(v);
8829         if(this.autoSize){
8830             var sz = this.boundEl.getSize();
8831             switch(this.autoSize){
8832                 case "width":
8833                 this.setSize(sz.width,  "");
8834                 break;
8835                 case "height":
8836                 this.setSize("",  sz.height);
8837                 break;
8838                 default:
8839                 this.setSize(sz.width,  sz.height);
8840             }
8841         }
8842         this.el.alignTo(this.boundEl, this.alignment);
8843         this.editing = true;
8844         if(Roo.QuickTips){
8845             Roo.QuickTips.disable();
8846         }
8847         this.show();
8848     },
8849
8850     /**
8851      * Sets the height and width of this editor.
8852      * @param {Number} width The new width
8853      * @param {Number} height The new height
8854      */
8855     setSize : function(w, h){
8856         this.field.setSize(w, h);
8857         if(this.el){
8858             this.el.sync();
8859         }
8860     },
8861
8862     /**
8863      * Realigns the editor to the bound field based on the current alignment config value.
8864      */
8865     realign : function(){
8866         this.el.alignTo(this.boundEl, this.alignment);
8867     },
8868
8869     /**
8870      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8871      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8872      */
8873     completeEdit : function(remainVisible){
8874         if(!this.editing){
8875             return;
8876         }
8877         var v = this.getValue();
8878         if(this.revertInvalid !== false && !this.field.isValid()){
8879             v = this.startValue;
8880             this.cancelEdit(true);
8881         }
8882         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8883             this.editing = false;
8884             this.hide();
8885             return;
8886         }
8887         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8888             this.editing = false;
8889             if(this.updateEl && this.boundEl){
8890                 this.boundEl.update(v);
8891             }
8892             if(remainVisible !== true){
8893                 this.hide();
8894             }
8895             this.fireEvent("complete", this, v, this.startValue);
8896         }
8897     },
8898
8899     // private
8900     onShow : function(){
8901         this.el.show();
8902         if(this.hideEl !== false){
8903             this.boundEl.hide();
8904         }
8905         this.field.show();
8906         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8907             this.fixIEFocus = true;
8908             this.deferredFocus.defer(50, this);
8909         }else{
8910             this.field.focus();
8911         }
8912         this.fireEvent("startedit", this.boundEl, this.startValue);
8913     },
8914
8915     deferredFocus : function(){
8916         if(this.editing){
8917             this.field.focus();
8918         }
8919     },
8920
8921     /**
8922      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8923      * reverted to the original starting value.
8924      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8925      * cancel (defaults to false)
8926      */
8927     cancelEdit : function(remainVisible){
8928         if(this.editing){
8929             this.setValue(this.startValue);
8930             if(remainVisible !== true){
8931                 this.hide();
8932             }
8933         }
8934     },
8935
8936     // private
8937     onBlur : function(){
8938         if(this.allowBlur !== true && this.editing){
8939             this.completeEdit();
8940         }
8941     },
8942
8943     // private
8944     onHide : function(){
8945         if(this.editing){
8946             this.completeEdit();
8947             return;
8948         }
8949         this.field.blur();
8950         if(this.field.collapse){
8951             this.field.collapse();
8952         }
8953         this.el.hide();
8954         if(this.hideEl !== false){
8955             this.boundEl.show();
8956         }
8957         if(Roo.QuickTips){
8958             Roo.QuickTips.enable();
8959         }
8960     },
8961
8962     /**
8963      * Sets the data value of the editor
8964      * @param {Mixed} value Any valid value supported by the underlying field
8965      */
8966     setValue : function(v){
8967         this.field.setValue(v);
8968     },
8969
8970     /**
8971      * Gets the data value of the editor
8972      * @return {Mixed} The data value
8973      */
8974     getValue : function(){
8975         return this.field.getValue();
8976     }
8977 });/*
8978  * Based on:
8979  * Ext JS Library 1.1.1
8980  * Copyright(c) 2006-2007, Ext JS, LLC.
8981  *
8982  * Originally Released Under LGPL - original licence link has changed is not relivant.
8983  *
8984  * Fork - LGPL
8985  * <script type="text/javascript">
8986  */
8987  
8988 /**
8989  * @class Roo.BasicDialog
8990  * @extends Roo.util.Observable
8991  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8992  * <pre><code>
8993 var dlg = new Roo.BasicDialog("my-dlg", {
8994     height: 200,
8995     width: 300,
8996     minHeight: 100,
8997     minWidth: 150,
8998     modal: true,
8999     proxyDrag: true,
9000     shadow: true
9001 });
9002 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9003 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9004 dlg.addButton('Cancel', dlg.hide, dlg);
9005 dlg.show();
9006 </code></pre>
9007   <b>A Dialog should always be a direct child of the body element.</b>
9008  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9009  * @cfg {String} title Default text to display in the title bar (defaults to null)
9010  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9011  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9012  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9013  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9014  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9015  * (defaults to null with no animation)
9016  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9017  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9018  * property for valid values (defaults to 'all')
9019  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9020  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9021  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9022  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9023  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9024  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9025  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9026  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9027  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9028  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9029  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9030  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9031  * draggable = true (defaults to false)
9032  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9033  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9034  * shadow (defaults to false)
9035  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9036  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9037  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9038  * @cfg {Array} buttons Array of buttons
9039  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9040  * @constructor
9041  * Create a new BasicDialog.
9042  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9043  * @param {Object} config Configuration options
9044  */
9045 Roo.BasicDialog = function(el, config){
9046     this.el = Roo.get(el);
9047     var dh = Roo.DomHelper;
9048     if(!this.el && config && config.autoCreate){
9049         if(typeof config.autoCreate == "object"){
9050             if(!config.autoCreate.id){
9051                 config.autoCreate.id = el;
9052             }
9053             this.el = dh.append(document.body,
9054                         config.autoCreate, true);
9055         }else{
9056             this.el = dh.append(document.body,
9057                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9058         }
9059     }
9060     el = this.el;
9061     el.setDisplayed(true);
9062     el.hide = this.hideAction;
9063     this.id = el.id;
9064     el.addClass("x-dlg");
9065
9066     Roo.apply(this, config);
9067
9068     this.proxy = el.createProxy("x-dlg-proxy");
9069     this.proxy.hide = this.hideAction;
9070     this.proxy.setOpacity(.5);
9071     this.proxy.hide();
9072
9073     if(config.width){
9074         el.setWidth(config.width);
9075     }
9076     if(config.height){
9077         el.setHeight(config.height);
9078     }
9079     this.size = el.getSize();
9080     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9081         this.xy = [config.x,config.y];
9082     }else{
9083         this.xy = el.getCenterXY(true);
9084     }
9085     /** The header element @type Roo.Element */
9086     this.header = el.child("> .x-dlg-hd");
9087     /** The body element @type Roo.Element */
9088     this.body = el.child("> .x-dlg-bd");
9089     /** The footer element @type Roo.Element */
9090     this.footer = el.child("> .x-dlg-ft");
9091
9092     if(!this.header){
9093         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9094     }
9095     if(!this.body){
9096         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9097     }
9098
9099     this.header.unselectable();
9100     if(this.title){
9101         this.header.update(this.title);
9102     }
9103     // this element allows the dialog to be focused for keyboard event
9104     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9105     this.focusEl.swallowEvent("click", true);
9106
9107     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9108
9109     // wrap the body and footer for special rendering
9110     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9111     if(this.footer){
9112         this.bwrap.dom.appendChild(this.footer.dom);
9113     }
9114
9115     this.bg = this.el.createChild({
9116         tag: "div", cls:"x-dlg-bg",
9117         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9118     });
9119     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9120
9121
9122     if(this.autoScroll !== false && !this.autoTabs){
9123         this.body.setStyle("overflow", "auto");
9124     }
9125
9126     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9127
9128     if(this.closable !== false){
9129         this.el.addClass("x-dlg-closable");
9130         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9131         this.close.on("click", this.closeClick, this);
9132         this.close.addClassOnOver("x-dlg-close-over");
9133     }
9134     if(this.collapsible !== false){
9135         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9136         this.collapseBtn.on("click", this.collapseClick, this);
9137         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9138         this.header.on("dblclick", this.collapseClick, this);
9139     }
9140     if(this.resizable !== false){
9141         this.el.addClass("x-dlg-resizable");
9142         this.resizer = new Roo.Resizable(el, {
9143             minWidth: this.minWidth || 80,
9144             minHeight:this.minHeight || 80,
9145             handles: this.resizeHandles || "all",
9146             pinned: true
9147         });
9148         this.resizer.on("beforeresize", this.beforeResize, this);
9149         this.resizer.on("resize", this.onResize, this);
9150     }
9151     if(this.draggable !== false){
9152         el.addClass("x-dlg-draggable");
9153         if (!this.proxyDrag) {
9154             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9155         }
9156         else {
9157             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9158         }
9159         dd.setHandleElId(this.header.id);
9160         dd.endDrag = this.endMove.createDelegate(this);
9161         dd.startDrag = this.startMove.createDelegate(this);
9162         dd.onDrag = this.onDrag.createDelegate(this);
9163         dd.scroll = false;
9164         this.dd = dd;
9165     }
9166     if(this.modal){
9167         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9168         this.mask.enableDisplayMode("block");
9169         this.mask.hide();
9170         this.el.addClass("x-dlg-modal");
9171     }
9172     if(this.shadow){
9173         this.shadow = new Roo.Shadow({
9174             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9175             offset : this.shadowOffset
9176         });
9177     }else{
9178         this.shadowOffset = 0;
9179     }
9180     if(Roo.useShims && this.shim !== false){
9181         this.shim = this.el.createShim();
9182         this.shim.hide = this.hideAction;
9183         this.shim.hide();
9184     }else{
9185         this.shim = false;
9186     }
9187     if(this.autoTabs){
9188         this.initTabs();
9189     }
9190     if (this.buttons) { 
9191         var bts= this.buttons;
9192         this.buttons = [];
9193         Roo.each(bts, function(b) {
9194             this.addButton(b);
9195         }, this);
9196     }
9197     
9198     
9199     this.addEvents({
9200         /**
9201          * @event keydown
9202          * Fires when a key is pressed
9203          * @param {Roo.BasicDialog} this
9204          * @param {Roo.EventObject} e
9205          */
9206         "keydown" : true,
9207         /**
9208          * @event move
9209          * Fires when this dialog is moved by the user.
9210          * @param {Roo.BasicDialog} this
9211          * @param {Number} x The new page X
9212          * @param {Number} y The new page Y
9213          */
9214         "move" : true,
9215         /**
9216          * @event resize
9217          * Fires when this dialog is resized by the user.
9218          * @param {Roo.BasicDialog} this
9219          * @param {Number} width The new width
9220          * @param {Number} height The new height
9221          */
9222         "resize" : true,
9223         /**
9224          * @event beforehide
9225          * Fires before this dialog is hidden.
9226          * @param {Roo.BasicDialog} this
9227          */
9228         "beforehide" : true,
9229         /**
9230          * @event hide
9231          * Fires when this dialog is hidden.
9232          * @param {Roo.BasicDialog} this
9233          */
9234         "hide" : true,
9235         /**
9236          * @event beforeshow
9237          * Fires before this dialog is shown.
9238          * @param {Roo.BasicDialog} this
9239          */
9240         "beforeshow" : true,
9241         /**
9242          * @event show
9243          * Fires when this dialog is shown.
9244          * @param {Roo.BasicDialog} this
9245          */
9246         "show" : true
9247     });
9248     el.on("keydown", this.onKeyDown, this);
9249     el.on("mousedown", this.toFront, this);
9250     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9251     this.el.hide();
9252     Roo.DialogManager.register(this);
9253     Roo.BasicDialog.superclass.constructor.call(this);
9254 };
9255
9256 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9257     shadowOffset: Roo.isIE ? 6 : 5,
9258     minHeight: 80,
9259     minWidth: 200,
9260     minButtonWidth: 75,
9261     defaultButton: null,
9262     buttonAlign: "right",
9263     tabTag: 'div',
9264     firstShow: true,
9265
9266     /**
9267      * Sets the dialog title text
9268      * @param {String} text The title text to display
9269      * @return {Roo.BasicDialog} this
9270      */
9271     setTitle : function(text){
9272         this.header.update(text);
9273         return this;
9274     },
9275
9276     // private
9277     closeClick : function(){
9278         this.hide();
9279     },
9280
9281     // private
9282     collapseClick : function(){
9283         this[this.collapsed ? "expand" : "collapse"]();
9284     },
9285
9286     /**
9287      * Collapses the dialog to its minimized state (only the title bar is visible).
9288      * Equivalent to the user clicking the collapse dialog button.
9289      */
9290     collapse : function(){
9291         if(!this.collapsed){
9292             this.collapsed = true;
9293             this.el.addClass("x-dlg-collapsed");
9294             this.restoreHeight = this.el.getHeight();
9295             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9296         }
9297     },
9298
9299     /**
9300      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9301      * clicking the expand dialog button.
9302      */
9303     expand : function(){
9304         if(this.collapsed){
9305             this.collapsed = false;
9306             this.el.removeClass("x-dlg-collapsed");
9307             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9308         }
9309     },
9310
9311     /**
9312      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9313      * @return {Roo.TabPanel} The tabs component
9314      */
9315     initTabs : function(){
9316         var tabs = this.getTabs();
9317         while(tabs.getTab(0)){
9318             tabs.removeTab(0);
9319         }
9320         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9321             var dom = el.dom;
9322             tabs.addTab(Roo.id(dom), dom.title);
9323             dom.title = "";
9324         });
9325         tabs.activate(0);
9326         return tabs;
9327     },
9328
9329     // private
9330     beforeResize : function(){
9331         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9332     },
9333
9334     // private
9335     onResize : function(){
9336         this.refreshSize();
9337         this.syncBodyHeight();
9338         this.adjustAssets();
9339         this.focus();
9340         this.fireEvent("resize", this, this.size.width, this.size.height);
9341     },
9342
9343     // private
9344     onKeyDown : function(e){
9345         if(this.isVisible()){
9346             this.fireEvent("keydown", this, e);
9347         }
9348     },
9349
9350     /**
9351      * Resizes the dialog.
9352      * @param {Number} width
9353      * @param {Number} height
9354      * @return {Roo.BasicDialog} this
9355      */
9356     resizeTo : function(width, height){
9357         this.el.setSize(width, height);
9358         this.size = {width: width, height: height};
9359         this.syncBodyHeight();
9360         if(this.fixedcenter){
9361             this.center();
9362         }
9363         if(this.isVisible()){
9364             this.constrainXY();
9365             this.adjustAssets();
9366         }
9367         this.fireEvent("resize", this, width, height);
9368         return this;
9369     },
9370
9371
9372     /**
9373      * Resizes the dialog to fit the specified content size.
9374      * @param {Number} width
9375      * @param {Number} height
9376      * @return {Roo.BasicDialog} this
9377      */
9378     setContentSize : function(w, h){
9379         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9380         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9381         //if(!this.el.isBorderBox()){
9382             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9383             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9384         //}
9385         if(this.tabs){
9386             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9387             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9388         }
9389         this.resizeTo(w, h);
9390         return this;
9391     },
9392
9393     /**
9394      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9395      * executed in response to a particular key being pressed while the dialog is active.
9396      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9397      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9398      * @param {Function} fn The function to call
9399      * @param {Object} scope (optional) The scope of the function
9400      * @return {Roo.BasicDialog} this
9401      */
9402     addKeyListener : function(key, fn, scope){
9403         var keyCode, shift, ctrl, alt;
9404         if(typeof key == "object" && !(key instanceof Array)){
9405             keyCode = key["key"];
9406             shift = key["shift"];
9407             ctrl = key["ctrl"];
9408             alt = key["alt"];
9409         }else{
9410             keyCode = key;
9411         }
9412         var handler = function(dlg, e){
9413             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9414                 var k = e.getKey();
9415                 if(keyCode instanceof Array){
9416                     for(var i = 0, len = keyCode.length; i < len; i++){
9417                         if(keyCode[i] == k){
9418                           fn.call(scope || window, dlg, k, e);
9419                           return;
9420                         }
9421                     }
9422                 }else{
9423                     if(k == keyCode){
9424                         fn.call(scope || window, dlg, k, e);
9425                     }
9426                 }
9427             }
9428         };
9429         this.on("keydown", handler);
9430         return this;
9431     },
9432
9433     /**
9434      * Returns the TabPanel component (creates it if it doesn't exist).
9435      * Note: If you wish to simply check for the existence of tabs without creating them,
9436      * check for a null 'tabs' property.
9437      * @return {Roo.TabPanel} The tabs component
9438      */
9439     getTabs : function(){
9440         if(!this.tabs){
9441             this.el.addClass("x-dlg-auto-tabs");
9442             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9443             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9444         }
9445         return this.tabs;
9446     },
9447
9448     /**
9449      * Adds a button to the footer section of the dialog.
9450      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9451      * object or a valid Roo.DomHelper element config
9452      * @param {Function} handler The function called when the button is clicked
9453      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9454      * @return {Roo.Button} The new button
9455      */
9456     addButton : function(config, handler, scope){
9457         var dh = Roo.DomHelper;
9458         if(!this.footer){
9459             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9460         }
9461         if(!this.btnContainer){
9462             var tb = this.footer.createChild({
9463
9464                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9465                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9466             }, null, true);
9467             this.btnContainer = tb.firstChild.firstChild.firstChild;
9468         }
9469         var bconfig = {
9470             handler: handler,
9471             scope: scope,
9472             minWidth: this.minButtonWidth,
9473             hideParent:true
9474         };
9475         if(typeof config == "string"){
9476             bconfig.text = config;
9477         }else{
9478             if(config.tag){
9479                 bconfig.dhconfig = config;
9480             }else{
9481                 Roo.apply(bconfig, config);
9482             }
9483         }
9484         var fc = false;
9485         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9486             bconfig.position = Math.max(0, bconfig.position);
9487             fc = this.btnContainer.childNodes[bconfig.position];
9488         }
9489          
9490         var btn = new Roo.Button(
9491             fc ? 
9492                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9493                 : this.btnContainer.appendChild(document.createElement("td")),
9494             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9495             bconfig
9496         );
9497         this.syncBodyHeight();
9498         if(!this.buttons){
9499             /**
9500              * Array of all the buttons that have been added to this dialog via addButton
9501              * @type Array
9502              */
9503             this.buttons = [];
9504         }
9505         this.buttons.push(btn);
9506         return btn;
9507     },
9508
9509     /**
9510      * Sets the default button to be focused when the dialog is displayed.
9511      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9512      * @return {Roo.BasicDialog} this
9513      */
9514     setDefaultButton : function(btn){
9515         this.defaultButton = btn;
9516         return this;
9517     },
9518
9519     // private
9520     getHeaderFooterHeight : function(safe){
9521         var height = 0;
9522         if(this.header){
9523            height += this.header.getHeight();
9524         }
9525         if(this.footer){
9526            var fm = this.footer.getMargins();
9527             height += (this.footer.getHeight()+fm.top+fm.bottom);
9528         }
9529         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9530         height += this.centerBg.getPadding("tb");
9531         return height;
9532     },
9533
9534     // private
9535     syncBodyHeight : function()
9536     {
9537         var bd = this.body, // the text
9538             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9539             bw = this.bwrap;
9540         var height = this.size.height - this.getHeaderFooterHeight(false);
9541         bd.setHeight(height-bd.getMargins("tb"));
9542         var hh = this.header.getHeight();
9543         var h = this.size.height-hh;
9544         cb.setHeight(h);
9545         
9546         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9547         bw.setHeight(h-cb.getPadding("tb"));
9548         
9549         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9550         bd.setWidth(bw.getWidth(true));
9551         if(this.tabs){
9552             this.tabs.syncHeight();
9553             if(Roo.isIE){
9554                 this.tabs.el.repaint();
9555             }
9556         }
9557     },
9558
9559     /**
9560      * Restores the previous state of the dialog if Roo.state is configured.
9561      * @return {Roo.BasicDialog} this
9562      */
9563     restoreState : function(){
9564         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9565         if(box && box.width){
9566             this.xy = [box.x, box.y];
9567             this.resizeTo(box.width, box.height);
9568         }
9569         return this;
9570     },
9571
9572     // private
9573     beforeShow : function(){
9574         this.expand();
9575         if(this.fixedcenter){
9576             this.xy = this.el.getCenterXY(true);
9577         }
9578         if(this.modal){
9579             Roo.get(document.body).addClass("x-body-masked");
9580             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9581             this.mask.show();
9582         }
9583         this.constrainXY();
9584     },
9585
9586     // private
9587     animShow : function(){
9588         var b = Roo.get(this.animateTarget).getBox();
9589         this.proxy.setSize(b.width, b.height);
9590         this.proxy.setLocation(b.x, b.y);
9591         this.proxy.show();
9592         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9593                     true, .35, this.showEl.createDelegate(this));
9594     },
9595
9596     /**
9597      * Shows the dialog.
9598      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9599      * @return {Roo.BasicDialog} this
9600      */
9601     show : function(animateTarget){
9602         if (this.fireEvent("beforeshow", this) === false){
9603             return;
9604         }
9605         if(this.syncHeightBeforeShow){
9606             this.syncBodyHeight();
9607         }else if(this.firstShow){
9608             this.firstShow = false;
9609             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9610         }
9611         this.animateTarget = animateTarget || this.animateTarget;
9612         if(!this.el.isVisible()){
9613             this.beforeShow();
9614             if(this.animateTarget && Roo.get(this.animateTarget)){
9615                 this.animShow();
9616             }else{
9617                 this.showEl();
9618             }
9619         }
9620         return this;
9621     },
9622
9623     // private
9624     showEl : function(){
9625         this.proxy.hide();
9626         this.el.setXY(this.xy);
9627         this.el.show();
9628         this.adjustAssets(true);
9629         this.toFront();
9630         this.focus();
9631         // IE peekaboo bug - fix found by Dave Fenwick
9632         if(Roo.isIE){
9633             this.el.repaint();
9634         }
9635         this.fireEvent("show", this);
9636     },
9637
9638     /**
9639      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9640      * dialog itself will receive focus.
9641      */
9642     focus : function(){
9643         if(this.defaultButton){
9644             this.defaultButton.focus();
9645         }else{
9646             this.focusEl.focus();
9647         }
9648     },
9649
9650     // private
9651     constrainXY : function(){
9652         if(this.constraintoviewport !== false){
9653             if(!this.viewSize){
9654                 if(this.container){
9655                     var s = this.container.getSize();
9656                     this.viewSize = [s.width, s.height];
9657                 }else{
9658                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9659                 }
9660             }
9661             var s = Roo.get(this.container||document).getScroll();
9662
9663             var x = this.xy[0], y = this.xy[1];
9664             var w = this.size.width, h = this.size.height;
9665             var vw = this.viewSize[0], vh = this.viewSize[1];
9666             // only move it if it needs it
9667             var moved = false;
9668             // first validate right/bottom
9669             if(x + w > vw+s.left){
9670                 x = vw - w;
9671                 moved = true;
9672             }
9673             if(y + h > vh+s.top){
9674                 y = vh - h;
9675                 moved = true;
9676             }
9677             // then make sure top/left isn't negative
9678             if(x < s.left){
9679                 x = s.left;
9680                 moved = true;
9681             }
9682             if(y < s.top){
9683                 y = s.top;
9684                 moved = true;
9685             }
9686             if(moved){
9687                 // cache xy
9688                 this.xy = [x, y];
9689                 if(this.isVisible()){
9690                     this.el.setLocation(x, y);
9691                     this.adjustAssets();
9692                 }
9693             }
9694         }
9695     },
9696
9697     // private
9698     onDrag : function(){
9699         if(!this.proxyDrag){
9700             this.xy = this.el.getXY();
9701             this.adjustAssets();
9702         }
9703     },
9704
9705     // private
9706     adjustAssets : function(doShow){
9707         var x = this.xy[0], y = this.xy[1];
9708         var w = this.size.width, h = this.size.height;
9709         if(doShow === true){
9710             if(this.shadow){
9711                 this.shadow.show(this.el);
9712             }
9713             if(this.shim){
9714                 this.shim.show();
9715             }
9716         }
9717         if(this.shadow && this.shadow.isVisible()){
9718             this.shadow.show(this.el);
9719         }
9720         if(this.shim && this.shim.isVisible()){
9721             this.shim.setBounds(x, y, w, h);
9722         }
9723     },
9724
9725     // private
9726     adjustViewport : function(w, h){
9727         if(!w || !h){
9728             w = Roo.lib.Dom.getViewWidth();
9729             h = Roo.lib.Dom.getViewHeight();
9730         }
9731         // cache the size
9732         this.viewSize = [w, h];
9733         if(this.modal && this.mask.isVisible()){
9734             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9735             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9736         }
9737         if(this.isVisible()){
9738             this.constrainXY();
9739         }
9740     },
9741
9742     /**
9743      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9744      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9745      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9746      */
9747     destroy : function(removeEl){
9748         if(this.isVisible()){
9749             this.animateTarget = null;
9750             this.hide();
9751         }
9752         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9753         if(this.tabs){
9754             this.tabs.destroy(removeEl);
9755         }
9756         Roo.destroy(
9757              this.shim,
9758              this.proxy,
9759              this.resizer,
9760              this.close,
9761              this.mask
9762         );
9763         if(this.dd){
9764             this.dd.unreg();
9765         }
9766         if(this.buttons){
9767            for(var i = 0, len = this.buttons.length; i < len; i++){
9768                this.buttons[i].destroy();
9769            }
9770         }
9771         this.el.removeAllListeners();
9772         if(removeEl === true){
9773             this.el.update("");
9774             this.el.remove();
9775         }
9776         Roo.DialogManager.unregister(this);
9777     },
9778
9779     // private
9780     startMove : function(){
9781         if(this.proxyDrag){
9782             this.proxy.show();
9783         }
9784         if(this.constraintoviewport !== false){
9785             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9786         }
9787     },
9788
9789     // private
9790     endMove : function(){
9791         if(!this.proxyDrag){
9792             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9793         }else{
9794             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9795             this.proxy.hide();
9796         }
9797         this.refreshSize();
9798         this.adjustAssets();
9799         this.focus();
9800         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9801     },
9802
9803     /**
9804      * Brings this dialog to the front of any other visible dialogs
9805      * @return {Roo.BasicDialog} this
9806      */
9807     toFront : function(){
9808         Roo.DialogManager.bringToFront(this);
9809         return this;
9810     },
9811
9812     /**
9813      * Sends this dialog to the back (under) of any other visible dialogs
9814      * @return {Roo.BasicDialog} this
9815      */
9816     toBack : function(){
9817         Roo.DialogManager.sendToBack(this);
9818         return this;
9819     },
9820
9821     /**
9822      * Centers this dialog in the viewport
9823      * @return {Roo.BasicDialog} this
9824      */
9825     center : function(){
9826         var xy = this.el.getCenterXY(true);
9827         this.moveTo(xy[0], xy[1]);
9828         return this;
9829     },
9830
9831     /**
9832      * Moves the dialog's top-left corner to the specified point
9833      * @param {Number} x
9834      * @param {Number} y
9835      * @return {Roo.BasicDialog} this
9836      */
9837     moveTo : function(x, y){
9838         this.xy = [x,y];
9839         if(this.isVisible()){
9840             this.el.setXY(this.xy);
9841             this.adjustAssets();
9842         }
9843         return this;
9844     },
9845
9846     /**
9847      * Aligns the dialog to the specified element
9848      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9849      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9850      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9851      * @return {Roo.BasicDialog} this
9852      */
9853     alignTo : function(element, position, offsets){
9854         this.xy = this.el.getAlignToXY(element, position, offsets);
9855         if(this.isVisible()){
9856             this.el.setXY(this.xy);
9857             this.adjustAssets();
9858         }
9859         return this;
9860     },
9861
9862     /**
9863      * Anchors an element to another element and realigns it when the window is resized.
9864      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9865      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9866      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9867      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9868      * is a number, it is used as the buffer delay (defaults to 50ms).
9869      * @return {Roo.BasicDialog} this
9870      */
9871     anchorTo : function(el, alignment, offsets, monitorScroll){
9872         var action = function(){
9873             this.alignTo(el, alignment, offsets);
9874         };
9875         Roo.EventManager.onWindowResize(action, this);
9876         var tm = typeof monitorScroll;
9877         if(tm != 'undefined'){
9878             Roo.EventManager.on(window, 'scroll', action, this,
9879                 {buffer: tm == 'number' ? monitorScroll : 50});
9880         }
9881         action.call(this);
9882         return this;
9883     },
9884
9885     /**
9886      * Returns true if the dialog is visible
9887      * @return {Boolean}
9888      */
9889     isVisible : function(){
9890         return this.el.isVisible();
9891     },
9892
9893     // private
9894     animHide : function(callback){
9895         var b = Roo.get(this.animateTarget).getBox();
9896         this.proxy.show();
9897         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9898         this.el.hide();
9899         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9900                     this.hideEl.createDelegate(this, [callback]));
9901     },
9902
9903     /**
9904      * Hides the dialog.
9905      * @param {Function} callback (optional) Function to call when the dialog is hidden
9906      * @return {Roo.BasicDialog} this
9907      */
9908     hide : function(callback){
9909         if (this.fireEvent("beforehide", this) === false){
9910             return;
9911         }
9912         if(this.shadow){
9913             this.shadow.hide();
9914         }
9915         if(this.shim) {
9916           this.shim.hide();
9917         }
9918         // sometimes animateTarget seems to get set.. causing problems...
9919         // this just double checks..
9920         if(this.animateTarget && Roo.get(this.animateTarget)) {
9921            this.animHide(callback);
9922         }else{
9923             this.el.hide();
9924             this.hideEl(callback);
9925         }
9926         return this;
9927     },
9928
9929     // private
9930     hideEl : function(callback){
9931         this.proxy.hide();
9932         if(this.modal){
9933             this.mask.hide();
9934             Roo.get(document.body).removeClass("x-body-masked");
9935         }
9936         this.fireEvent("hide", this);
9937         if(typeof callback == "function"){
9938             callback();
9939         }
9940     },
9941
9942     // private
9943     hideAction : function(){
9944         this.setLeft("-10000px");
9945         this.setTop("-10000px");
9946         this.setStyle("visibility", "hidden");
9947     },
9948
9949     // private
9950     refreshSize : function(){
9951         this.size = this.el.getSize();
9952         this.xy = this.el.getXY();
9953         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9954     },
9955
9956     // private
9957     // z-index is managed by the DialogManager and may be overwritten at any time
9958     setZIndex : function(index){
9959         if(this.modal){
9960             this.mask.setStyle("z-index", index);
9961         }
9962         if(this.shim){
9963             this.shim.setStyle("z-index", ++index);
9964         }
9965         if(this.shadow){
9966             this.shadow.setZIndex(++index);
9967         }
9968         this.el.setStyle("z-index", ++index);
9969         if(this.proxy){
9970             this.proxy.setStyle("z-index", ++index);
9971         }
9972         if(this.resizer){
9973             this.resizer.proxy.setStyle("z-index", ++index);
9974         }
9975
9976         this.lastZIndex = index;
9977     },
9978
9979     /**
9980      * Returns the element for this dialog
9981      * @return {Roo.Element} The underlying dialog Element
9982      */
9983     getEl : function(){
9984         return this.el;
9985     }
9986 });
9987
9988 /**
9989  * @class Roo.DialogManager
9990  * Provides global access to BasicDialogs that have been created and
9991  * support for z-indexing (layering) multiple open dialogs.
9992  */
9993 Roo.DialogManager = function(){
9994     var list = {};
9995     var accessList = [];
9996     var front = null;
9997
9998     // private
9999     var sortDialogs = function(d1, d2){
10000         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10001     };
10002
10003     // private
10004     var orderDialogs = function(){
10005         accessList.sort(sortDialogs);
10006         var seed = Roo.DialogManager.zseed;
10007         for(var i = 0, len = accessList.length; i < len; i++){
10008             var dlg = accessList[i];
10009             if(dlg){
10010                 dlg.setZIndex(seed + (i*10));
10011             }
10012         }
10013     };
10014
10015     return {
10016         /**
10017          * The starting z-index for BasicDialogs (defaults to 9000)
10018          * @type Number The z-index value
10019          */
10020         zseed : 9000,
10021
10022         // private
10023         register : function(dlg){
10024             list[dlg.id] = dlg;
10025             accessList.push(dlg);
10026         },
10027
10028         // private
10029         unregister : function(dlg){
10030             delete list[dlg.id];
10031             var i=0;
10032             var len=0;
10033             if(!accessList.indexOf){
10034                 for(  i = 0, len = accessList.length; i < len; i++){
10035                     if(accessList[i] == dlg){
10036                         accessList.splice(i, 1);
10037                         return;
10038                     }
10039                 }
10040             }else{
10041                  i = accessList.indexOf(dlg);
10042                 if(i != -1){
10043                     accessList.splice(i, 1);
10044                 }
10045             }
10046         },
10047
10048         /**
10049          * Gets a registered dialog by id
10050          * @param {String/Object} id The id of the dialog or a dialog
10051          * @return {Roo.BasicDialog} this
10052          */
10053         get : function(id){
10054             return typeof id == "object" ? id : list[id];
10055         },
10056
10057         /**
10058          * Brings the specified dialog to the front
10059          * @param {String/Object} dlg The id of the dialog or a dialog
10060          * @return {Roo.BasicDialog} this
10061          */
10062         bringToFront : function(dlg){
10063             dlg = this.get(dlg);
10064             if(dlg != front){
10065                 front = dlg;
10066                 dlg._lastAccess = new Date().getTime();
10067                 orderDialogs();
10068             }
10069             return dlg;
10070         },
10071
10072         /**
10073          * Sends the specified dialog to the back
10074          * @param {String/Object} dlg The id of the dialog or a dialog
10075          * @return {Roo.BasicDialog} this
10076          */
10077         sendToBack : function(dlg){
10078             dlg = this.get(dlg);
10079             dlg._lastAccess = -(new Date().getTime());
10080             orderDialogs();
10081             return dlg;
10082         },
10083
10084         /**
10085          * Hides all dialogs
10086          */
10087         hideAll : function(){
10088             for(var id in list){
10089                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10090                     list[id].hide();
10091                 }
10092             }
10093         }
10094     };
10095 }();
10096
10097 /**
10098  * @class Roo.LayoutDialog
10099  * @extends Roo.BasicDialog
10100  * @children Roo.ContentPanel
10101  * @builder-top
10102  * Dialog which provides adjustments for working with a layout in a Dialog.
10103  * Add your necessary layout config options to the dialog's config.<br>
10104  * Example usage (including a nested layout):
10105  * <pre><code>
10106 if(!dialog){
10107     dialog = new Roo.LayoutDialog("download-dlg", {
10108         modal: true,
10109         width:600,
10110         height:450,
10111         shadow:true,
10112         minWidth:500,
10113         minHeight:350,
10114         autoTabs:true,
10115         proxyDrag:true,
10116         // layout config merges with the dialog config
10117         center:{
10118             tabPosition: "top",
10119             alwaysShowTabs: true
10120         }
10121     });
10122     dialog.addKeyListener(27, dialog.hide, dialog);
10123     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10124     dialog.addButton("Build It!", this.getDownload, this);
10125
10126     // we can even add nested layouts
10127     var innerLayout = new Roo.BorderLayout("dl-inner", {
10128         east: {
10129             initialSize: 200,
10130             autoScroll:true,
10131             split:true
10132         },
10133         center: {
10134             autoScroll:true
10135         }
10136     });
10137     innerLayout.beginUpdate();
10138     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10139     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10140     innerLayout.endUpdate(true);
10141
10142     var layout = dialog.getLayout();
10143     layout.beginUpdate();
10144     layout.add("center", new Roo.ContentPanel("standard-panel",
10145                         {title: "Download the Source", fitToFrame:true}));
10146     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10147                {title: "Build your own roo.js"}));
10148     layout.getRegion("center").showPanel(sp);
10149     layout.endUpdate();
10150 }
10151 </code></pre>
10152     * @constructor
10153     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10154     * @param {Object} config configuration options
10155   */
10156 Roo.LayoutDialog = function(el, cfg){
10157     
10158     var config=  cfg;
10159     if (typeof(cfg) == 'undefined') {
10160         config = Roo.apply({}, el);
10161         // not sure why we use documentElement here.. - it should always be body.
10162         // IE7 borks horribly if we use documentElement.
10163         // webkit also does not like documentElement - it creates a body element...
10164         el = Roo.get( document.body || document.documentElement ).createChild();
10165         //config.autoCreate = true;
10166     }
10167     
10168     
10169     config.autoTabs = false;
10170     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10171     this.body.setStyle({overflow:"hidden", position:"relative"});
10172     this.layout = new Roo.BorderLayout(this.body.dom, config);
10173     this.layout.monitorWindowResize = false;
10174     this.el.addClass("x-dlg-auto-layout");
10175     // fix case when center region overwrites center function
10176     this.center = Roo.BasicDialog.prototype.center;
10177     this.on("show", this.layout.layout, this.layout, true);
10178     if (config.items) {
10179         var xitems = config.items;
10180         delete config.items;
10181         Roo.each(xitems, this.addxtype, this);
10182     }
10183     
10184     
10185 };
10186 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10187     
10188     
10189     /**
10190      * @cfg {Roo.LayoutRegion} east  
10191      */
10192     /**
10193      * @cfg {Roo.LayoutRegion} west
10194      */
10195     /**
10196      * @cfg {Roo.LayoutRegion} south
10197      */
10198     /**
10199      * @cfg {Roo.LayoutRegion} north
10200      */
10201     /**
10202      * @cfg {Roo.LayoutRegion} center
10203      */
10204     /**
10205      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10206      */
10207     
10208     
10209     /**
10210      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10211      * @deprecated
10212      */
10213     endUpdate : function(){
10214         this.layout.endUpdate();
10215     },
10216
10217     /**
10218      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10219      *  @deprecated
10220      */
10221     beginUpdate : function(){
10222         this.layout.beginUpdate();
10223     },
10224
10225     /**
10226      * Get the BorderLayout for this dialog
10227      * @return {Roo.BorderLayout}
10228      */
10229     getLayout : function(){
10230         return this.layout;
10231     },
10232
10233     showEl : function(){
10234         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10235         if(Roo.isIE7){
10236             this.layout.layout();
10237         }
10238     },
10239
10240     // private
10241     // Use the syncHeightBeforeShow config option to control this automatically
10242     syncBodyHeight : function(){
10243         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10244         if(this.layout){this.layout.layout();}
10245     },
10246     
10247       /**
10248      * Add an xtype element (actually adds to the layout.)
10249      * @return {Object} xdata xtype object data.
10250      */
10251     
10252     addxtype : function(c) {
10253         return this.layout.addxtype(c);
10254     }
10255 });/*
10256  * Based on:
10257  * Ext JS Library 1.1.1
10258  * Copyright(c) 2006-2007, Ext JS, LLC.
10259  *
10260  * Originally Released Under LGPL - original licence link has changed is not relivant.
10261  *
10262  * Fork - LGPL
10263  * <script type="text/javascript">
10264  */
10265  
10266 /**
10267  * @class Roo.MessageBox
10268  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10269  * Example usage:
10270  *<pre><code>
10271 // Basic alert:
10272 Roo.Msg.alert('Status', 'Changes saved successfully.');
10273
10274 // Prompt for user data:
10275 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10276     if (btn == 'ok'){
10277         // process text value...
10278     }
10279 });
10280
10281 // Show a dialog using config options:
10282 Roo.Msg.show({
10283    title:'Save Changes?',
10284    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10285    buttons: Roo.Msg.YESNOCANCEL,
10286    fn: processResult,
10287    animEl: 'elId'
10288 });
10289 </code></pre>
10290  * @singleton
10291  */
10292 Roo.MessageBox = function(){
10293     var dlg, opt, mask, waitTimer;
10294     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10295     var buttons, activeTextEl, bwidth;
10296
10297     // private
10298     var handleButton = function(button){
10299         dlg.hide();
10300         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10301     };
10302
10303     // private
10304     var handleHide = function(){
10305         if(opt && opt.cls){
10306             dlg.el.removeClass(opt.cls);
10307         }
10308         if(waitTimer){
10309             Roo.TaskMgr.stop(waitTimer);
10310             waitTimer = null;
10311         }
10312     };
10313
10314     // private
10315     var updateButtons = function(b){
10316         var width = 0;
10317         if(!b){
10318             buttons["ok"].hide();
10319             buttons["cancel"].hide();
10320             buttons["yes"].hide();
10321             buttons["no"].hide();
10322             dlg.footer.dom.style.display = 'none';
10323             return width;
10324         }
10325         dlg.footer.dom.style.display = '';
10326         for(var k in buttons){
10327             if(typeof buttons[k] != "function"){
10328                 if(b[k]){
10329                     buttons[k].show();
10330                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10331                     width += buttons[k].el.getWidth()+15;
10332                 }else{
10333                     buttons[k].hide();
10334                 }
10335             }
10336         }
10337         return width;
10338     };
10339
10340     // private
10341     var handleEsc = function(d, k, e){
10342         if(opt && opt.closable !== false){
10343             dlg.hide();
10344         }
10345         if(e){
10346             e.stopEvent();
10347         }
10348     };
10349
10350     return {
10351         /**
10352          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10353          * @return {Roo.BasicDialog} The BasicDialog element
10354          */
10355         getDialog : function(){
10356            if(!dlg){
10357                 dlg = new Roo.BasicDialog("x-msg-box", {
10358                     autoCreate : true,
10359                     shadow: true,
10360                     draggable: true,
10361                     resizable:false,
10362                     constraintoviewport:false,
10363                     fixedcenter:true,
10364                     collapsible : false,
10365                     shim:true,
10366                     modal: true,
10367                     width:400, height:100,
10368                     buttonAlign:"center",
10369                     closeClick : function(){
10370                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10371                             handleButton("no");
10372                         }else{
10373                             handleButton("cancel");
10374                         }
10375                     }
10376                 });
10377                 dlg.on("hide", handleHide);
10378                 mask = dlg.mask;
10379                 dlg.addKeyListener(27, handleEsc);
10380                 buttons = {};
10381                 var bt = this.buttonText;
10382                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10383                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10384                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10385                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10386                 bodyEl = dlg.body.createChild({
10387
10388                     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>'
10389                 });
10390                 msgEl = bodyEl.dom.firstChild;
10391                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10392                 textboxEl.enableDisplayMode();
10393                 textboxEl.addKeyListener([10,13], function(){
10394                     if(dlg.isVisible() && opt && opt.buttons){
10395                         if(opt.buttons.ok){
10396                             handleButton("ok");
10397                         }else if(opt.buttons.yes){
10398                             handleButton("yes");
10399                         }
10400                     }
10401                 });
10402                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10403                 textareaEl.enableDisplayMode();
10404                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10405                 progressEl.enableDisplayMode();
10406                 var pf = progressEl.dom.firstChild;
10407                 if (pf) {
10408                     pp = Roo.get(pf.firstChild);
10409                     pp.setHeight(pf.offsetHeight);
10410                 }
10411                 
10412             }
10413             return dlg;
10414         },
10415
10416         /**
10417          * Updates the message box body text
10418          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10419          * the XHTML-compliant non-breaking space character '&amp;#160;')
10420          * @return {Roo.MessageBox} This message box
10421          */
10422         updateText : function(text){
10423             if(!dlg.isVisible() && !opt.width){
10424                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10425             }
10426             msgEl.innerHTML = text || '&#160;';
10427       
10428             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10429             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10430             var w = Math.max(
10431                     Math.min(opt.width || cw , this.maxWidth), 
10432                     Math.max(opt.minWidth || this.minWidth, bwidth)
10433             );
10434             if(opt.prompt){
10435                 activeTextEl.setWidth(w);
10436             }
10437             if(dlg.isVisible()){
10438                 dlg.fixedcenter = false;
10439             }
10440             // to big, make it scroll. = But as usual stupid IE does not support
10441             // !important..
10442             
10443             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10444                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10445                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10446             } else {
10447                 bodyEl.dom.style.height = '';
10448                 bodyEl.dom.style.overflowY = '';
10449             }
10450             if (cw > w) {
10451                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10452             } else {
10453                 bodyEl.dom.style.overflowX = '';
10454             }
10455             
10456             dlg.setContentSize(w, bodyEl.getHeight());
10457             if(dlg.isVisible()){
10458                 dlg.fixedcenter = true;
10459             }
10460             return this;
10461         },
10462
10463         /**
10464          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10465          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10466          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10467          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10468          * @return {Roo.MessageBox} This message box
10469          */
10470         updateProgress : function(value, text){
10471             if(text){
10472                 this.updateText(text);
10473             }
10474             if (pp) { // weird bug on my firefox - for some reason this is not defined
10475                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10476             }
10477             return this;
10478         },        
10479
10480         /**
10481          * Returns true if the message box is currently displayed
10482          * @return {Boolean} True if the message box is visible, else false
10483          */
10484         isVisible : function(){
10485             return dlg && dlg.isVisible();  
10486         },
10487
10488         /**
10489          * Hides the message box if it is displayed
10490          */
10491         hide : function(){
10492             if(this.isVisible()){
10493                 dlg.hide();
10494             }  
10495         },
10496
10497         /**
10498          * Displays a new message box, or reinitializes an existing message box, based on the config options
10499          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10500          * The following config object properties are supported:
10501          * <pre>
10502 Property    Type             Description
10503 ----------  ---------------  ------------------------------------------------------------------------------------
10504 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10505                                    closes (defaults to undefined)
10506 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10507                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10508 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10509                                    progress and wait dialogs will ignore this property and always hide the
10510                                    close button as they can only be closed programmatically.
10511 cls               String           A custom CSS class to apply to the message box element
10512 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10513                                    displayed (defaults to 75)
10514 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10515                                    function will be btn (the name of the button that was clicked, if applicable,
10516                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10517                                    Progress and wait dialogs will ignore this option since they do not respond to
10518                                    user actions and can only be closed programmatically, so any required function
10519                                    should be called by the same code after it closes the dialog.
10520 icon              String           A CSS class that provides a background image to be used as an icon for
10521                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10522 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10523 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10524 modal             Boolean          False to allow user interaction with the page while the message box is
10525                                    displayed (defaults to true)
10526 msg               String           A string that will replace the existing message box body text (defaults
10527                                    to the XHTML-compliant non-breaking space character '&#160;')
10528 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10529 progress          Boolean          True to display a progress bar (defaults to false)
10530 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10531 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10532 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10533 title             String           The title text
10534 value             String           The string value to set into the active textbox element if displayed
10535 wait              Boolean          True to display a progress bar (defaults to false)
10536 width             Number           The width of the dialog in pixels
10537 </pre>
10538          *
10539          * Example usage:
10540          * <pre><code>
10541 Roo.Msg.show({
10542    title: 'Address',
10543    msg: 'Please enter your address:',
10544    width: 300,
10545    buttons: Roo.MessageBox.OKCANCEL,
10546    multiline: true,
10547    fn: saveAddress,
10548    animEl: 'addAddressBtn'
10549 });
10550 </code></pre>
10551          * @param {Object} config Configuration options
10552          * @return {Roo.MessageBox} This message box
10553          */
10554         show : function(options)
10555         {
10556             
10557             // this causes nightmares if you show one dialog after another
10558             // especially on callbacks..
10559              
10560             if(this.isVisible()){
10561                 
10562                 this.hide();
10563                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10564                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10565                 Roo.log("New Dialog Message:" +  options.msg )
10566                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10567                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10568                 
10569             }
10570             var d = this.getDialog();
10571             opt = options;
10572             d.setTitle(opt.title || "&#160;");
10573             d.close.setDisplayed(opt.closable !== false);
10574             activeTextEl = textboxEl;
10575             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10576             if(opt.prompt){
10577                 if(opt.multiline){
10578                     textboxEl.hide();
10579                     textareaEl.show();
10580                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10581                         opt.multiline : this.defaultTextHeight);
10582                     activeTextEl = textareaEl;
10583                 }else{
10584                     textboxEl.show();
10585                     textareaEl.hide();
10586                 }
10587             }else{
10588                 textboxEl.hide();
10589                 textareaEl.hide();
10590             }
10591             progressEl.setDisplayed(opt.progress === true);
10592             this.updateProgress(0);
10593             activeTextEl.dom.value = opt.value || "";
10594             if(opt.prompt){
10595                 dlg.setDefaultButton(activeTextEl);
10596             }else{
10597                 var bs = opt.buttons;
10598                 var db = null;
10599                 if(bs && bs.ok){
10600                     db = buttons["ok"];
10601                 }else if(bs && bs.yes){
10602                     db = buttons["yes"];
10603                 }
10604                 dlg.setDefaultButton(db);
10605             }
10606             bwidth = updateButtons(opt.buttons);
10607             this.updateText(opt.msg);
10608             if(opt.cls){
10609                 d.el.addClass(opt.cls);
10610             }
10611             d.proxyDrag = opt.proxyDrag === true;
10612             d.modal = opt.modal !== false;
10613             d.mask = opt.modal !== false ? mask : false;
10614             if(!d.isVisible()){
10615                 // force it to the end of the z-index stack so it gets a cursor in FF
10616                 document.body.appendChild(dlg.el.dom);
10617                 d.animateTarget = null;
10618                 d.show(options.animEl);
10619             }
10620             return this;
10621         },
10622
10623         /**
10624          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10625          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10626          * and closing the message box when the process is complete.
10627          * @param {String} title The title bar text
10628          * @param {String} msg The message box body text
10629          * @return {Roo.MessageBox} This message box
10630          */
10631         progress : function(title, msg){
10632             this.show({
10633                 title : title,
10634                 msg : msg,
10635                 buttons: false,
10636                 progress:true,
10637                 closable:false,
10638                 minWidth: this.minProgressWidth,
10639                 modal : true
10640             });
10641             return this;
10642         },
10643
10644         /**
10645          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10646          * If a callback function is passed it will be called after the user clicks the button, and the
10647          * id of the button that was clicked will be passed as the only parameter to the callback
10648          * (could also be the top-right close button).
10649          * @param {String} title The title bar text
10650          * @param {String} msg The message box body text
10651          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10652          * @param {Object} scope (optional) The scope of the callback function
10653          * @return {Roo.MessageBox} This message box
10654          */
10655         alert : function(title, msg, fn, scope){
10656             this.show({
10657                 title : title,
10658                 msg : msg,
10659                 buttons: this.OK,
10660                 fn: fn,
10661                 scope : scope,
10662                 modal : true
10663             });
10664             return this;
10665         },
10666
10667         /**
10668          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10669          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10670          * You are responsible for closing the message box when the process is complete.
10671          * @param {String} msg The message box body text
10672          * @param {String} title (optional) The title bar text
10673          * @return {Roo.MessageBox} This message box
10674          */
10675         wait : function(msg, title){
10676             this.show({
10677                 title : title,
10678                 msg : msg,
10679                 buttons: false,
10680                 closable:false,
10681                 progress:true,
10682                 modal:true,
10683                 width:300,
10684                 wait:true
10685             });
10686             waitTimer = Roo.TaskMgr.start({
10687                 run: function(i){
10688                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10689                 },
10690                 interval: 1000
10691             });
10692             return this;
10693         },
10694
10695         /**
10696          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10697          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10698          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10699          * @param {String} title The title bar text
10700          * @param {String} msg The message box body text
10701          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10702          * @param {Object} scope (optional) The scope of the callback function
10703          * @return {Roo.MessageBox} This message box
10704          */
10705         confirm : function(title, msg, fn, scope){
10706             this.show({
10707                 title : title,
10708                 msg : msg,
10709                 buttons: this.YESNO,
10710                 fn: fn,
10711                 scope : scope,
10712                 modal : true
10713             });
10714             return this;
10715         },
10716
10717         /**
10718          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10719          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10720          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10721          * (could also be the top-right close button) and the text that was entered will be passed as the two
10722          * parameters to the callback.
10723          * @param {String} title The title bar text
10724          * @param {String} msg The message box body text
10725          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10726          * @param {Object} scope (optional) The scope of the callback function
10727          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10728          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10729          * @return {Roo.MessageBox} This message box
10730          */
10731         prompt : function(title, msg, fn, scope, multiline){
10732             this.show({
10733                 title : title,
10734                 msg : msg,
10735                 buttons: this.OKCANCEL,
10736                 fn: fn,
10737                 minWidth:250,
10738                 scope : scope,
10739                 prompt:true,
10740                 multiline: multiline,
10741                 modal : true
10742             });
10743             return this;
10744         },
10745
10746         /**
10747          * Button config that displays a single OK button
10748          * @type Object
10749          */
10750         OK : {ok:true},
10751         /**
10752          * Button config that displays Yes and No buttons
10753          * @type Object
10754          */
10755         YESNO : {yes:true, no:true},
10756         /**
10757          * Button config that displays OK and Cancel buttons
10758          * @type Object
10759          */
10760         OKCANCEL : {ok:true, cancel:true},
10761         /**
10762          * Button config that displays Yes, No and Cancel buttons
10763          * @type Object
10764          */
10765         YESNOCANCEL : {yes:true, no:true, cancel:true},
10766
10767         /**
10768          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10769          * @type Number
10770          */
10771         defaultTextHeight : 75,
10772         /**
10773          * The maximum width in pixels of the message box (defaults to 600)
10774          * @type Number
10775          */
10776         maxWidth : 600,
10777         /**
10778          * The minimum width in pixels of the message box (defaults to 100)
10779          * @type Number
10780          */
10781         minWidth : 100,
10782         /**
10783          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10784          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10785          * @type Number
10786          */
10787         minProgressWidth : 250,
10788         /**
10789          * An object containing the default button text strings that can be overriden for localized language support.
10790          * Supported properties are: ok, cancel, yes and no.
10791          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10792          * @type Object
10793          */
10794         buttonText : {
10795             ok : "OK",
10796             cancel : "Cancel",
10797             yes : "Yes",
10798             no : "No"
10799         }
10800     };
10801 }();
10802
10803 /**
10804  * Shorthand for {@link Roo.MessageBox}
10805  */
10806 Roo.Msg = Roo.MessageBox;/*
10807  * Based on:
10808  * Ext JS Library 1.1.1
10809  * Copyright(c) 2006-2007, Ext JS, LLC.
10810  *
10811  * Originally Released Under LGPL - original licence link has changed is not relivant.
10812  *
10813  * Fork - LGPL
10814  * <script type="text/javascript">
10815  */
10816 /**
10817  * @class Roo.QuickTips
10818  * Provides attractive and customizable tooltips for any element.
10819  * @singleton
10820  */
10821 Roo.QuickTips = function(){
10822     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10823     var ce, bd, xy, dd;
10824     var visible = false, disabled = true, inited = false;
10825     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10826     
10827     var onOver = function(e){
10828         if(disabled){
10829             return;
10830         }
10831         var t = e.getTarget();
10832         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10833             return;
10834         }
10835         if(ce && t == ce.el){
10836             clearTimeout(hideProc);
10837             return;
10838         }
10839         if(t && tagEls[t.id]){
10840             tagEls[t.id].el = t;
10841             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10842             return;
10843         }
10844         var ttp, et = Roo.fly(t);
10845         var ns = cfg.namespace;
10846         if(tm.interceptTitles && t.title){
10847             ttp = t.title;
10848             t.qtip = ttp;
10849             t.removeAttribute("title");
10850             e.preventDefault();
10851         }else{
10852             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10853         }
10854         if(ttp){
10855             showProc = show.defer(tm.showDelay, tm, [{
10856                 el: t, 
10857                 text: ttp.replace(/\\n/g,'<br/>'),
10858                 width: et.getAttributeNS(ns, cfg.width),
10859                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10860                 title: et.getAttributeNS(ns, cfg.title),
10861                     cls: et.getAttributeNS(ns, cfg.cls)
10862             }]);
10863         }
10864     };
10865     
10866     var onOut = function(e){
10867         clearTimeout(showProc);
10868         var t = e.getTarget();
10869         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10870             hideProc = setTimeout(hide, tm.hideDelay);
10871         }
10872     };
10873     
10874     var onMove = function(e){
10875         if(disabled){
10876             return;
10877         }
10878         xy = e.getXY();
10879         xy[1] += 18;
10880         if(tm.trackMouse && ce){
10881             el.setXY(xy);
10882         }
10883     };
10884     
10885     var onDown = function(e){
10886         clearTimeout(showProc);
10887         clearTimeout(hideProc);
10888         if(!e.within(el)){
10889             if(tm.hideOnClick){
10890                 hide();
10891                 tm.disable();
10892                 tm.enable.defer(100, tm);
10893             }
10894         }
10895     };
10896     
10897     var getPad = function(){
10898         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10899     };
10900
10901     var show = function(o){
10902         if(disabled){
10903             return;
10904         }
10905         clearTimeout(dismissProc);
10906         ce = o;
10907         if(removeCls){ // in case manually hidden
10908             el.removeClass(removeCls);
10909             removeCls = null;
10910         }
10911         if(ce.cls){
10912             el.addClass(ce.cls);
10913             removeCls = ce.cls;
10914         }
10915         if(ce.title){
10916             tipTitle.update(ce.title);
10917             tipTitle.show();
10918         }else{
10919             tipTitle.update('');
10920             tipTitle.hide();
10921         }
10922         el.dom.style.width  = tm.maxWidth+'px';
10923         //tipBody.dom.style.width = '';
10924         tipBodyText.update(o.text);
10925         var p = getPad(), w = ce.width;
10926         if(!w){
10927             var td = tipBodyText.dom;
10928             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10929             if(aw > tm.maxWidth){
10930                 w = tm.maxWidth;
10931             }else if(aw < tm.minWidth){
10932                 w = tm.minWidth;
10933             }else{
10934                 w = aw;
10935             }
10936         }
10937         //tipBody.setWidth(w);
10938         el.setWidth(parseInt(w, 10) + p);
10939         if(ce.autoHide === false){
10940             close.setDisplayed(true);
10941             if(dd){
10942                 dd.unlock();
10943             }
10944         }else{
10945             close.setDisplayed(false);
10946             if(dd){
10947                 dd.lock();
10948             }
10949         }
10950         if(xy){
10951             el.avoidY = xy[1]-18;
10952             el.setXY(xy);
10953         }
10954         if(tm.animate){
10955             el.setOpacity(.1);
10956             el.setStyle("visibility", "visible");
10957             el.fadeIn({callback: afterShow});
10958         }else{
10959             afterShow();
10960         }
10961     };
10962     
10963     var afterShow = function(){
10964         if(ce){
10965             el.show();
10966             esc.enable();
10967             if(tm.autoDismiss && ce.autoHide !== false){
10968                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10969             }
10970         }
10971     };
10972     
10973     var hide = function(noanim){
10974         clearTimeout(dismissProc);
10975         clearTimeout(hideProc);
10976         ce = null;
10977         if(el.isVisible()){
10978             esc.disable();
10979             if(noanim !== true && tm.animate){
10980                 el.fadeOut({callback: afterHide});
10981             }else{
10982                 afterHide();
10983             } 
10984         }
10985     };
10986     
10987     var afterHide = function(){
10988         el.hide();
10989         if(removeCls){
10990             el.removeClass(removeCls);
10991             removeCls = null;
10992         }
10993     };
10994     
10995     return {
10996         /**
10997         * @cfg {Number} minWidth
10998         * The minimum width of the quick tip (defaults to 40)
10999         */
11000        minWidth : 40,
11001         /**
11002         * @cfg {Number} maxWidth
11003         * The maximum width of the quick tip (defaults to 300)
11004         */
11005        maxWidth : 300,
11006         /**
11007         * @cfg {Boolean} interceptTitles
11008         * True to automatically use the element's DOM title value if available (defaults to false)
11009         */
11010        interceptTitles : false,
11011         /**
11012         * @cfg {Boolean} trackMouse
11013         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11014         */
11015        trackMouse : false,
11016         /**
11017         * @cfg {Boolean} hideOnClick
11018         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11019         */
11020        hideOnClick : true,
11021         /**
11022         * @cfg {Number} showDelay
11023         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11024         */
11025        showDelay : 500,
11026         /**
11027         * @cfg {Number} hideDelay
11028         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11029         */
11030        hideDelay : 200,
11031         /**
11032         * @cfg {Boolean} autoHide
11033         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11034         * Used in conjunction with hideDelay.
11035         */
11036        autoHide : true,
11037         /**
11038         * @cfg {Boolean}
11039         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11040         * (defaults to true).  Used in conjunction with autoDismissDelay.
11041         */
11042        autoDismiss : true,
11043         /**
11044         * @cfg {Number}
11045         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11046         */
11047        autoDismissDelay : 5000,
11048        /**
11049         * @cfg {Boolean} animate
11050         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11051         */
11052        animate : false,
11053
11054        /**
11055         * @cfg {String} title
11056         * Title text to display (defaults to '').  This can be any valid HTML markup.
11057         */
11058         title: '',
11059        /**
11060         * @cfg {String} text
11061         * Body text to display (defaults to '').  This can be any valid HTML markup.
11062         */
11063         text : '',
11064        /**
11065         * @cfg {String} cls
11066         * A CSS class to apply to the base quick tip element (defaults to '').
11067         */
11068         cls : '',
11069        /**
11070         * @cfg {Number} width
11071         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11072         * minWidth or maxWidth.
11073         */
11074         width : null,
11075
11076     /**
11077      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11078      * or display QuickTips in a page.
11079      */
11080        init : function(){
11081           tm = Roo.QuickTips;
11082           cfg = tm.tagConfig;
11083           if(!inited){
11084               if(!Roo.isReady){ // allow calling of init() before onReady
11085                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11086                   return;
11087               }
11088               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11089               el.fxDefaults = {stopFx: true};
11090               // maximum custom styling
11091               //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>');
11092               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>');              
11093               tipTitle = el.child('h3');
11094               tipTitle.enableDisplayMode("block");
11095               tipBody = el.child('div.x-tip-bd');
11096               tipBodyText = el.child('div.x-tip-bd-inner');
11097               //bdLeft = el.child('div.x-tip-bd-left');
11098               //bdRight = el.child('div.x-tip-bd-right');
11099               close = el.child('div.x-tip-close');
11100               close.enableDisplayMode("block");
11101               close.on("click", hide);
11102               var d = Roo.get(document);
11103               d.on("mousedown", onDown);
11104               d.on("mouseover", onOver);
11105               d.on("mouseout", onOut);
11106               d.on("mousemove", onMove);
11107               esc = d.addKeyListener(27, hide);
11108               esc.disable();
11109               if(Roo.dd.DD){
11110                   dd = el.initDD("default", null, {
11111                       onDrag : function(){
11112                           el.sync();  
11113                       }
11114                   });
11115                   dd.setHandleElId(tipTitle.id);
11116                   dd.lock();
11117               }
11118               inited = true;
11119           }
11120           this.enable(); 
11121        },
11122
11123     /**
11124      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11125      * are supported:
11126      * <pre>
11127 Property    Type                   Description
11128 ----------  ---------------------  ------------------------------------------------------------------------
11129 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11130      * </ul>
11131      * @param {Object} config The config object
11132      */
11133        register : function(config){
11134            var cs = config instanceof Array ? config : arguments;
11135            for(var i = 0, len = cs.length; i < len; i++) {
11136                var c = cs[i];
11137                var target = c.target;
11138                if(target){
11139                    if(target instanceof Array){
11140                        for(var j = 0, jlen = target.length; j < jlen; j++){
11141                            tagEls[target[j]] = c;
11142                        }
11143                    }else{
11144                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11145                    }
11146                }
11147            }
11148        },
11149
11150     /**
11151      * Removes this quick tip from its element and destroys it.
11152      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11153      */
11154        unregister : function(el){
11155            delete tagEls[Roo.id(el)];
11156        },
11157
11158     /**
11159      * Enable this quick tip.
11160      */
11161        enable : function(){
11162            if(inited && disabled){
11163                locks.pop();
11164                if(locks.length < 1){
11165                    disabled = false;
11166                }
11167            }
11168        },
11169
11170     /**
11171      * Disable this quick tip.
11172      */
11173        disable : function(){
11174           disabled = true;
11175           clearTimeout(showProc);
11176           clearTimeout(hideProc);
11177           clearTimeout(dismissProc);
11178           if(ce){
11179               hide(true);
11180           }
11181           locks.push(1);
11182        },
11183
11184     /**
11185      * Returns true if the quick tip is enabled, else false.
11186      */
11187        isEnabled : function(){
11188             return !disabled;
11189        },
11190
11191         // private
11192        tagConfig : {
11193            namespace : "roo", // was ext?? this may break..
11194            alt_namespace : "ext",
11195            attribute : "qtip",
11196            width : "width",
11197            target : "target",
11198            title : "qtitle",
11199            hide : "hide",
11200            cls : "qclass"
11201        }
11202    };
11203 }();
11204
11205 // backwards compat
11206 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11207  * Based on:
11208  * Ext JS Library 1.1.1
11209  * Copyright(c) 2006-2007, Ext JS, LLC.
11210  *
11211  * Originally Released Under LGPL - original licence link has changed is not relivant.
11212  *
11213  * Fork - LGPL
11214  * <script type="text/javascript">
11215  */
11216  
11217
11218 /**
11219  * @class Roo.tree.TreePanel
11220  * @extends Roo.data.Tree
11221  * @cfg {Roo.tree.TreeNode} root The root node
11222  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11223  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11224  * @cfg {Boolean} enableDD true to enable drag and drop
11225  * @cfg {Boolean} enableDrag true to enable just drag
11226  * @cfg {Boolean} enableDrop true to enable just drop
11227  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11228  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11229  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11230  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11231  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11232  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11233  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11234  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11235  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11236  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11237  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11238  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11239  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11240  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11241  * @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>
11242  * @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>
11243  * 
11244  * @constructor
11245  * @param {String/HTMLElement/Element} el The container element
11246  * @param {Object} config
11247  */
11248 Roo.tree.TreePanel = function(el, config){
11249     var root = false;
11250     var loader = false;
11251     if (config.root) {
11252         root = config.root;
11253         delete config.root;
11254     }
11255     if (config.loader) {
11256         loader = config.loader;
11257         delete config.loader;
11258     }
11259     
11260     Roo.apply(this, config);
11261     Roo.tree.TreePanel.superclass.constructor.call(this);
11262     this.el = Roo.get(el);
11263     this.el.addClass('x-tree');
11264     //console.log(root);
11265     if (root) {
11266         this.setRootNode( Roo.factory(root, Roo.tree));
11267     }
11268     if (loader) {
11269         this.loader = Roo.factory(loader, Roo.tree);
11270     }
11271    /**
11272     * Read-only. The id of the container element becomes this TreePanel's id.
11273     */
11274     this.id = this.el.id;
11275     this.addEvents({
11276         /**
11277         * @event beforeload
11278         * Fires before a node is loaded, return false to cancel
11279         * @param {Node} node The node being loaded
11280         */
11281         "beforeload" : true,
11282         /**
11283         * @event load
11284         * Fires when a node is loaded
11285         * @param {Node} node The node that was loaded
11286         */
11287         "load" : true,
11288         /**
11289         * @event textchange
11290         * Fires when the text for a node is changed
11291         * @param {Node} node The node
11292         * @param {String} text The new text
11293         * @param {String} oldText The old text
11294         */
11295         "textchange" : true,
11296         /**
11297         * @event beforeexpand
11298         * Fires before a node is expanded, return false to cancel.
11299         * @param {Node} node The node
11300         * @param {Boolean} deep
11301         * @param {Boolean} anim
11302         */
11303         "beforeexpand" : true,
11304         /**
11305         * @event beforecollapse
11306         * Fires before a node is collapsed, return false to cancel.
11307         * @param {Node} node The node
11308         * @param {Boolean} deep
11309         * @param {Boolean} anim
11310         */
11311         "beforecollapse" : true,
11312         /**
11313         * @event expand
11314         * Fires when a node is expanded
11315         * @param {Node} node The node
11316         */
11317         "expand" : true,
11318         /**
11319         * @event disabledchange
11320         * Fires when the disabled status of a node changes
11321         * @param {Node} node The node
11322         * @param {Boolean} disabled
11323         */
11324         "disabledchange" : true,
11325         /**
11326         * @event collapse
11327         * Fires when a node is collapsed
11328         * @param {Node} node The node
11329         */
11330         "collapse" : true,
11331         /**
11332         * @event beforeclick
11333         * Fires before click processing on a node. Return false to cancel the default action.
11334         * @param {Node} node The node
11335         * @param {Roo.EventObject} e The event object
11336         */
11337         "beforeclick":true,
11338         /**
11339         * @event checkchange
11340         * Fires when a node with a checkbox's checked property changes
11341         * @param {Node} this This node
11342         * @param {Boolean} checked
11343         */
11344         "checkchange":true,
11345         /**
11346         * @event click
11347         * Fires when a node is clicked
11348         * @param {Node} node The node
11349         * @param {Roo.EventObject} e The event object
11350         */
11351         "click":true,
11352         /**
11353         * @event dblclick
11354         * Fires when a node is double clicked
11355         * @param {Node} node The node
11356         * @param {Roo.EventObject} e The event object
11357         */
11358         "dblclick":true,
11359         /**
11360         * @event contextmenu
11361         * Fires when a node is right clicked
11362         * @param {Node} node The node
11363         * @param {Roo.EventObject} e The event object
11364         */
11365         "contextmenu":true,
11366         /**
11367         * @event beforechildrenrendered
11368         * Fires right before the child nodes for a node are rendered
11369         * @param {Node} node The node
11370         */
11371         "beforechildrenrendered":true,
11372         /**
11373         * @event startdrag
11374         * Fires when a node starts being dragged
11375         * @param {Roo.tree.TreePanel} this
11376         * @param {Roo.tree.TreeNode} node
11377         * @param {event} e The raw browser event
11378         */ 
11379        "startdrag" : true,
11380        /**
11381         * @event enddrag
11382         * Fires when a drag operation is complete
11383         * @param {Roo.tree.TreePanel} this
11384         * @param {Roo.tree.TreeNode} node
11385         * @param {event} e The raw browser event
11386         */
11387        "enddrag" : true,
11388        /**
11389         * @event dragdrop
11390         * Fires when a dragged node is dropped on a valid DD target
11391         * @param {Roo.tree.TreePanel} this
11392         * @param {Roo.tree.TreeNode} node
11393         * @param {DD} dd The dd it was dropped on
11394         * @param {event} e The raw browser event
11395         */
11396        "dragdrop" : true,
11397        /**
11398         * @event beforenodedrop
11399         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11400         * passed to handlers has the following properties:<br />
11401         * <ul style="padding:5px;padding-left:16px;">
11402         * <li>tree - The TreePanel</li>
11403         * <li>target - The node being targeted for the drop</li>
11404         * <li>data - The drag data from the drag source</li>
11405         * <li>point - The point of the drop - append, above or below</li>
11406         * <li>source - The drag source</li>
11407         * <li>rawEvent - Raw mouse event</li>
11408         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11409         * to be inserted by setting them on this object.</li>
11410         * <li>cancel - Set this to true to cancel the drop.</li>
11411         * </ul>
11412         * @param {Object} dropEvent
11413         */
11414        "beforenodedrop" : true,
11415        /**
11416         * @event nodedrop
11417         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11418         * passed to handlers has the following properties:<br />
11419         * <ul style="padding:5px;padding-left:16px;">
11420         * <li>tree - The TreePanel</li>
11421         * <li>target - The node being targeted for the drop</li>
11422         * <li>data - The drag data from the drag source</li>
11423         * <li>point - The point of the drop - append, above or below</li>
11424         * <li>source - The drag source</li>
11425         * <li>rawEvent - Raw mouse event</li>
11426         * <li>dropNode - Dropped node(s).</li>
11427         * </ul>
11428         * @param {Object} dropEvent
11429         */
11430        "nodedrop" : true,
11431         /**
11432         * @event nodedragover
11433         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11434         * passed to handlers has the following properties:<br />
11435         * <ul style="padding:5px;padding-left:16px;">
11436         * <li>tree - The TreePanel</li>
11437         * <li>target - The node being targeted for the drop</li>
11438         * <li>data - The drag data from the drag source</li>
11439         * <li>point - The point of the drop - append, above or below</li>
11440         * <li>source - The drag source</li>
11441         * <li>rawEvent - Raw mouse event</li>
11442         * <li>dropNode - Drop node(s) provided by the source.</li>
11443         * <li>cancel - Set this to true to signal drop not allowed.</li>
11444         * </ul>
11445         * @param {Object} dragOverEvent
11446         */
11447        "nodedragover" : true,
11448        /**
11449         * @event appendnode
11450         * Fires when append node to the tree
11451         * @param {Roo.tree.TreePanel} this
11452         * @param {Roo.tree.TreeNode} node
11453         * @param {Number} index The index of the newly appended node
11454         */
11455        "appendnode" : true
11456         
11457     });
11458     if(this.singleExpand){
11459        this.on("beforeexpand", this.restrictExpand, this);
11460     }
11461     if (this.editor) {
11462         this.editor.tree = this;
11463         this.editor = Roo.factory(this.editor, Roo.tree);
11464     }
11465     
11466     if (this.selModel) {
11467         this.selModel = Roo.factory(this.selModel, Roo.tree);
11468     }
11469    
11470 };
11471 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11472     rootVisible : true,
11473     animate: Roo.enableFx,
11474     lines : true,
11475     enableDD : false,
11476     hlDrop : Roo.enableFx,
11477   
11478     renderer: false,
11479     
11480     rendererTip: false,
11481     // private
11482     restrictExpand : function(node){
11483         var p = node.parentNode;
11484         if(p){
11485             if(p.expandedChild && p.expandedChild.parentNode == p){
11486                 p.expandedChild.collapse();
11487             }
11488             p.expandedChild = node;
11489         }
11490     },
11491
11492     // private override
11493     setRootNode : function(node){
11494         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11495         if(!this.rootVisible){
11496             node.ui = new Roo.tree.RootTreeNodeUI(node);
11497         }
11498         return node;
11499     },
11500
11501     /**
11502      * Returns the container element for this TreePanel
11503      */
11504     getEl : function(){
11505         return this.el;
11506     },
11507
11508     /**
11509      * Returns the default TreeLoader for this TreePanel
11510      */
11511     getLoader : function(){
11512         return this.loader;
11513     },
11514
11515     /**
11516      * Expand all nodes
11517      */
11518     expandAll : function(){
11519         this.root.expand(true);
11520     },
11521
11522     /**
11523      * Collapse all nodes
11524      */
11525     collapseAll : function(){
11526         this.root.collapse(true);
11527     },
11528
11529     /**
11530      * Returns the selection model used by this TreePanel
11531      */
11532     getSelectionModel : function(){
11533         if(!this.selModel){
11534             this.selModel = new Roo.tree.DefaultSelectionModel();
11535         }
11536         return this.selModel;
11537     },
11538
11539     /**
11540      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11541      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11542      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11543      * @return {Array}
11544      */
11545     getChecked : function(a, startNode){
11546         startNode = startNode || this.root;
11547         var r = [];
11548         var f = function(){
11549             if(this.attributes.checked){
11550                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11551             }
11552         }
11553         startNode.cascade(f);
11554         return r;
11555     },
11556
11557     /**
11558      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11559      * @param {String} path
11560      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11561      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11562      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11563      */
11564     expandPath : function(path, attr, callback){
11565         attr = attr || "id";
11566         var keys = path.split(this.pathSeparator);
11567         var curNode = this.root;
11568         if(curNode.attributes[attr] != keys[1]){ // invalid root
11569             if(callback){
11570                 callback(false, null);
11571             }
11572             return;
11573         }
11574         var index = 1;
11575         var f = function(){
11576             if(++index == keys.length){
11577                 if(callback){
11578                     callback(true, curNode);
11579                 }
11580                 return;
11581             }
11582             var c = curNode.findChild(attr, keys[index]);
11583             if(!c){
11584                 if(callback){
11585                     callback(false, curNode);
11586                 }
11587                 return;
11588             }
11589             curNode = c;
11590             c.expand(false, false, f);
11591         };
11592         curNode.expand(false, false, f);
11593     },
11594
11595     /**
11596      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11597      * @param {String} path
11598      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11599      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11600      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11601      */
11602     selectPath : function(path, attr, callback){
11603         attr = attr || "id";
11604         var keys = path.split(this.pathSeparator);
11605         var v = keys.pop();
11606         if(keys.length > 0){
11607             var f = function(success, node){
11608                 if(success && node){
11609                     var n = node.findChild(attr, v);
11610                     if(n){
11611                         n.select();
11612                         if(callback){
11613                             callback(true, n);
11614                         }
11615                     }else if(callback){
11616                         callback(false, n);
11617                     }
11618                 }else{
11619                     if(callback){
11620                         callback(false, n);
11621                     }
11622                 }
11623             };
11624             this.expandPath(keys.join(this.pathSeparator), attr, f);
11625         }else{
11626             this.root.select();
11627             if(callback){
11628                 callback(true, this.root);
11629             }
11630         }
11631     },
11632
11633     getTreeEl : function(){
11634         return this.el;
11635     },
11636
11637     /**
11638      * Trigger rendering of this TreePanel
11639      */
11640     render : function(){
11641         if (this.innerCt) {
11642             return this; // stop it rendering more than once!!
11643         }
11644         
11645         this.innerCt = this.el.createChild({tag:"ul",
11646                cls:"x-tree-root-ct " +
11647                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11648
11649         if(this.containerScroll){
11650             Roo.dd.ScrollManager.register(this.el);
11651         }
11652         if((this.enableDD || this.enableDrop) && !this.dropZone){
11653            /**
11654             * The dropZone used by this tree if drop is enabled
11655             * @type Roo.tree.TreeDropZone
11656             */
11657              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11658                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11659            });
11660         }
11661         if((this.enableDD || this.enableDrag) && !this.dragZone){
11662            /**
11663             * The dragZone used by this tree if drag is enabled
11664             * @type Roo.tree.TreeDragZone
11665             */
11666             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11667                ddGroup: this.ddGroup || "TreeDD",
11668                scroll: this.ddScroll
11669            });
11670         }
11671         this.getSelectionModel().init(this);
11672         if (!this.root) {
11673             Roo.log("ROOT not set in tree");
11674             return this;
11675         }
11676         this.root.render();
11677         if(!this.rootVisible){
11678             this.root.renderChildren();
11679         }
11680         return this;
11681     }
11682 });/*
11683  * Based on:
11684  * Ext JS Library 1.1.1
11685  * Copyright(c) 2006-2007, Ext JS, LLC.
11686  *
11687  * Originally Released Under LGPL - original licence link has changed is not relivant.
11688  *
11689  * Fork - LGPL
11690  * <script type="text/javascript">
11691  */
11692  
11693
11694 /**
11695  * @class Roo.tree.DefaultSelectionModel
11696  * @extends Roo.util.Observable
11697  * The default single selection for a TreePanel.
11698  * @param {Object} cfg Configuration
11699  */
11700 Roo.tree.DefaultSelectionModel = function(cfg){
11701    this.selNode = null;
11702    
11703    
11704    
11705    this.addEvents({
11706        /**
11707         * @event selectionchange
11708         * Fires when the selected node changes
11709         * @param {DefaultSelectionModel} this
11710         * @param {TreeNode} node the new selection
11711         */
11712        "selectionchange" : true,
11713
11714        /**
11715         * @event beforeselect
11716         * Fires before the selected node changes, return false to cancel the change
11717         * @param {DefaultSelectionModel} this
11718         * @param {TreeNode} node the new selection
11719         * @param {TreeNode} node the old selection
11720         */
11721        "beforeselect" : true
11722    });
11723    
11724     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11725 };
11726
11727 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11728     init : function(tree){
11729         this.tree = tree;
11730         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11731         tree.on("click", this.onNodeClick, this);
11732     },
11733     
11734     onNodeClick : function(node, e){
11735         if (e.ctrlKey && this.selNode == node)  {
11736             this.unselect(node);
11737             return;
11738         }
11739         this.select(node);
11740     },
11741     
11742     /**
11743      * Select a node.
11744      * @param {TreeNode} node The node to select
11745      * @return {TreeNode} The selected node
11746      */
11747     select : function(node){
11748         var last = this.selNode;
11749         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11750             if(last){
11751                 last.ui.onSelectedChange(false);
11752             }
11753             this.selNode = node;
11754             node.ui.onSelectedChange(true);
11755             this.fireEvent("selectionchange", this, node, last);
11756         }
11757         return node;
11758     },
11759     
11760     /**
11761      * Deselect a node.
11762      * @param {TreeNode} node The node to unselect
11763      */
11764     unselect : function(node){
11765         if(this.selNode == node){
11766             this.clearSelections();
11767         }    
11768     },
11769     
11770     /**
11771      * Clear all selections
11772      */
11773     clearSelections : function(){
11774         var n = this.selNode;
11775         if(n){
11776             n.ui.onSelectedChange(false);
11777             this.selNode = null;
11778             this.fireEvent("selectionchange", this, null);
11779         }
11780         return n;
11781     },
11782     
11783     /**
11784      * Get the selected node
11785      * @return {TreeNode} The selected node
11786      */
11787     getSelectedNode : function(){
11788         return this.selNode;    
11789     },
11790     
11791     /**
11792      * Returns true if the node is selected
11793      * @param {TreeNode} node The node to check
11794      * @return {Boolean}
11795      */
11796     isSelected : function(node){
11797         return this.selNode == node;  
11798     },
11799
11800     /**
11801      * Selects the node above the selected node in the tree, intelligently walking the nodes
11802      * @return TreeNode The new selection
11803      */
11804     selectPrevious : function(){
11805         var s = this.selNode || this.lastSelNode;
11806         if(!s){
11807             return null;
11808         }
11809         var ps = s.previousSibling;
11810         if(ps){
11811             if(!ps.isExpanded() || ps.childNodes.length < 1){
11812                 return this.select(ps);
11813             } else{
11814                 var lc = ps.lastChild;
11815                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11816                     lc = lc.lastChild;
11817                 }
11818                 return this.select(lc);
11819             }
11820         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11821             return this.select(s.parentNode);
11822         }
11823         return null;
11824     },
11825
11826     /**
11827      * Selects the node above the selected node in the tree, intelligently walking the nodes
11828      * @return TreeNode The new selection
11829      */
11830     selectNext : function(){
11831         var s = this.selNode || this.lastSelNode;
11832         if(!s){
11833             return null;
11834         }
11835         if(s.firstChild && s.isExpanded()){
11836              return this.select(s.firstChild);
11837          }else if(s.nextSibling){
11838              return this.select(s.nextSibling);
11839          }else if(s.parentNode){
11840             var newS = null;
11841             s.parentNode.bubble(function(){
11842                 if(this.nextSibling){
11843                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11844                     return false;
11845                 }
11846             });
11847             return newS;
11848          }
11849         return null;
11850     },
11851
11852     onKeyDown : function(e){
11853         var s = this.selNode || this.lastSelNode;
11854         // undesirable, but required
11855         var sm = this;
11856         if(!s){
11857             return;
11858         }
11859         var k = e.getKey();
11860         switch(k){
11861              case e.DOWN:
11862                  e.stopEvent();
11863                  this.selectNext();
11864              break;
11865              case e.UP:
11866                  e.stopEvent();
11867                  this.selectPrevious();
11868              break;
11869              case e.RIGHT:
11870                  e.preventDefault();
11871                  if(s.hasChildNodes()){
11872                      if(!s.isExpanded()){
11873                          s.expand();
11874                      }else if(s.firstChild){
11875                          this.select(s.firstChild, e);
11876                      }
11877                  }
11878              break;
11879              case e.LEFT:
11880                  e.preventDefault();
11881                  if(s.hasChildNodes() && s.isExpanded()){
11882                      s.collapse();
11883                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11884                      this.select(s.parentNode, e);
11885                  }
11886              break;
11887         };
11888     }
11889 });
11890
11891 /**
11892  * @class Roo.tree.MultiSelectionModel
11893  * @extends Roo.util.Observable
11894  * Multi selection for a TreePanel.
11895  * @param {Object} cfg Configuration
11896  */
11897 Roo.tree.MultiSelectionModel = function(){
11898    this.selNodes = [];
11899    this.selMap = {};
11900    this.addEvents({
11901        /**
11902         * @event selectionchange
11903         * Fires when the selected nodes change
11904         * @param {MultiSelectionModel} this
11905         * @param {Array} nodes Array of the selected nodes
11906         */
11907        "selectionchange" : true
11908    });
11909    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11910    
11911 };
11912
11913 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11914     init : function(tree){
11915         this.tree = tree;
11916         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11917         tree.on("click", this.onNodeClick, this);
11918     },
11919     
11920     onNodeClick : function(node, e){
11921         this.select(node, e, e.ctrlKey);
11922     },
11923     
11924     /**
11925      * Select a node.
11926      * @param {TreeNode} node The node to select
11927      * @param {EventObject} e (optional) An event associated with the selection
11928      * @param {Boolean} keepExisting True to retain existing selections
11929      * @return {TreeNode} The selected node
11930      */
11931     select : function(node, e, keepExisting){
11932         if(keepExisting !== true){
11933             this.clearSelections(true);
11934         }
11935         if(this.isSelected(node)){
11936             this.lastSelNode = node;
11937             return node;
11938         }
11939         this.selNodes.push(node);
11940         this.selMap[node.id] = node;
11941         this.lastSelNode = node;
11942         node.ui.onSelectedChange(true);
11943         this.fireEvent("selectionchange", this, this.selNodes);
11944         return node;
11945     },
11946     
11947     /**
11948      * Deselect a node.
11949      * @param {TreeNode} node The node to unselect
11950      */
11951     unselect : function(node){
11952         if(this.selMap[node.id]){
11953             node.ui.onSelectedChange(false);
11954             var sn = this.selNodes;
11955             var index = -1;
11956             if(sn.indexOf){
11957                 index = sn.indexOf(node);
11958             }else{
11959                 for(var i = 0, len = sn.length; i < len; i++){
11960                     if(sn[i] == node){
11961                         index = i;
11962                         break;
11963                     }
11964                 }
11965             }
11966             if(index != -1){
11967                 this.selNodes.splice(index, 1);
11968             }
11969             delete this.selMap[node.id];
11970             this.fireEvent("selectionchange", this, this.selNodes);
11971         }
11972     },
11973     
11974     /**
11975      * Clear all selections
11976      */
11977     clearSelections : function(suppressEvent){
11978         var sn = this.selNodes;
11979         if(sn.length > 0){
11980             for(var i = 0, len = sn.length; i < len; i++){
11981                 sn[i].ui.onSelectedChange(false);
11982             }
11983             this.selNodes = [];
11984             this.selMap = {};
11985             if(suppressEvent !== true){
11986                 this.fireEvent("selectionchange", this, this.selNodes);
11987             }
11988         }
11989     },
11990     
11991     /**
11992      * Returns true if the node is selected
11993      * @param {TreeNode} node The node to check
11994      * @return {Boolean}
11995      */
11996     isSelected : function(node){
11997         return this.selMap[node.id] ? true : false;  
11998     },
11999     
12000     /**
12001      * Returns an array of the selected nodes
12002      * @return {Array}
12003      */
12004     getSelectedNodes : function(){
12005         return this.selNodes;    
12006     },
12007
12008     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12009
12010     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12011
12012     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12013 });/*
12014  * Based on:
12015  * Ext JS Library 1.1.1
12016  * Copyright(c) 2006-2007, Ext JS, LLC.
12017  *
12018  * Originally Released Under LGPL - original licence link has changed is not relivant.
12019  *
12020  * Fork - LGPL
12021  * <script type="text/javascript">
12022  */
12023  
12024 /**
12025  * @class Roo.tree.TreeNode
12026  * @extends Roo.data.Node
12027  * @cfg {String} text The text for this node
12028  * @cfg {Boolean} expanded true to start the node expanded
12029  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12030  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12031  * @cfg {Boolean} disabled true to start the node disabled
12032  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12033  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12034  * @cfg {String} cls A css class to be added to the node
12035  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12036  * @cfg {String} href URL of the link used for the node (defaults to #)
12037  * @cfg {String} hrefTarget target frame for the link
12038  * @cfg {String} qtip An Ext QuickTip for the node
12039  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12040  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12041  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12042  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12043  * (defaults to undefined with no checkbox rendered)
12044  * @constructor
12045  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12046  */
12047 Roo.tree.TreeNode = function(attributes){
12048     attributes = attributes || {};
12049     if(typeof attributes == "string"){
12050         attributes = {text: attributes};
12051     }
12052     this.childrenRendered = false;
12053     this.rendered = false;
12054     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12055     this.expanded = attributes.expanded === true;
12056     this.isTarget = attributes.isTarget !== false;
12057     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12058     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12059
12060     /**
12061      * Read-only. The text for this node. To change it use setText().
12062      * @type String
12063      */
12064     this.text = attributes.text;
12065     /**
12066      * True if this node is disabled.
12067      * @type Boolean
12068      */
12069     this.disabled = attributes.disabled === true;
12070
12071     this.addEvents({
12072         /**
12073         * @event textchange
12074         * Fires when the text for this node is changed
12075         * @param {Node} this This node
12076         * @param {String} text The new text
12077         * @param {String} oldText The old text
12078         */
12079         "textchange" : true,
12080         /**
12081         * @event beforeexpand
12082         * Fires before this node is expanded, return false to cancel.
12083         * @param {Node} this This node
12084         * @param {Boolean} deep
12085         * @param {Boolean} anim
12086         */
12087         "beforeexpand" : true,
12088         /**
12089         * @event beforecollapse
12090         * Fires before this node is collapsed, return false to cancel.
12091         * @param {Node} this This node
12092         * @param {Boolean} deep
12093         * @param {Boolean} anim
12094         */
12095         "beforecollapse" : true,
12096         /**
12097         * @event expand
12098         * Fires when this node is expanded
12099         * @param {Node} this This node
12100         */
12101         "expand" : true,
12102         /**
12103         * @event disabledchange
12104         * Fires when the disabled status of this node changes
12105         * @param {Node} this This node
12106         * @param {Boolean} disabled
12107         */
12108         "disabledchange" : true,
12109         /**
12110         * @event collapse
12111         * Fires when this node is collapsed
12112         * @param {Node} this This node
12113         */
12114         "collapse" : true,
12115         /**
12116         * @event beforeclick
12117         * Fires before click processing. Return false to cancel the default action.
12118         * @param {Node} this This node
12119         * @param {Roo.EventObject} e The event object
12120         */
12121         "beforeclick":true,
12122         /**
12123         * @event checkchange
12124         * Fires when a node with a checkbox's checked property changes
12125         * @param {Node} this This node
12126         * @param {Boolean} checked
12127         */
12128         "checkchange":true,
12129         /**
12130         * @event click
12131         * Fires when this node is clicked
12132         * @param {Node} this This node
12133         * @param {Roo.EventObject} e The event object
12134         */
12135         "click":true,
12136         /**
12137         * @event dblclick
12138         * Fires when this node is double clicked
12139         * @param {Node} this This node
12140         * @param {Roo.EventObject} e The event object
12141         */
12142         "dblclick":true,
12143         /**
12144         * @event contextmenu
12145         * Fires when this node is right clicked
12146         * @param {Node} this This node
12147         * @param {Roo.EventObject} e The event object
12148         */
12149         "contextmenu":true,
12150         /**
12151         * @event beforechildrenrendered
12152         * Fires right before the child nodes for this node are rendered
12153         * @param {Node} this This node
12154         */
12155         "beforechildrenrendered":true
12156     });
12157
12158     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12159
12160     /**
12161      * Read-only. The UI for this node
12162      * @type TreeNodeUI
12163      */
12164     this.ui = new uiClass(this);
12165     
12166     // finally support items[]
12167     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12168         return;
12169     }
12170     
12171     
12172     Roo.each(this.attributes.items, function(c) {
12173         this.appendChild(Roo.factory(c,Roo.Tree));
12174     }, this);
12175     delete this.attributes.items;
12176     
12177     
12178     
12179 };
12180 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12181     preventHScroll: true,
12182     /**
12183      * Returns true if this node is expanded
12184      * @return {Boolean}
12185      */
12186     isExpanded : function(){
12187         return this.expanded;
12188     },
12189
12190     /**
12191      * Returns the UI object for this node
12192      * @return {TreeNodeUI}
12193      */
12194     getUI : function(){
12195         return this.ui;
12196     },
12197
12198     // private override
12199     setFirstChild : function(node){
12200         var of = this.firstChild;
12201         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12202         if(this.childrenRendered && of && node != of){
12203             of.renderIndent(true, true);
12204         }
12205         if(this.rendered){
12206             this.renderIndent(true, true);
12207         }
12208     },
12209
12210     // private override
12211     setLastChild : function(node){
12212         var ol = this.lastChild;
12213         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12214         if(this.childrenRendered && ol && node != ol){
12215             ol.renderIndent(true, true);
12216         }
12217         if(this.rendered){
12218             this.renderIndent(true, true);
12219         }
12220     },
12221
12222     // these methods are overridden to provide lazy rendering support
12223     // private override
12224     appendChild : function()
12225     {
12226         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12227         if(node && this.childrenRendered){
12228             node.render();
12229         }
12230         this.ui.updateExpandIcon();
12231         return node;
12232     },
12233
12234     // private override
12235     removeChild : function(node){
12236         this.ownerTree.getSelectionModel().unselect(node);
12237         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12238         // if it's been rendered remove dom node
12239         if(this.childrenRendered){
12240             node.ui.remove();
12241         }
12242         if(this.childNodes.length < 1){
12243             this.collapse(false, false);
12244         }else{
12245             this.ui.updateExpandIcon();
12246         }
12247         if(!this.firstChild) {
12248             this.childrenRendered = false;
12249         }
12250         return node;
12251     },
12252
12253     // private override
12254     insertBefore : function(node, refNode){
12255         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12256         if(newNode && refNode && this.childrenRendered){
12257             node.render();
12258         }
12259         this.ui.updateExpandIcon();
12260         return newNode;
12261     },
12262
12263     /**
12264      * Sets the text for this node
12265      * @param {String} text
12266      */
12267     setText : function(text){
12268         var oldText = this.text;
12269         this.text = text;
12270         this.attributes.text = text;
12271         if(this.rendered){ // event without subscribing
12272             this.ui.onTextChange(this, text, oldText);
12273         }
12274         this.fireEvent("textchange", this, text, oldText);
12275     },
12276
12277     /**
12278      * Triggers selection of this node
12279      */
12280     select : function(){
12281         this.getOwnerTree().getSelectionModel().select(this);
12282     },
12283
12284     /**
12285      * Triggers deselection of this node
12286      */
12287     unselect : function(){
12288         this.getOwnerTree().getSelectionModel().unselect(this);
12289     },
12290
12291     /**
12292      * Returns true if this node is selected
12293      * @return {Boolean}
12294      */
12295     isSelected : function(){
12296         return this.getOwnerTree().getSelectionModel().isSelected(this);
12297     },
12298
12299     /**
12300      * Expand this node.
12301      * @param {Boolean} deep (optional) True to expand all children as well
12302      * @param {Boolean} anim (optional) false to cancel the default animation
12303      * @param {Function} callback (optional) A callback to be called when
12304      * expanding this node completes (does not wait for deep expand to complete).
12305      * Called with 1 parameter, this node.
12306      */
12307     expand : function(deep, anim, callback){
12308         if(!this.expanded){
12309             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12310                 return;
12311             }
12312             if(!this.childrenRendered){
12313                 this.renderChildren();
12314             }
12315             this.expanded = true;
12316             
12317             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12318                 this.ui.animExpand(function(){
12319                     this.fireEvent("expand", this);
12320                     if(typeof callback == "function"){
12321                         callback(this);
12322                     }
12323                     if(deep === true){
12324                         this.expandChildNodes(true);
12325                     }
12326                 }.createDelegate(this));
12327                 return;
12328             }else{
12329                 this.ui.expand();
12330                 this.fireEvent("expand", this);
12331                 if(typeof callback == "function"){
12332                     callback(this);
12333                 }
12334             }
12335         }else{
12336            if(typeof callback == "function"){
12337                callback(this);
12338            }
12339         }
12340         if(deep === true){
12341             this.expandChildNodes(true);
12342         }
12343     },
12344
12345     isHiddenRoot : function(){
12346         return this.isRoot && !this.getOwnerTree().rootVisible;
12347     },
12348
12349     /**
12350      * Collapse this node.
12351      * @param {Boolean} deep (optional) True to collapse all children as well
12352      * @param {Boolean} anim (optional) false to cancel the default animation
12353      */
12354     collapse : function(deep, anim){
12355         if(this.expanded && !this.isHiddenRoot()){
12356             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12357                 return;
12358             }
12359             this.expanded = false;
12360             if((this.getOwnerTree().animate && anim !== false) || anim){
12361                 this.ui.animCollapse(function(){
12362                     this.fireEvent("collapse", this);
12363                     if(deep === true){
12364                         this.collapseChildNodes(true);
12365                     }
12366                 }.createDelegate(this));
12367                 return;
12368             }else{
12369                 this.ui.collapse();
12370                 this.fireEvent("collapse", this);
12371             }
12372         }
12373         if(deep === true){
12374             var cs = this.childNodes;
12375             for(var i = 0, len = cs.length; i < len; i++) {
12376                 cs[i].collapse(true, false);
12377             }
12378         }
12379     },
12380
12381     // private
12382     delayedExpand : function(delay){
12383         if(!this.expandProcId){
12384             this.expandProcId = this.expand.defer(delay, this);
12385         }
12386     },
12387
12388     // private
12389     cancelExpand : function(){
12390         if(this.expandProcId){
12391             clearTimeout(this.expandProcId);
12392         }
12393         this.expandProcId = false;
12394     },
12395
12396     /**
12397      * Toggles expanded/collapsed state of the node
12398      */
12399     toggle : function(){
12400         if(this.expanded){
12401             this.collapse();
12402         }else{
12403             this.expand();
12404         }
12405     },
12406
12407     /**
12408      * Ensures all parent nodes are expanded
12409      */
12410     ensureVisible : function(callback){
12411         var tree = this.getOwnerTree();
12412         tree.expandPath(this.parentNode.getPath(), false, function(){
12413             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12414             Roo.callback(callback);
12415         }.createDelegate(this));
12416     },
12417
12418     /**
12419      * Expand all child nodes
12420      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12421      */
12422     expandChildNodes : function(deep){
12423         var cs = this.childNodes;
12424         for(var i = 0, len = cs.length; i < len; i++) {
12425                 cs[i].expand(deep);
12426         }
12427     },
12428
12429     /**
12430      * Collapse all child nodes
12431      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12432      */
12433     collapseChildNodes : function(deep){
12434         var cs = this.childNodes;
12435         for(var i = 0, len = cs.length; i < len; i++) {
12436                 cs[i].collapse(deep);
12437         }
12438     },
12439
12440     /**
12441      * Disables this node
12442      */
12443     disable : function(){
12444         this.disabled = true;
12445         this.unselect();
12446         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12447             this.ui.onDisableChange(this, true);
12448         }
12449         this.fireEvent("disabledchange", this, true);
12450     },
12451
12452     /**
12453      * Enables this node
12454      */
12455     enable : function(){
12456         this.disabled = false;
12457         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12458             this.ui.onDisableChange(this, false);
12459         }
12460         this.fireEvent("disabledchange", this, false);
12461     },
12462
12463     // private
12464     renderChildren : function(suppressEvent){
12465         if(suppressEvent !== false){
12466             this.fireEvent("beforechildrenrendered", this);
12467         }
12468         var cs = this.childNodes;
12469         for(var i = 0, len = cs.length; i < len; i++){
12470             cs[i].render(true);
12471         }
12472         this.childrenRendered = true;
12473     },
12474
12475     // private
12476     sort : function(fn, scope){
12477         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12478         if(this.childrenRendered){
12479             var cs = this.childNodes;
12480             for(var i = 0, len = cs.length; i < len; i++){
12481                 cs[i].render(true);
12482             }
12483         }
12484     },
12485
12486     // private
12487     render : function(bulkRender){
12488         this.ui.render(bulkRender);
12489         if(!this.rendered){
12490             this.rendered = true;
12491             if(this.expanded){
12492                 this.expanded = false;
12493                 this.expand(false, false);
12494             }
12495         }
12496     },
12497
12498     // private
12499     renderIndent : function(deep, refresh){
12500         if(refresh){
12501             this.ui.childIndent = null;
12502         }
12503         this.ui.renderIndent();
12504         if(deep === true && this.childrenRendered){
12505             var cs = this.childNodes;
12506             for(var i = 0, len = cs.length; i < len; i++){
12507                 cs[i].renderIndent(true, refresh);
12508             }
12509         }
12510     }
12511 });/*
12512  * Based on:
12513  * Ext JS Library 1.1.1
12514  * Copyright(c) 2006-2007, Ext JS, LLC.
12515  *
12516  * Originally Released Under LGPL - original licence link has changed is not relivant.
12517  *
12518  * Fork - LGPL
12519  * <script type="text/javascript">
12520  */
12521  
12522 /**
12523  * @class Roo.tree.AsyncTreeNode
12524  * @extends Roo.tree.TreeNode
12525  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12526  * @constructor
12527  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12528  */
12529  Roo.tree.AsyncTreeNode = function(config){
12530     this.loaded = false;
12531     this.loading = false;
12532     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12533     /**
12534     * @event beforeload
12535     * Fires before this node is loaded, return false to cancel
12536     * @param {Node} this This node
12537     */
12538     this.addEvents({'beforeload':true, 'load': true});
12539     /**
12540     * @event load
12541     * Fires when this node is loaded
12542     * @param {Node} this This node
12543     */
12544     /**
12545      * The loader used by this node (defaults to using the tree's defined loader)
12546      * @type TreeLoader
12547      * @property loader
12548      */
12549 };
12550 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12551     expand : function(deep, anim, callback){
12552         if(this.loading){ // if an async load is already running, waiting til it's done
12553             var timer;
12554             var f = function(){
12555                 if(!this.loading){ // done loading
12556                     clearInterval(timer);
12557                     this.expand(deep, anim, callback);
12558                 }
12559             }.createDelegate(this);
12560             timer = setInterval(f, 200);
12561             return;
12562         }
12563         if(!this.loaded){
12564             if(this.fireEvent("beforeload", this) === false){
12565                 return;
12566             }
12567             this.loading = true;
12568             this.ui.beforeLoad(this);
12569             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12570             if(loader){
12571                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12572                 return;
12573             }
12574         }
12575         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12576     },
12577     
12578     /**
12579      * Returns true if this node is currently loading
12580      * @return {Boolean}
12581      */
12582     isLoading : function(){
12583         return this.loading;  
12584     },
12585     
12586     loadComplete : function(deep, anim, callback){
12587         this.loading = false;
12588         this.loaded = true;
12589         this.ui.afterLoad(this);
12590         this.fireEvent("load", this);
12591         this.expand(deep, anim, callback);
12592     },
12593     
12594     /**
12595      * Returns true if this node has been loaded
12596      * @return {Boolean}
12597      */
12598     isLoaded : function(){
12599         return this.loaded;
12600     },
12601     
12602     hasChildNodes : function(){
12603         if(!this.isLeaf() && !this.loaded){
12604             return true;
12605         }else{
12606             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12607         }
12608     },
12609
12610     /**
12611      * Trigger a reload for this node
12612      * @param {Function} callback
12613      */
12614     reload : function(callback){
12615         this.collapse(false, false);
12616         while(this.firstChild){
12617             this.removeChild(this.firstChild);
12618         }
12619         this.childrenRendered = false;
12620         this.loaded = false;
12621         if(this.isHiddenRoot()){
12622             this.expanded = false;
12623         }
12624         this.expand(false, false, callback);
12625     }
12626 });/*
12627  * Based on:
12628  * Ext JS Library 1.1.1
12629  * Copyright(c) 2006-2007, Ext JS, LLC.
12630  *
12631  * Originally Released Under LGPL - original licence link has changed is not relivant.
12632  *
12633  * Fork - LGPL
12634  * <script type="text/javascript">
12635  */
12636  
12637 /**
12638  * @class Roo.tree.TreeNodeUI
12639  * @constructor
12640  * @param {Object} node The node to render
12641  * The TreeNode UI implementation is separate from the
12642  * tree implementation. Unless you are customizing the tree UI,
12643  * you should never have to use this directly.
12644  */
12645 Roo.tree.TreeNodeUI = function(node){
12646     this.node = node;
12647     this.rendered = false;
12648     this.animating = false;
12649     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12650 };
12651
12652 Roo.tree.TreeNodeUI.prototype = {
12653     removeChild : function(node){
12654         if(this.rendered){
12655             this.ctNode.removeChild(node.ui.getEl());
12656         }
12657     },
12658
12659     beforeLoad : function(){
12660          this.addClass("x-tree-node-loading");
12661     },
12662
12663     afterLoad : function(){
12664          this.removeClass("x-tree-node-loading");
12665     },
12666
12667     onTextChange : function(node, text, oldText){
12668         if(this.rendered){
12669             this.textNode.innerHTML = text;
12670         }
12671     },
12672
12673     onDisableChange : function(node, state){
12674         this.disabled = state;
12675         if(state){
12676             this.addClass("x-tree-node-disabled");
12677         }else{
12678             this.removeClass("x-tree-node-disabled");
12679         }
12680     },
12681
12682     onSelectedChange : function(state){
12683         if(state){
12684             this.focus();
12685             this.addClass("x-tree-selected");
12686         }else{
12687             //this.blur();
12688             this.removeClass("x-tree-selected");
12689         }
12690     },
12691
12692     onMove : function(tree, node, oldParent, newParent, index, refNode){
12693         this.childIndent = null;
12694         if(this.rendered){
12695             var targetNode = newParent.ui.getContainer();
12696             if(!targetNode){//target not rendered
12697                 this.holder = document.createElement("div");
12698                 this.holder.appendChild(this.wrap);
12699                 return;
12700             }
12701             var insertBefore = refNode ? refNode.ui.getEl() : null;
12702             if(insertBefore){
12703                 targetNode.insertBefore(this.wrap, insertBefore);
12704             }else{
12705                 targetNode.appendChild(this.wrap);
12706             }
12707             this.node.renderIndent(true);
12708         }
12709     },
12710
12711     addClass : function(cls){
12712         if(this.elNode){
12713             Roo.fly(this.elNode).addClass(cls);
12714         }
12715     },
12716
12717     removeClass : function(cls){
12718         if(this.elNode){
12719             Roo.fly(this.elNode).removeClass(cls);
12720         }
12721     },
12722
12723     remove : function(){
12724         if(this.rendered){
12725             this.holder = document.createElement("div");
12726             this.holder.appendChild(this.wrap);
12727         }
12728     },
12729
12730     fireEvent : function(){
12731         return this.node.fireEvent.apply(this.node, arguments);
12732     },
12733
12734     initEvents : function(){
12735         this.node.on("move", this.onMove, this);
12736         var E = Roo.EventManager;
12737         var a = this.anchor;
12738
12739         var el = Roo.fly(a, '_treeui');
12740
12741         if(Roo.isOpera){ // opera render bug ignores the CSS
12742             el.setStyle("text-decoration", "none");
12743         }
12744
12745         el.on("click", this.onClick, this);
12746         el.on("dblclick", this.onDblClick, this);
12747
12748         if(this.checkbox){
12749             Roo.EventManager.on(this.checkbox,
12750                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12751         }
12752
12753         el.on("contextmenu", this.onContextMenu, this);
12754
12755         var icon = Roo.fly(this.iconNode);
12756         icon.on("click", this.onClick, this);
12757         icon.on("dblclick", this.onDblClick, this);
12758         icon.on("contextmenu", this.onContextMenu, this);
12759         E.on(this.ecNode, "click", this.ecClick, this, true);
12760
12761         if(this.node.disabled){
12762             this.addClass("x-tree-node-disabled");
12763         }
12764         if(this.node.hidden){
12765             this.addClass("x-tree-node-disabled");
12766         }
12767         var ot = this.node.getOwnerTree();
12768         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12769         if(dd && (!this.node.isRoot || ot.rootVisible)){
12770             Roo.dd.Registry.register(this.elNode, {
12771                 node: this.node,
12772                 handles: this.getDDHandles(),
12773                 isHandle: false
12774             });
12775         }
12776     },
12777
12778     getDDHandles : function(){
12779         return [this.iconNode, this.textNode];
12780     },
12781
12782     hide : function(){
12783         if(this.rendered){
12784             this.wrap.style.display = "none";
12785         }
12786     },
12787
12788     show : function(){
12789         if(this.rendered){
12790             this.wrap.style.display = "";
12791         }
12792     },
12793
12794     onContextMenu : function(e){
12795         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12796             e.preventDefault();
12797             this.focus();
12798             this.fireEvent("contextmenu", this.node, e);
12799         }
12800     },
12801
12802     onClick : function(e){
12803         if(this.dropping){
12804             e.stopEvent();
12805             return;
12806         }
12807         if(this.fireEvent("beforeclick", this.node, e) !== false){
12808             if(!this.disabled && this.node.attributes.href){
12809                 this.fireEvent("click", this.node, e);
12810                 return;
12811             }
12812             e.preventDefault();
12813             if(this.disabled){
12814                 return;
12815             }
12816
12817             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12818                 this.node.toggle();
12819             }
12820
12821             this.fireEvent("click", this.node, e);
12822         }else{
12823             e.stopEvent();
12824         }
12825     },
12826
12827     onDblClick : function(e){
12828         e.preventDefault();
12829         if(this.disabled){
12830             return;
12831         }
12832         if(this.checkbox){
12833             this.toggleCheck();
12834         }
12835         if(!this.animating && this.node.hasChildNodes()){
12836             this.node.toggle();
12837         }
12838         this.fireEvent("dblclick", this.node, e);
12839     },
12840
12841     onCheckChange : function(){
12842         var checked = this.checkbox.checked;
12843         this.node.attributes.checked = checked;
12844         this.fireEvent('checkchange', this.node, checked);
12845     },
12846
12847     ecClick : function(e){
12848         if(!this.animating && this.node.hasChildNodes()){
12849             this.node.toggle();
12850         }
12851     },
12852
12853     startDrop : function(){
12854         this.dropping = true;
12855     },
12856
12857     // delayed drop so the click event doesn't get fired on a drop
12858     endDrop : function(){
12859        setTimeout(function(){
12860            this.dropping = false;
12861        }.createDelegate(this), 50);
12862     },
12863
12864     expand : function(){
12865         this.updateExpandIcon();
12866         this.ctNode.style.display = "";
12867     },
12868
12869     focus : function(){
12870         if(!this.node.preventHScroll){
12871             try{this.anchor.focus();
12872             }catch(e){}
12873         }else if(!Roo.isIE){
12874             try{
12875                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12876                 var l = noscroll.scrollLeft;
12877                 this.anchor.focus();
12878                 noscroll.scrollLeft = l;
12879             }catch(e){}
12880         }
12881     },
12882
12883     toggleCheck : function(value){
12884         var cb = this.checkbox;
12885         if(cb){
12886             cb.checked = (value === undefined ? !cb.checked : value);
12887         }
12888     },
12889
12890     blur : function(){
12891         try{
12892             this.anchor.blur();
12893         }catch(e){}
12894     },
12895
12896     animExpand : function(callback){
12897         var ct = Roo.get(this.ctNode);
12898         ct.stopFx();
12899         if(!this.node.hasChildNodes()){
12900             this.updateExpandIcon();
12901             this.ctNode.style.display = "";
12902             Roo.callback(callback);
12903             return;
12904         }
12905         this.animating = true;
12906         this.updateExpandIcon();
12907
12908         ct.slideIn('t', {
12909            callback : function(){
12910                this.animating = false;
12911                Roo.callback(callback);
12912             },
12913             scope: this,
12914             duration: this.node.ownerTree.duration || .25
12915         });
12916     },
12917
12918     highlight : function(){
12919         var tree = this.node.getOwnerTree();
12920         Roo.fly(this.wrap).highlight(
12921             tree.hlColor || "C3DAF9",
12922             {endColor: tree.hlBaseColor}
12923         );
12924     },
12925
12926     collapse : function(){
12927         this.updateExpandIcon();
12928         this.ctNode.style.display = "none";
12929     },
12930
12931     animCollapse : function(callback){
12932         var ct = Roo.get(this.ctNode);
12933         ct.enableDisplayMode('block');
12934         ct.stopFx();
12935
12936         this.animating = true;
12937         this.updateExpandIcon();
12938
12939         ct.slideOut('t', {
12940             callback : function(){
12941                this.animating = false;
12942                Roo.callback(callback);
12943             },
12944             scope: this,
12945             duration: this.node.ownerTree.duration || .25
12946         });
12947     },
12948
12949     getContainer : function(){
12950         return this.ctNode;
12951     },
12952
12953     getEl : function(){
12954         return this.wrap;
12955     },
12956
12957     appendDDGhost : function(ghostNode){
12958         ghostNode.appendChild(this.elNode.cloneNode(true));
12959     },
12960
12961     getDDRepairXY : function(){
12962         return Roo.lib.Dom.getXY(this.iconNode);
12963     },
12964
12965     onRender : function(){
12966         this.render();
12967     },
12968
12969     render : function(bulkRender){
12970         var n = this.node, a = n.attributes;
12971         var targetNode = n.parentNode ?
12972               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12973
12974         if(!this.rendered){
12975             this.rendered = true;
12976
12977             this.renderElements(n, a, targetNode, bulkRender);
12978
12979             if(a.qtip){
12980                if(this.textNode.setAttributeNS){
12981                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12982                    if(a.qtipTitle){
12983                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12984                    }
12985                }else{
12986                    this.textNode.setAttribute("ext:qtip", a.qtip);
12987                    if(a.qtipTitle){
12988                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12989                    }
12990                }
12991             }else if(a.qtipCfg){
12992                 a.qtipCfg.target = Roo.id(this.textNode);
12993                 Roo.QuickTips.register(a.qtipCfg);
12994             }
12995             this.initEvents();
12996             if(!this.node.expanded){
12997                 this.updateExpandIcon();
12998             }
12999         }else{
13000             if(bulkRender === true) {
13001                 targetNode.appendChild(this.wrap);
13002             }
13003         }
13004     },
13005
13006     renderElements : function(n, a, targetNode, bulkRender)
13007     {
13008         // add some indent caching, this helps performance when rendering a large tree
13009         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13010         var t = n.getOwnerTree();
13011         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13012         if (typeof(n.attributes.html) != 'undefined') {
13013             txt = n.attributes.html;
13014         }
13015         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13016         var cb = typeof a.checked == 'boolean';
13017         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13018         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13019             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13020             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13021             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13022             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13023             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13024              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13025                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13026             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13027             "</li>"];
13028
13029         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13030             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13031                                 n.nextSibling.ui.getEl(), buf.join(""));
13032         }else{
13033             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13034         }
13035
13036         this.elNode = this.wrap.childNodes[0];
13037         this.ctNode = this.wrap.childNodes[1];
13038         var cs = this.elNode.childNodes;
13039         this.indentNode = cs[0];
13040         this.ecNode = cs[1];
13041         this.iconNode = cs[2];
13042         var index = 3;
13043         if(cb){
13044             this.checkbox = cs[3];
13045             index++;
13046         }
13047         this.anchor = cs[index];
13048         this.textNode = cs[index].firstChild;
13049     },
13050
13051     getAnchor : function(){
13052         return this.anchor;
13053     },
13054
13055     getTextEl : function(){
13056         return this.textNode;
13057     },
13058
13059     getIconEl : function(){
13060         return this.iconNode;
13061     },
13062
13063     isChecked : function(){
13064         return this.checkbox ? this.checkbox.checked : false;
13065     },
13066
13067     updateExpandIcon : function(){
13068         if(this.rendered){
13069             var n = this.node, c1, c2;
13070             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13071             var hasChild = n.hasChildNodes();
13072             if(hasChild){
13073                 if(n.expanded){
13074                     cls += "-minus";
13075                     c1 = "x-tree-node-collapsed";
13076                     c2 = "x-tree-node-expanded";
13077                 }else{
13078                     cls += "-plus";
13079                     c1 = "x-tree-node-expanded";
13080                     c2 = "x-tree-node-collapsed";
13081                 }
13082                 if(this.wasLeaf){
13083                     this.removeClass("x-tree-node-leaf");
13084                     this.wasLeaf = false;
13085                 }
13086                 if(this.c1 != c1 || this.c2 != c2){
13087                     Roo.fly(this.elNode).replaceClass(c1, c2);
13088                     this.c1 = c1; this.c2 = c2;
13089                 }
13090             }else{
13091                 // this changes non-leafs into leafs if they have no children.
13092                 // it's not very rational behaviour..
13093                 
13094                 if(!this.wasLeaf && this.node.leaf){
13095                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13096                     delete this.c1;
13097                     delete this.c2;
13098                     this.wasLeaf = true;
13099                 }
13100             }
13101             var ecc = "x-tree-ec-icon "+cls;
13102             if(this.ecc != ecc){
13103                 this.ecNode.className = ecc;
13104                 this.ecc = ecc;
13105             }
13106         }
13107     },
13108
13109     getChildIndent : function(){
13110         if(!this.childIndent){
13111             var buf = [];
13112             var p = this.node;
13113             while(p){
13114                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13115                     if(!p.isLast()) {
13116                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13117                     } else {
13118                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13119                     }
13120                 }
13121                 p = p.parentNode;
13122             }
13123             this.childIndent = buf.join("");
13124         }
13125         return this.childIndent;
13126     },
13127
13128     renderIndent : function(){
13129         if(this.rendered){
13130             var indent = "";
13131             var p = this.node.parentNode;
13132             if(p){
13133                 indent = p.ui.getChildIndent();
13134             }
13135             if(this.indentMarkup != indent){ // don't rerender if not required
13136                 this.indentNode.innerHTML = indent;
13137                 this.indentMarkup = indent;
13138             }
13139             this.updateExpandIcon();
13140         }
13141     }
13142 };
13143
13144 Roo.tree.RootTreeNodeUI = function(){
13145     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13146 };
13147 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13148     render : function(){
13149         if(!this.rendered){
13150             var targetNode = this.node.ownerTree.innerCt.dom;
13151             this.node.expanded = true;
13152             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13153             this.wrap = this.ctNode = targetNode.firstChild;
13154         }
13155     },
13156     collapse : function(){
13157     },
13158     expand : function(){
13159     }
13160 });/*
13161  * Based on:
13162  * Ext JS Library 1.1.1
13163  * Copyright(c) 2006-2007, Ext JS, LLC.
13164  *
13165  * Originally Released Under LGPL - original licence link has changed is not relivant.
13166  *
13167  * Fork - LGPL
13168  * <script type="text/javascript">
13169  */
13170 /**
13171  * @class Roo.tree.TreeLoader
13172  * @extends Roo.util.Observable
13173  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13174  * nodes from a specified URL. The response must be a javascript Array definition
13175  * who's elements are node definition objects. eg:
13176  * <pre><code>
13177 {  success : true,
13178    data :      [
13179    
13180     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13181     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13182     ]
13183 }
13184
13185
13186 </code></pre>
13187  * <br><br>
13188  * The old style respose with just an array is still supported, but not recommended.
13189  * <br><br>
13190  *
13191  * A server request is sent, and child nodes are loaded only when a node is expanded.
13192  * The loading node's id is passed to the server under the parameter name "node" to
13193  * enable the server to produce the correct child nodes.
13194  * <br><br>
13195  * To pass extra parameters, an event handler may be attached to the "beforeload"
13196  * event, and the parameters specified in the TreeLoader's baseParams property:
13197  * <pre><code>
13198     myTreeLoader.on("beforeload", function(treeLoader, node) {
13199         this.baseParams.category = node.attributes.category;
13200     }, this);
13201     
13202 </code></pre>
13203  *
13204  * This would pass an HTTP parameter called "category" to the server containing
13205  * the value of the Node's "category" attribute.
13206  * @constructor
13207  * Creates a new Treeloader.
13208  * @param {Object} config A config object containing config properties.
13209  */
13210 Roo.tree.TreeLoader = function(config){
13211     this.baseParams = {};
13212     this.requestMethod = "POST";
13213     Roo.apply(this, config);
13214
13215     this.addEvents({
13216     
13217         /**
13218          * @event beforeload
13219          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13220          * @param {Object} This TreeLoader object.
13221          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13222          * @param {Object} callback The callback function specified in the {@link #load} call.
13223          */
13224         beforeload : true,
13225         /**
13226          * @event load
13227          * Fires when the node has been successfuly loaded.
13228          * @param {Object} This TreeLoader object.
13229          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13230          * @param {Object} response The response object containing the data from the server.
13231          */
13232         load : true,
13233         /**
13234          * @event loadexception
13235          * Fires if the network request failed.
13236          * @param {Object} This TreeLoader object.
13237          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13238          * @param {Object} response The response object containing the data from the server.
13239          */
13240         loadexception : true,
13241         /**
13242          * @event create
13243          * Fires before a node is created, enabling you to return custom Node types 
13244          * @param {Object} This TreeLoader object.
13245          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13246          */
13247         create : true
13248     });
13249
13250     Roo.tree.TreeLoader.superclass.constructor.call(this);
13251 };
13252
13253 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13254     /**
13255     * @cfg {String} dataUrl The URL from which to request a Json string which
13256     * specifies an array of node definition object representing the child nodes
13257     * to be loaded.
13258     */
13259     /**
13260     * @cfg {String} requestMethod either GET or POST
13261     * defaults to POST (due to BC)
13262     * to be loaded.
13263     */
13264     /**
13265     * @cfg {Object} baseParams (optional) An object containing properties which
13266     * specify HTTP parameters to be passed to each request for child nodes.
13267     */
13268     /**
13269     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13270     * created by this loader. If the attributes sent by the server have an attribute in this object,
13271     * they take priority.
13272     */
13273     /**
13274     * @cfg {Object} uiProviders (optional) An object containing properties which
13275     * 
13276     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13277     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13278     * <i>uiProvider</i> attribute of a returned child node is a string rather
13279     * than a reference to a TreeNodeUI implementation, this that string value
13280     * is used as a property name in the uiProviders object. You can define the provider named
13281     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13282     */
13283     uiProviders : {},
13284
13285     /**
13286     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13287     * child nodes before loading.
13288     */
13289     clearOnLoad : true,
13290
13291     /**
13292     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13293     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13294     * Grid query { data : [ .....] }
13295     */
13296     
13297     root : false,
13298      /**
13299     * @cfg {String} queryParam (optional) 
13300     * Name of the query as it will be passed on the querystring (defaults to 'node')
13301     * eg. the request will be ?node=[id]
13302     */
13303     
13304     
13305     queryParam: false,
13306     
13307     /**
13308      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13309      * This is called automatically when a node is expanded, but may be used to reload
13310      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13311      * @param {Roo.tree.TreeNode} node
13312      * @param {Function} callback
13313      */
13314     load : function(node, callback){
13315         if(this.clearOnLoad){
13316             while(node.firstChild){
13317                 node.removeChild(node.firstChild);
13318             }
13319         }
13320         if(node.attributes.children){ // preloaded json children
13321             var cs = node.attributes.children;
13322             for(var i = 0, len = cs.length; i < len; i++){
13323                 node.appendChild(this.createNode(cs[i]));
13324             }
13325             if(typeof callback == "function"){
13326                 callback();
13327             }
13328         }else if(this.dataUrl){
13329             this.requestData(node, callback);
13330         }
13331     },
13332
13333     getParams: function(node){
13334         var buf = [], bp = this.baseParams;
13335         for(var key in bp){
13336             if(typeof bp[key] != "function"){
13337                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13338             }
13339         }
13340         var n = this.queryParam === false ? 'node' : this.queryParam;
13341         buf.push(n + "=", encodeURIComponent(node.id));
13342         return buf.join("");
13343     },
13344
13345     requestData : function(node, callback){
13346         if(this.fireEvent("beforeload", this, node, callback) !== false){
13347             this.transId = Roo.Ajax.request({
13348                 method:this.requestMethod,
13349                 url: this.dataUrl||this.url,
13350                 success: this.handleResponse,
13351                 failure: this.handleFailure,
13352                 scope: this,
13353                 argument: {callback: callback, node: node},
13354                 params: this.getParams(node)
13355             });
13356         }else{
13357             // if the load is cancelled, make sure we notify
13358             // the node that we are done
13359             if(typeof callback == "function"){
13360                 callback();
13361             }
13362         }
13363     },
13364
13365     isLoading : function(){
13366         return this.transId ? true : false;
13367     },
13368
13369     abort : function(){
13370         if(this.isLoading()){
13371             Roo.Ajax.abort(this.transId);
13372         }
13373     },
13374
13375     // private
13376     createNode : function(attr)
13377     {
13378         // apply baseAttrs, nice idea Corey!
13379         if(this.baseAttrs){
13380             Roo.applyIf(attr, this.baseAttrs);
13381         }
13382         if(this.applyLoader !== false){
13383             attr.loader = this;
13384         }
13385         // uiProvider = depreciated..
13386         
13387         if(typeof(attr.uiProvider) == 'string'){
13388            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13389                 /**  eval:var:attr */ eval(attr.uiProvider);
13390         }
13391         if(typeof(this.uiProviders['default']) != 'undefined') {
13392             attr.uiProvider = this.uiProviders['default'];
13393         }
13394         
13395         this.fireEvent('create', this, attr);
13396         
13397         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13398         return(attr.leaf ?
13399                         new Roo.tree.TreeNode(attr) :
13400                         new Roo.tree.AsyncTreeNode(attr));
13401     },
13402
13403     processResponse : function(response, node, callback)
13404     {
13405         var json = response.responseText;
13406         try {
13407             
13408             var o = Roo.decode(json);
13409             
13410             if (this.root === false && typeof(o.success) != undefined) {
13411                 this.root = 'data'; // the default behaviour for list like data..
13412                 }
13413                 
13414             if (this.root !== false &&  !o.success) {
13415                 // it's a failure condition.
13416                 var a = response.argument;
13417                 this.fireEvent("loadexception", this, a.node, response);
13418                 Roo.log("Load failed - should have a handler really");
13419                 return;
13420             }
13421             
13422             
13423             
13424             if (this.root !== false) {
13425                  o = o[this.root];
13426             }
13427             
13428             for(var i = 0, len = o.length; i < len; i++){
13429                 var n = this.createNode(o[i]);
13430                 if(n){
13431                     node.appendChild(n);
13432                 }
13433             }
13434             if(typeof callback == "function"){
13435                 callback(this, node);
13436             }
13437         }catch(e){
13438             this.handleFailure(response);
13439         }
13440     },
13441
13442     handleResponse : function(response){
13443         this.transId = false;
13444         var a = response.argument;
13445         this.processResponse(response, a.node, a.callback);
13446         this.fireEvent("load", this, a.node, response);
13447     },
13448
13449     handleFailure : function(response)
13450     {
13451         // should handle failure better..
13452         this.transId = false;
13453         var a = response.argument;
13454         this.fireEvent("loadexception", this, a.node, response);
13455         if(typeof a.callback == "function"){
13456             a.callback(this, a.node);
13457         }
13458     }
13459 });/*
13460  * Based on:
13461  * Ext JS Library 1.1.1
13462  * Copyright(c) 2006-2007, Ext JS, LLC.
13463  *
13464  * Originally Released Under LGPL - original licence link has changed is not relivant.
13465  *
13466  * Fork - LGPL
13467  * <script type="text/javascript">
13468  */
13469
13470 /**
13471 * @class Roo.tree.TreeFilter
13472 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13473 * @param {TreePanel} tree
13474 * @param {Object} config (optional)
13475  */
13476 Roo.tree.TreeFilter = function(tree, config){
13477     this.tree = tree;
13478     this.filtered = {};
13479     Roo.apply(this, config);
13480 };
13481
13482 Roo.tree.TreeFilter.prototype = {
13483     clearBlank:false,
13484     reverse:false,
13485     autoClear:false,
13486     remove:false,
13487
13488      /**
13489      * Filter the data by a specific attribute.
13490      * @param {String/RegExp} value Either string that the attribute value
13491      * should start with or a RegExp to test against the attribute
13492      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13493      * @param {TreeNode} startNode (optional) The node to start the filter at.
13494      */
13495     filter : function(value, attr, startNode){
13496         attr = attr || "text";
13497         var f;
13498         if(typeof value == "string"){
13499             var vlen = value.length;
13500             // auto clear empty filter
13501             if(vlen == 0 && this.clearBlank){
13502                 this.clear();
13503                 return;
13504             }
13505             value = value.toLowerCase();
13506             f = function(n){
13507                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13508             };
13509         }else if(value.exec){ // regex?
13510             f = function(n){
13511                 return value.test(n.attributes[attr]);
13512             };
13513         }else{
13514             throw 'Illegal filter type, must be string or regex';
13515         }
13516         this.filterBy(f, null, startNode);
13517         },
13518
13519     /**
13520      * Filter by a function. The passed function will be called with each
13521      * node in the tree (or from the startNode). If the function returns true, the node is kept
13522      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13523      * @param {Function} fn The filter function
13524      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13525      */
13526     filterBy : function(fn, scope, startNode){
13527         startNode = startNode || this.tree.root;
13528         if(this.autoClear){
13529             this.clear();
13530         }
13531         var af = this.filtered, rv = this.reverse;
13532         var f = function(n){
13533             if(n == startNode){
13534                 return true;
13535             }
13536             if(af[n.id]){
13537                 return false;
13538             }
13539             var m = fn.call(scope || n, n);
13540             if(!m || rv){
13541                 af[n.id] = n;
13542                 n.ui.hide();
13543                 return false;
13544             }
13545             return true;
13546         };
13547         startNode.cascade(f);
13548         if(this.remove){
13549            for(var id in af){
13550                if(typeof id != "function"){
13551                    var n = af[id];
13552                    if(n && n.parentNode){
13553                        n.parentNode.removeChild(n);
13554                    }
13555                }
13556            }
13557         }
13558     },
13559
13560     /**
13561      * Clears the current filter. Note: with the "remove" option
13562      * set a filter cannot be cleared.
13563      */
13564     clear : function(){
13565         var t = this.tree;
13566         var af = this.filtered;
13567         for(var id in af){
13568             if(typeof id != "function"){
13569                 var n = af[id];
13570                 if(n){
13571                     n.ui.show();
13572                 }
13573             }
13574         }
13575         this.filtered = {};
13576     }
13577 };
13578 /*
13579  * Based on:
13580  * Ext JS Library 1.1.1
13581  * Copyright(c) 2006-2007, Ext JS, LLC.
13582  *
13583  * Originally Released Under LGPL - original licence link has changed is not relivant.
13584  *
13585  * Fork - LGPL
13586  * <script type="text/javascript">
13587  */
13588  
13589
13590 /**
13591  * @class Roo.tree.TreeSorter
13592  * Provides sorting of nodes in a TreePanel
13593  * 
13594  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13595  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13596  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13597  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13598  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13599  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13600  * @constructor
13601  * @param {TreePanel} tree
13602  * @param {Object} config
13603  */
13604 Roo.tree.TreeSorter = function(tree, config){
13605     Roo.apply(this, config);
13606     tree.on("beforechildrenrendered", this.doSort, this);
13607     tree.on("append", this.updateSort, this);
13608     tree.on("insert", this.updateSort, this);
13609     
13610     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13611     var p = this.property || "text";
13612     var sortType = this.sortType;
13613     var fs = this.folderSort;
13614     var cs = this.caseSensitive === true;
13615     var leafAttr = this.leafAttr || 'leaf';
13616
13617     this.sortFn = function(n1, n2){
13618         if(fs){
13619             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13620                 return 1;
13621             }
13622             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13623                 return -1;
13624             }
13625         }
13626         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13627         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13628         if(v1 < v2){
13629                         return dsc ? +1 : -1;
13630                 }else if(v1 > v2){
13631                         return dsc ? -1 : +1;
13632         }else{
13633                 return 0;
13634         }
13635     };
13636 };
13637
13638 Roo.tree.TreeSorter.prototype = {
13639     doSort : function(node){
13640         node.sort(this.sortFn);
13641     },
13642     
13643     compareNodes : function(n1, n2){
13644         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13645     },
13646     
13647     updateSort : function(tree, node){
13648         if(node.childrenRendered){
13649             this.doSort.defer(1, this, [node]);
13650         }
13651     }
13652 };/*
13653  * Based on:
13654  * Ext JS Library 1.1.1
13655  * Copyright(c) 2006-2007, Ext JS, LLC.
13656  *
13657  * Originally Released Under LGPL - original licence link has changed is not relivant.
13658  *
13659  * Fork - LGPL
13660  * <script type="text/javascript">
13661  */
13662
13663 if(Roo.dd.DropZone){
13664     
13665 Roo.tree.TreeDropZone = function(tree, config){
13666     this.allowParentInsert = false;
13667     this.allowContainerDrop = false;
13668     this.appendOnly = false;
13669     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13670     this.tree = tree;
13671     this.lastInsertClass = "x-tree-no-status";
13672     this.dragOverData = {};
13673 };
13674
13675 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13676     ddGroup : "TreeDD",
13677     scroll:  true,
13678     
13679     expandDelay : 1000,
13680     
13681     expandNode : function(node){
13682         if(node.hasChildNodes() && !node.isExpanded()){
13683             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13684         }
13685     },
13686     
13687     queueExpand : function(node){
13688         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13689     },
13690     
13691     cancelExpand : function(){
13692         if(this.expandProcId){
13693             clearTimeout(this.expandProcId);
13694             this.expandProcId = false;
13695         }
13696     },
13697     
13698     isValidDropPoint : function(n, pt, dd, e, data){
13699         if(!n || !data){ return false; }
13700         var targetNode = n.node;
13701         var dropNode = data.node;
13702         // default drop rules
13703         if(!(targetNode && targetNode.isTarget && pt)){
13704             return false;
13705         }
13706         if(pt == "append" && targetNode.allowChildren === false){
13707             return false;
13708         }
13709         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13710             return false;
13711         }
13712         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13713             return false;
13714         }
13715         // reuse the object
13716         var overEvent = this.dragOverData;
13717         overEvent.tree = this.tree;
13718         overEvent.target = targetNode;
13719         overEvent.data = data;
13720         overEvent.point = pt;
13721         overEvent.source = dd;
13722         overEvent.rawEvent = e;
13723         overEvent.dropNode = dropNode;
13724         overEvent.cancel = false;  
13725         var result = this.tree.fireEvent("nodedragover", overEvent);
13726         return overEvent.cancel === false && result !== false;
13727     },
13728     
13729     getDropPoint : function(e, n, dd)
13730     {
13731         var tn = n.node;
13732         if(tn.isRoot){
13733             return tn.allowChildren !== false ? "append" : false; // always append for root
13734         }
13735         var dragEl = n.ddel;
13736         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13737         var y = Roo.lib.Event.getPageY(e);
13738         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13739         
13740         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13741         var noAppend = tn.allowChildren === false;
13742         if(this.appendOnly || tn.parentNode.allowChildren === false){
13743             return noAppend ? false : "append";
13744         }
13745         var noBelow = false;
13746         if(!this.allowParentInsert){
13747             noBelow = tn.hasChildNodes() && tn.isExpanded();
13748         }
13749         var q = (b - t) / (noAppend ? 2 : 3);
13750         if(y >= t && y < (t + q)){
13751             return "above";
13752         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13753             return "below";
13754         }else{
13755             return "append";
13756         }
13757     },
13758     
13759     onNodeEnter : function(n, dd, e, data)
13760     {
13761         this.cancelExpand();
13762     },
13763     
13764     onNodeOver : function(n, dd, e, data)
13765     {
13766        
13767         var pt = this.getDropPoint(e, n, dd);
13768         var node = n.node;
13769         
13770         // auto node expand check
13771         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13772             this.queueExpand(node);
13773         }else if(pt != "append"){
13774             this.cancelExpand();
13775         }
13776         
13777         // set the insert point style on the target node
13778         var returnCls = this.dropNotAllowed;
13779         if(this.isValidDropPoint(n, pt, dd, e, data)){
13780            if(pt){
13781                var el = n.ddel;
13782                var cls;
13783                if(pt == "above"){
13784                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13785                    cls = "x-tree-drag-insert-above";
13786                }else if(pt == "below"){
13787                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13788                    cls = "x-tree-drag-insert-below";
13789                }else{
13790                    returnCls = "x-tree-drop-ok-append";
13791                    cls = "x-tree-drag-append";
13792                }
13793                if(this.lastInsertClass != cls){
13794                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13795                    this.lastInsertClass = cls;
13796                }
13797            }
13798        }
13799        return returnCls;
13800     },
13801     
13802     onNodeOut : function(n, dd, e, data){
13803         
13804         this.cancelExpand();
13805         this.removeDropIndicators(n);
13806     },
13807     
13808     onNodeDrop : function(n, dd, e, data){
13809         var point = this.getDropPoint(e, n, dd);
13810         var targetNode = n.node;
13811         targetNode.ui.startDrop();
13812         if(!this.isValidDropPoint(n, point, dd, e, data)){
13813             targetNode.ui.endDrop();
13814             return false;
13815         }
13816         // first try to find the drop node
13817         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13818         var dropEvent = {
13819             tree : this.tree,
13820             target: targetNode,
13821             data: data,
13822             point: point,
13823             source: dd,
13824             rawEvent: e,
13825             dropNode: dropNode,
13826             cancel: !dropNode   
13827         };
13828         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13829         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13830             targetNode.ui.endDrop();
13831             return false;
13832         }
13833         // allow target changing
13834         targetNode = dropEvent.target;
13835         if(point == "append" && !targetNode.isExpanded()){
13836             targetNode.expand(false, null, function(){
13837                 this.completeDrop(dropEvent);
13838             }.createDelegate(this));
13839         }else{
13840             this.completeDrop(dropEvent);
13841         }
13842         return true;
13843     },
13844     
13845     completeDrop : function(de){
13846         var ns = de.dropNode, p = de.point, t = de.target;
13847         if(!(ns instanceof Array)){
13848             ns = [ns];
13849         }
13850         var n;
13851         for(var i = 0, len = ns.length; i < len; i++){
13852             n = ns[i];
13853             if(p == "above"){
13854                 t.parentNode.insertBefore(n, t);
13855             }else if(p == "below"){
13856                 t.parentNode.insertBefore(n, t.nextSibling);
13857             }else{
13858                 t.appendChild(n);
13859             }
13860         }
13861         n.ui.focus();
13862         if(this.tree.hlDrop){
13863             n.ui.highlight();
13864         }
13865         t.ui.endDrop();
13866         this.tree.fireEvent("nodedrop", de);
13867     },
13868     
13869     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13870         if(this.tree.hlDrop){
13871             dropNode.ui.focus();
13872             dropNode.ui.highlight();
13873         }
13874         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13875     },
13876     
13877     getTree : function(){
13878         return this.tree;
13879     },
13880     
13881     removeDropIndicators : function(n){
13882         if(n && n.ddel){
13883             var el = n.ddel;
13884             Roo.fly(el).removeClass([
13885                     "x-tree-drag-insert-above",
13886                     "x-tree-drag-insert-below",
13887                     "x-tree-drag-append"]);
13888             this.lastInsertClass = "_noclass";
13889         }
13890     },
13891     
13892     beforeDragDrop : function(target, e, id){
13893         this.cancelExpand();
13894         return true;
13895     },
13896     
13897     afterRepair : function(data){
13898         if(data && Roo.enableFx){
13899             data.node.ui.highlight();
13900         }
13901         this.hideProxy();
13902     } 
13903     
13904 });
13905
13906 }
13907 /*
13908  * Based on:
13909  * Ext JS Library 1.1.1
13910  * Copyright(c) 2006-2007, Ext JS, LLC.
13911  *
13912  * Originally Released Under LGPL - original licence link has changed is not relivant.
13913  *
13914  * Fork - LGPL
13915  * <script type="text/javascript">
13916  */
13917  
13918
13919 if(Roo.dd.DragZone){
13920 Roo.tree.TreeDragZone = function(tree, config){
13921     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13922     this.tree = tree;
13923 };
13924
13925 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13926     ddGroup : "TreeDD",
13927    
13928     onBeforeDrag : function(data, e){
13929         var n = data.node;
13930         return n && n.draggable && !n.disabled;
13931     },
13932      
13933     
13934     onInitDrag : function(e){
13935         var data = this.dragData;
13936         this.tree.getSelectionModel().select(data.node);
13937         this.proxy.update("");
13938         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13939         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13940     },
13941     
13942     getRepairXY : function(e, data){
13943         return data.node.ui.getDDRepairXY();
13944     },
13945     
13946     onEndDrag : function(data, e){
13947         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13948         
13949         
13950     },
13951     
13952     onValidDrop : function(dd, e, id){
13953         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13954         this.hideProxy();
13955     },
13956     
13957     beforeInvalidDrop : function(e, id){
13958         // this scrolls the original position back into view
13959         var sm = this.tree.getSelectionModel();
13960         sm.clearSelections();
13961         sm.select(this.dragData.node);
13962     }
13963 });
13964 }/*
13965  * Based on:
13966  * Ext JS Library 1.1.1
13967  * Copyright(c) 2006-2007, Ext JS, LLC.
13968  *
13969  * Originally Released Under LGPL - original licence link has changed is not relivant.
13970  *
13971  * Fork - LGPL
13972  * <script type="text/javascript">
13973  */
13974 /**
13975  * @class Roo.tree.TreeEditor
13976  * @extends Roo.Editor
13977  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13978  * as the editor field.
13979  * @constructor
13980  * @param {Object} config (used to be the tree panel.)
13981  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13982  * 
13983  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13984  * @cfg {Roo.form.TextField} field [required] The field configuration
13985  *
13986  * 
13987  */
13988 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13989     var tree = config;
13990     var field;
13991     if (oldconfig) { // old style..
13992         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13993     } else {
13994         // new style..
13995         tree = config.tree;
13996         config.field = config.field  || {};
13997         config.field.xtype = 'TextField';
13998         field = Roo.factory(config.field, Roo.form);
13999     }
14000     config = config || {};
14001     
14002     
14003     this.addEvents({
14004         /**
14005          * @event beforenodeedit
14006          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14007          * false from the handler of this event.
14008          * @param {Editor} this
14009          * @param {Roo.tree.Node} node 
14010          */
14011         "beforenodeedit" : true
14012     });
14013     
14014     //Roo.log(config);
14015     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14016
14017     this.tree = tree;
14018
14019     tree.on('beforeclick', this.beforeNodeClick, this);
14020     tree.getTreeEl().on('mousedown', this.hide, this);
14021     this.on('complete', this.updateNode, this);
14022     this.on('beforestartedit', this.fitToTree, this);
14023     this.on('startedit', this.bindScroll, this, {delay:10});
14024     this.on('specialkey', this.onSpecialKey, this);
14025 };
14026
14027 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14028     /**
14029      * @cfg {String} alignment
14030      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14031      */
14032     alignment: "l-l",
14033     // inherit
14034     autoSize: false,
14035     /**
14036      * @cfg {Boolean} hideEl
14037      * True to hide the bound element while the editor is displayed (defaults to false)
14038      */
14039     hideEl : false,
14040     /**
14041      * @cfg {String} cls
14042      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14043      */
14044     cls: "x-small-editor x-tree-editor",
14045     /**
14046      * @cfg {Boolean} shim
14047      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14048      */
14049     shim:false,
14050     // inherit
14051     shadow:"frame",
14052     /**
14053      * @cfg {Number} maxWidth
14054      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14055      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14056      * scroll and client offsets into account prior to each edit.
14057      */
14058     maxWidth: 250,
14059
14060     editDelay : 350,
14061
14062     // private
14063     fitToTree : function(ed, el){
14064         var td = this.tree.getTreeEl().dom, nd = el.dom;
14065         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14066             td.scrollLeft = nd.offsetLeft;
14067         }
14068         var w = Math.min(
14069                 this.maxWidth,
14070                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14071         this.setSize(w, '');
14072         
14073         return this.fireEvent('beforenodeedit', this, this.editNode);
14074         
14075     },
14076
14077     // private
14078     triggerEdit : function(node){
14079         this.completeEdit();
14080         this.editNode = node;
14081         this.startEdit(node.ui.textNode, node.text);
14082     },
14083
14084     // private
14085     bindScroll : function(){
14086         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14087     },
14088
14089     // private
14090     beforeNodeClick : function(node, e){
14091         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14092         this.lastClick = new Date();
14093         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14094             e.stopEvent();
14095             this.triggerEdit(node);
14096             return false;
14097         }
14098         return true;
14099     },
14100
14101     // private
14102     updateNode : function(ed, value){
14103         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14104         this.editNode.setText(value);
14105     },
14106
14107     // private
14108     onHide : function(){
14109         Roo.tree.TreeEditor.superclass.onHide.call(this);
14110         if(this.editNode){
14111             this.editNode.ui.focus();
14112         }
14113     },
14114
14115     // private
14116     onSpecialKey : function(field, e){
14117         var k = e.getKey();
14118         if(k == e.ESC){
14119             e.stopEvent();
14120             this.cancelEdit();
14121         }else if(k == e.ENTER && !e.hasModifier()){
14122             e.stopEvent();
14123             this.completeEdit();
14124         }
14125     }
14126 });//<Script type="text/javascript">
14127 /*
14128  * Based on:
14129  * Ext JS Library 1.1.1
14130  * Copyright(c) 2006-2007, Ext JS, LLC.
14131  *
14132  * Originally Released Under LGPL - original licence link has changed is not relivant.
14133  *
14134  * Fork - LGPL
14135  * <script type="text/javascript">
14136  */
14137  
14138 /**
14139  * Not documented??? - probably should be...
14140  */
14141
14142 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14143     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14144     
14145     renderElements : function(n, a, targetNode, bulkRender){
14146         //consel.log("renderElements?");
14147         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14148
14149         var t = n.getOwnerTree();
14150         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14151         
14152         var cols = t.columns;
14153         var bw = t.borderWidth;
14154         var c = cols[0];
14155         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14156          var cb = typeof a.checked == "boolean";
14157         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14158         var colcls = 'x-t-' + tid + '-c0';
14159         var buf = [
14160             '<li class="x-tree-node">',
14161             
14162                 
14163                 '<div class="x-tree-node-el ', a.cls,'">',
14164                     // extran...
14165                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14166                 
14167                 
14168                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14169                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14170                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14171                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14172                            (a.iconCls ? ' '+a.iconCls : ''),
14173                            '" unselectable="on" />',
14174                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14175                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14176                              
14177                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14178                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14179                             '<span unselectable="on" qtip="' + tx + '">',
14180                              tx,
14181                              '</span></a>' ,
14182                     '</div>',
14183                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14184                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14185                  ];
14186         for(var i = 1, len = cols.length; i < len; i++){
14187             c = cols[i];
14188             colcls = 'x-t-' + tid + '-c' +i;
14189             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14190             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14191                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14192                       "</div>");
14193          }
14194          
14195          buf.push(
14196             '</a>',
14197             '<div class="x-clear"></div></div>',
14198             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14199             "</li>");
14200         
14201         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14202             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14203                                 n.nextSibling.ui.getEl(), buf.join(""));
14204         }else{
14205             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14206         }
14207         var el = this.wrap.firstChild;
14208         this.elRow = el;
14209         this.elNode = el.firstChild;
14210         this.ranchor = el.childNodes[1];
14211         this.ctNode = this.wrap.childNodes[1];
14212         var cs = el.firstChild.childNodes;
14213         this.indentNode = cs[0];
14214         this.ecNode = cs[1];
14215         this.iconNode = cs[2];
14216         var index = 3;
14217         if(cb){
14218             this.checkbox = cs[3];
14219             index++;
14220         }
14221         this.anchor = cs[index];
14222         
14223         this.textNode = cs[index].firstChild;
14224         
14225         //el.on("click", this.onClick, this);
14226         //el.on("dblclick", this.onDblClick, this);
14227         
14228         
14229        // console.log(this);
14230     },
14231     initEvents : function(){
14232         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14233         
14234             
14235         var a = this.ranchor;
14236
14237         var el = Roo.get(a);
14238
14239         if(Roo.isOpera){ // opera render bug ignores the CSS
14240             el.setStyle("text-decoration", "none");
14241         }
14242
14243         el.on("click", this.onClick, this);
14244         el.on("dblclick", this.onDblClick, this);
14245         el.on("contextmenu", this.onContextMenu, this);
14246         
14247     },
14248     
14249     /*onSelectedChange : function(state){
14250         if(state){
14251             this.focus();
14252             this.addClass("x-tree-selected");
14253         }else{
14254             //this.blur();
14255             this.removeClass("x-tree-selected");
14256         }
14257     },*/
14258     addClass : function(cls){
14259         if(this.elRow){
14260             Roo.fly(this.elRow).addClass(cls);
14261         }
14262         
14263     },
14264     
14265     
14266     removeClass : function(cls){
14267         if(this.elRow){
14268             Roo.fly(this.elRow).removeClass(cls);
14269         }
14270     }
14271
14272     
14273     
14274 });//<Script type="text/javascript">
14275
14276 /*
14277  * Based on:
14278  * Ext JS Library 1.1.1
14279  * Copyright(c) 2006-2007, Ext JS, LLC.
14280  *
14281  * Originally Released Under LGPL - original licence link has changed is not relivant.
14282  *
14283  * Fork - LGPL
14284  * <script type="text/javascript">
14285  */
14286  
14287
14288 /**
14289  * @class Roo.tree.ColumnTree
14290  * @extends Roo.data.TreePanel
14291  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14292  * @cfg {int} borderWidth  compined right/left border allowance
14293  * @constructor
14294  * @param {String/HTMLElement/Element} el The container element
14295  * @param {Object} config
14296  */
14297 Roo.tree.ColumnTree =  function(el, config)
14298 {
14299    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14300    this.addEvents({
14301         /**
14302         * @event resize
14303         * Fire this event on a container when it resizes
14304         * @param {int} w Width
14305         * @param {int} h Height
14306         */
14307        "resize" : true
14308     });
14309     this.on('resize', this.onResize, this);
14310 };
14311
14312 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14313     //lines:false,
14314     
14315     
14316     borderWidth: Roo.isBorderBox ? 0 : 2, 
14317     headEls : false,
14318     
14319     render : function(){
14320         // add the header.....
14321        
14322         Roo.tree.ColumnTree.superclass.render.apply(this);
14323         
14324         this.el.addClass('x-column-tree');
14325         
14326         this.headers = this.el.createChild(
14327             {cls:'x-tree-headers'},this.innerCt.dom);
14328    
14329         var cols = this.columns, c;
14330         var totalWidth = 0;
14331         this.headEls = [];
14332         var  len = cols.length;
14333         for(var i = 0; i < len; i++){
14334              c = cols[i];
14335              totalWidth += c.width;
14336             this.headEls.push(this.headers.createChild({
14337                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14338                  cn: {
14339                      cls:'x-tree-hd-text',
14340                      html: c.header
14341                  },
14342                  style:'width:'+(c.width-this.borderWidth)+'px;'
14343              }));
14344         }
14345         this.headers.createChild({cls:'x-clear'});
14346         // prevent floats from wrapping when clipped
14347         this.headers.setWidth(totalWidth);
14348         //this.innerCt.setWidth(totalWidth);
14349         this.innerCt.setStyle({ overflow: 'auto' });
14350         this.onResize(this.width, this.height);
14351              
14352         
14353     },
14354     onResize : function(w,h)
14355     {
14356         this.height = h;
14357         this.width = w;
14358         // resize cols..
14359         this.innerCt.setWidth(this.width);
14360         this.innerCt.setHeight(this.height-20);
14361         
14362         // headers...
14363         var cols = this.columns, c;
14364         var totalWidth = 0;
14365         var expEl = false;
14366         var len = cols.length;
14367         for(var i = 0; i < len; i++){
14368             c = cols[i];
14369             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14370                 // it's the expander..
14371                 expEl  = this.headEls[i];
14372                 continue;
14373             }
14374             totalWidth += c.width;
14375             
14376         }
14377         if (expEl) {
14378             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14379         }
14380         this.headers.setWidth(w-20);
14381
14382         
14383         
14384         
14385     }
14386 });
14387 /*
14388  * Based on:
14389  * Ext JS Library 1.1.1
14390  * Copyright(c) 2006-2007, Ext JS, LLC.
14391  *
14392  * Originally Released Under LGPL - original licence link has changed is not relivant.
14393  *
14394  * Fork - LGPL
14395  * <script type="text/javascript">
14396  */
14397  
14398 /**
14399  * @class Roo.menu.Menu
14400  * @extends Roo.util.Observable
14401  * @children Roo.menu.BaseItem
14402  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14403  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14404  * @constructor
14405  * Creates a new Menu
14406  * @param {Object} config Configuration options
14407  */
14408 Roo.menu.Menu = function(config){
14409     
14410     Roo.menu.Menu.superclass.constructor.call(this, config);
14411     
14412     this.id = this.id || Roo.id();
14413     this.addEvents({
14414         /**
14415          * @event beforeshow
14416          * Fires before this menu is displayed
14417          * @param {Roo.menu.Menu} this
14418          */
14419         beforeshow : true,
14420         /**
14421          * @event beforehide
14422          * Fires before this menu is hidden
14423          * @param {Roo.menu.Menu} this
14424          */
14425         beforehide : true,
14426         /**
14427          * @event show
14428          * Fires after this menu is displayed
14429          * @param {Roo.menu.Menu} this
14430          */
14431         show : true,
14432         /**
14433          * @event hide
14434          * Fires after this menu is hidden
14435          * @param {Roo.menu.Menu} this
14436          */
14437         hide : true,
14438         /**
14439          * @event click
14440          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14441          * @param {Roo.menu.Menu} this
14442          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14443          * @param {Roo.EventObject} e
14444          */
14445         click : true,
14446         /**
14447          * @event mouseover
14448          * Fires when the mouse is hovering over this menu
14449          * @param {Roo.menu.Menu} this
14450          * @param {Roo.EventObject} e
14451          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14452          */
14453         mouseover : true,
14454         /**
14455          * @event mouseout
14456          * Fires when the mouse exits this menu
14457          * @param {Roo.menu.Menu} this
14458          * @param {Roo.EventObject} e
14459          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14460          */
14461         mouseout : true,
14462         /**
14463          * @event itemclick
14464          * Fires when a menu item contained in this menu is clicked
14465          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14466          * @param {Roo.EventObject} e
14467          */
14468         itemclick: true
14469     });
14470     if (this.registerMenu) {
14471         Roo.menu.MenuMgr.register(this);
14472     }
14473     
14474     var mis = this.items;
14475     this.items = new Roo.util.MixedCollection();
14476     if(mis){
14477         this.add.apply(this, mis);
14478     }
14479 };
14480
14481 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14482     /**
14483      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14484      */
14485     minWidth : 120,
14486     /**
14487      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14488      * for bottom-right shadow (defaults to "sides")
14489      */
14490     shadow : "sides",
14491     /**
14492      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14493      * this menu (defaults to "tl-tr?")
14494      */
14495     subMenuAlign : "tl-tr?",
14496     /**
14497      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14498      * relative to its element of origin (defaults to "tl-bl?")
14499      */
14500     defaultAlign : "tl-bl?",
14501     /**
14502      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14503      */
14504     allowOtherMenus : false,
14505     /**
14506      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14507      */
14508     registerMenu : true,
14509
14510     hidden:true,
14511
14512     // private
14513     render : function(){
14514         if(this.el){
14515             return;
14516         }
14517         var el = this.el = new Roo.Layer({
14518             cls: "x-menu",
14519             shadow:this.shadow,
14520             constrain: false,
14521             parentEl: this.parentEl || document.body,
14522             zindex:15000
14523         });
14524
14525         this.keyNav = new Roo.menu.MenuNav(this);
14526
14527         if(this.plain){
14528             el.addClass("x-menu-plain");
14529         }
14530         if(this.cls){
14531             el.addClass(this.cls);
14532         }
14533         // generic focus element
14534         this.focusEl = el.createChild({
14535             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14536         });
14537         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14538         //disabling touch- as it's causing issues ..
14539         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14540         ul.on('click'   , this.onClick, this);
14541         
14542         
14543         ul.on("mouseover", this.onMouseOver, this);
14544         ul.on("mouseout", this.onMouseOut, this);
14545         this.items.each(function(item){
14546             if (item.hidden) {
14547                 return;
14548             }
14549             
14550             var li = document.createElement("li");
14551             li.className = "x-menu-list-item";
14552             ul.dom.appendChild(li);
14553             item.render(li, this);
14554         }, this);
14555         this.ul = ul;
14556         this.autoWidth();
14557     },
14558
14559     // private
14560     autoWidth : function(){
14561         var el = this.el, ul = this.ul;
14562         if(!el){
14563             return;
14564         }
14565         var w = this.width;
14566         if(w){
14567             el.setWidth(w);
14568         }else if(Roo.isIE){
14569             el.setWidth(this.minWidth);
14570             var t = el.dom.offsetWidth; // force recalc
14571             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14572         }
14573     },
14574
14575     // private
14576     delayAutoWidth : function(){
14577         if(this.rendered){
14578             if(!this.awTask){
14579                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14580             }
14581             this.awTask.delay(20);
14582         }
14583     },
14584
14585     // private
14586     findTargetItem : function(e){
14587         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14588         if(t && t.menuItemId){
14589             return this.items.get(t.menuItemId);
14590         }
14591     },
14592
14593     // private
14594     onClick : function(e){
14595         Roo.log("menu.onClick");
14596         var t = this.findTargetItem(e);
14597         if(!t){
14598             return;
14599         }
14600         Roo.log(e);
14601         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14602             if(t == this.activeItem && t.shouldDeactivate(e)){
14603                 this.activeItem.deactivate();
14604                 delete this.activeItem;
14605                 return;
14606             }
14607             if(t.canActivate){
14608                 this.setActiveItem(t, true);
14609             }
14610             return;
14611             
14612             
14613         }
14614         
14615         t.onClick(e);
14616         this.fireEvent("click", this, t, e);
14617     },
14618
14619     // private
14620     setActiveItem : function(item, autoExpand){
14621         if(item != this.activeItem){
14622             if(this.activeItem){
14623                 this.activeItem.deactivate();
14624             }
14625             this.activeItem = item;
14626             item.activate(autoExpand);
14627         }else if(autoExpand){
14628             item.expandMenu();
14629         }
14630     },
14631
14632     // private
14633     tryActivate : function(start, step){
14634         var items = this.items;
14635         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14636             var item = items.get(i);
14637             if(!item.disabled && item.canActivate){
14638                 this.setActiveItem(item, false);
14639                 return item;
14640             }
14641         }
14642         return false;
14643     },
14644
14645     // private
14646     onMouseOver : function(e){
14647         var t;
14648         if(t = this.findTargetItem(e)){
14649             if(t.canActivate && !t.disabled){
14650                 this.setActiveItem(t, true);
14651             }
14652         }
14653         this.fireEvent("mouseover", this, e, t);
14654     },
14655
14656     // private
14657     onMouseOut : function(e){
14658         var t;
14659         if(t = this.findTargetItem(e)){
14660             if(t == this.activeItem && t.shouldDeactivate(e)){
14661                 this.activeItem.deactivate();
14662                 delete this.activeItem;
14663             }
14664         }
14665         this.fireEvent("mouseout", this, e, t);
14666     },
14667
14668     /**
14669      * Read-only.  Returns true if the menu is currently displayed, else false.
14670      * @type Boolean
14671      */
14672     isVisible : function(){
14673         return this.el && !this.hidden;
14674     },
14675
14676     /**
14677      * Displays this menu relative to another element
14678      * @param {String/HTMLElement/Roo.Element} element The element to align to
14679      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14680      * the element (defaults to this.defaultAlign)
14681      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14682      */
14683     show : function(el, pos, parentMenu){
14684         this.parentMenu = parentMenu;
14685         if(!this.el){
14686             this.render();
14687         }
14688         this.fireEvent("beforeshow", this);
14689         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14690     },
14691
14692     /**
14693      * Displays this menu at a specific xy position
14694      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14695      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14696      */
14697     showAt : function(xy, parentMenu, /* private: */_e){
14698         this.parentMenu = parentMenu;
14699         if(!this.el){
14700             this.render();
14701         }
14702         if(_e !== false){
14703             this.fireEvent("beforeshow", this);
14704             xy = this.el.adjustForConstraints(xy);
14705         }
14706         this.el.setXY(xy);
14707         this.el.show();
14708         this.hidden = false;
14709         this.focus();
14710         this.fireEvent("show", this);
14711     },
14712
14713     focus : function(){
14714         if(!this.hidden){
14715             this.doFocus.defer(50, this);
14716         }
14717     },
14718
14719     doFocus : function(){
14720         if(!this.hidden){
14721             this.focusEl.focus();
14722         }
14723     },
14724
14725     /**
14726      * Hides this menu and optionally all parent menus
14727      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14728      */
14729     hide : function(deep){
14730         if(this.el && this.isVisible()){
14731             this.fireEvent("beforehide", this);
14732             if(this.activeItem){
14733                 this.activeItem.deactivate();
14734                 this.activeItem = null;
14735             }
14736             this.el.hide();
14737             this.hidden = true;
14738             this.fireEvent("hide", this);
14739         }
14740         if(deep === true && this.parentMenu){
14741             this.parentMenu.hide(true);
14742         }
14743     },
14744
14745     /**
14746      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14747      * Any of the following are valid:
14748      * <ul>
14749      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14750      * <li>An HTMLElement object which will be converted to a menu item</li>
14751      * <li>A menu item config object that will be created as a new menu item</li>
14752      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14753      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14754      * </ul>
14755      * Usage:
14756      * <pre><code>
14757 // Create the menu
14758 var menu = new Roo.menu.Menu();
14759
14760 // Create a menu item to add by reference
14761 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14762
14763 // Add a bunch of items at once using different methods.
14764 // Only the last item added will be returned.
14765 var item = menu.add(
14766     menuItem,                // add existing item by ref
14767     'Dynamic Item',          // new TextItem
14768     '-',                     // new separator
14769     { text: 'Config Item' }  // new item by config
14770 );
14771 </code></pre>
14772      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14773      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14774      */
14775     add : function(){
14776         var a = arguments, l = a.length, item;
14777         for(var i = 0; i < l; i++){
14778             var el = a[i];
14779             if ((typeof(el) == "object") && el.xtype && el.xns) {
14780                 el = Roo.factory(el, Roo.menu);
14781             }
14782             
14783             if(el.render){ // some kind of Item
14784                 item = this.addItem(el);
14785             }else if(typeof el == "string"){ // string
14786                 if(el == "separator" || el == "-"){
14787                     item = this.addSeparator();
14788                 }else{
14789                     item = this.addText(el);
14790                 }
14791             }else if(el.tagName || el.el){ // element
14792                 item = this.addElement(el);
14793             }else if(typeof el == "object"){ // must be menu item config?
14794                 item = this.addMenuItem(el);
14795             }
14796         }
14797         return item;
14798     },
14799
14800     /**
14801      * Returns this menu's underlying {@link Roo.Element} object
14802      * @return {Roo.Element} The element
14803      */
14804     getEl : function(){
14805         if(!this.el){
14806             this.render();
14807         }
14808         return this.el;
14809     },
14810
14811     /**
14812      * Adds a separator bar to the menu
14813      * @return {Roo.menu.Item} The menu item that was added
14814      */
14815     addSeparator : function(){
14816         return this.addItem(new Roo.menu.Separator());
14817     },
14818
14819     /**
14820      * Adds an {@link Roo.Element} object to the menu
14821      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14822      * @return {Roo.menu.Item} The menu item that was added
14823      */
14824     addElement : function(el){
14825         return this.addItem(new Roo.menu.BaseItem(el));
14826     },
14827
14828     /**
14829      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14830      * @param {Roo.menu.Item} item The menu item to add
14831      * @return {Roo.menu.Item} The menu item that was added
14832      */
14833     addItem : function(item){
14834         this.items.add(item);
14835         if(this.ul){
14836             var li = document.createElement("li");
14837             li.className = "x-menu-list-item";
14838             this.ul.dom.appendChild(li);
14839             item.render(li, this);
14840             this.delayAutoWidth();
14841         }
14842         return item;
14843     },
14844
14845     /**
14846      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14847      * @param {Object} config A MenuItem config object
14848      * @return {Roo.menu.Item} The menu item that was added
14849      */
14850     addMenuItem : function(config){
14851         if(!(config instanceof Roo.menu.Item)){
14852             if(typeof config.checked == "boolean"){ // must be check menu item config?
14853                 config = new Roo.menu.CheckItem(config);
14854             }else{
14855                 config = new Roo.menu.Item(config);
14856             }
14857         }
14858         return this.addItem(config);
14859     },
14860
14861     /**
14862      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14863      * @param {String} text The text to display in the menu item
14864      * @return {Roo.menu.Item} The menu item that was added
14865      */
14866     addText : function(text){
14867         return this.addItem(new Roo.menu.TextItem({ text : text }));
14868     },
14869
14870     /**
14871      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14872      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14873      * @param {Roo.menu.Item} item The menu item to add
14874      * @return {Roo.menu.Item} The menu item that was added
14875      */
14876     insert : function(index, item){
14877         this.items.insert(index, item);
14878         if(this.ul){
14879             var li = document.createElement("li");
14880             li.className = "x-menu-list-item";
14881             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14882             item.render(li, this);
14883             this.delayAutoWidth();
14884         }
14885         return item;
14886     },
14887
14888     /**
14889      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14890      * @param {Roo.menu.Item} item The menu item to remove
14891      */
14892     remove : function(item){
14893         this.items.removeKey(item.id);
14894         item.destroy();
14895     },
14896
14897     /**
14898      * Removes and destroys all items in the menu
14899      */
14900     removeAll : function(){
14901         var f;
14902         while(f = this.items.first()){
14903             this.remove(f);
14904         }
14905     }
14906 });
14907
14908 // MenuNav is a private utility class used internally by the Menu
14909 Roo.menu.MenuNav = function(menu){
14910     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14911     this.scope = this.menu = menu;
14912 };
14913
14914 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14915     doRelay : function(e, h){
14916         var k = e.getKey();
14917         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14918             this.menu.tryActivate(0, 1);
14919             return false;
14920         }
14921         return h.call(this.scope || this, e, this.menu);
14922     },
14923
14924     up : function(e, m){
14925         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14926             m.tryActivate(m.items.length-1, -1);
14927         }
14928     },
14929
14930     down : function(e, m){
14931         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14932             m.tryActivate(0, 1);
14933         }
14934     },
14935
14936     right : function(e, m){
14937         if(m.activeItem){
14938             m.activeItem.expandMenu(true);
14939         }
14940     },
14941
14942     left : function(e, m){
14943         m.hide();
14944         if(m.parentMenu && m.parentMenu.activeItem){
14945             m.parentMenu.activeItem.activate();
14946         }
14947     },
14948
14949     enter : function(e, m){
14950         if(m.activeItem){
14951             e.stopPropagation();
14952             m.activeItem.onClick(e);
14953             m.fireEvent("click", this, m.activeItem);
14954             return true;
14955         }
14956     }
14957 });/*
14958  * Based on:
14959  * Ext JS Library 1.1.1
14960  * Copyright(c) 2006-2007, Ext JS, LLC.
14961  *
14962  * Originally Released Under LGPL - original licence link has changed is not relivant.
14963  *
14964  * Fork - LGPL
14965  * <script type="text/javascript">
14966  */
14967  
14968 /**
14969  * @class Roo.menu.MenuMgr
14970  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14971  * @singleton
14972  */
14973 Roo.menu.MenuMgr = function(){
14974    var menus, active, groups = {}, attached = false, lastShow = new Date();
14975
14976    // private - called when first menu is created
14977    function init(){
14978        menus = {};
14979        active = new Roo.util.MixedCollection();
14980        Roo.get(document).addKeyListener(27, function(){
14981            if(active.length > 0){
14982                hideAll();
14983            }
14984        });
14985    }
14986
14987    // private
14988    function hideAll(){
14989        if(active && active.length > 0){
14990            var c = active.clone();
14991            c.each(function(m){
14992                m.hide();
14993            });
14994        }
14995    }
14996
14997    // private
14998    function onHide(m){
14999        active.remove(m);
15000        if(active.length < 1){
15001            Roo.get(document).un("mousedown", onMouseDown);
15002            attached = false;
15003        }
15004    }
15005
15006    // private
15007    function onShow(m){
15008        var last = active.last();
15009        lastShow = new Date();
15010        active.add(m);
15011        if(!attached){
15012            Roo.get(document).on("mousedown", onMouseDown);
15013            attached = true;
15014        }
15015        if(m.parentMenu){
15016           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15017           m.parentMenu.activeChild = m;
15018        }else if(last && last.isVisible()){
15019           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15020        }
15021    }
15022
15023    // private
15024    function onBeforeHide(m){
15025        if(m.activeChild){
15026            m.activeChild.hide();
15027        }
15028        if(m.autoHideTimer){
15029            clearTimeout(m.autoHideTimer);
15030            delete m.autoHideTimer;
15031        }
15032    }
15033
15034    // private
15035    function onBeforeShow(m){
15036        var pm = m.parentMenu;
15037        if(!pm && !m.allowOtherMenus){
15038            hideAll();
15039        }else if(pm && pm.activeChild && active != m){
15040            pm.activeChild.hide();
15041        }
15042    }
15043
15044    // private
15045    function onMouseDown(e){
15046        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15047            hideAll();
15048        }
15049    }
15050
15051    // private
15052    function onBeforeCheck(mi, state){
15053        if(state){
15054            var g = groups[mi.group];
15055            for(var i = 0, l = g.length; i < l; i++){
15056                if(g[i] != mi){
15057                    g[i].setChecked(false);
15058                }
15059            }
15060        }
15061    }
15062
15063    return {
15064
15065        /**
15066         * Hides all menus that are currently visible
15067         */
15068        hideAll : function(){
15069             hideAll();  
15070        },
15071
15072        // private
15073        register : function(menu){
15074            if(!menus){
15075                init();
15076            }
15077            menus[menu.id] = menu;
15078            menu.on("beforehide", onBeforeHide);
15079            menu.on("hide", onHide);
15080            menu.on("beforeshow", onBeforeShow);
15081            menu.on("show", onShow);
15082            var g = menu.group;
15083            if(g && menu.events["checkchange"]){
15084                if(!groups[g]){
15085                    groups[g] = [];
15086                }
15087                groups[g].push(menu);
15088                menu.on("checkchange", onCheck);
15089            }
15090        },
15091
15092         /**
15093          * Returns a {@link Roo.menu.Menu} object
15094          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15095          * be used to generate and return a new Menu instance.
15096          */
15097        get : function(menu){
15098            if(typeof menu == "string"){ // menu id
15099                return menus[menu];
15100            }else if(menu.events){  // menu instance
15101                return menu;
15102            }else if(typeof menu.length == 'number'){ // array of menu items?
15103                return new Roo.menu.Menu({items:menu});
15104            }else{ // otherwise, must be a config
15105                return new Roo.menu.Menu(menu);
15106            }
15107        },
15108
15109        // private
15110        unregister : function(menu){
15111            delete menus[menu.id];
15112            menu.un("beforehide", onBeforeHide);
15113            menu.un("hide", onHide);
15114            menu.un("beforeshow", onBeforeShow);
15115            menu.un("show", onShow);
15116            var g = menu.group;
15117            if(g && menu.events["checkchange"]){
15118                groups[g].remove(menu);
15119                menu.un("checkchange", onCheck);
15120            }
15121        },
15122
15123        // private
15124        registerCheckable : function(menuItem){
15125            var g = menuItem.group;
15126            if(g){
15127                if(!groups[g]){
15128                    groups[g] = [];
15129                }
15130                groups[g].push(menuItem);
15131                menuItem.on("beforecheckchange", onBeforeCheck);
15132            }
15133        },
15134
15135        // private
15136        unregisterCheckable : function(menuItem){
15137            var g = menuItem.group;
15138            if(g){
15139                groups[g].remove(menuItem);
15140                menuItem.un("beforecheckchange", onBeforeCheck);
15141            }
15142        }
15143    };
15144 }();/*
15145  * Based on:
15146  * Ext JS Library 1.1.1
15147  * Copyright(c) 2006-2007, Ext JS, LLC.
15148  *
15149  * Originally Released Under LGPL - original licence link has changed is not relivant.
15150  *
15151  * Fork - LGPL
15152  * <script type="text/javascript">
15153  */
15154  
15155
15156 /**
15157  * @class Roo.menu.BaseItem
15158  * @extends Roo.Component
15159  * @abstract
15160  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15161  * management and base configuration options shared by all menu components.
15162  * @constructor
15163  * Creates a new BaseItem
15164  * @param {Object} config Configuration options
15165  */
15166 Roo.menu.BaseItem = function(config){
15167     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15168
15169     this.addEvents({
15170         /**
15171          * @event click
15172          * Fires when this item is clicked
15173          * @param {Roo.menu.BaseItem} this
15174          * @param {Roo.EventObject} e
15175          */
15176         click: true,
15177         /**
15178          * @event activate
15179          * Fires when this item is activated
15180          * @param {Roo.menu.BaseItem} this
15181          */
15182         activate : true,
15183         /**
15184          * @event deactivate
15185          * Fires when this item is deactivated
15186          * @param {Roo.menu.BaseItem} this
15187          */
15188         deactivate : true
15189     });
15190
15191     if(this.handler){
15192         this.on("click", this.handler, this.scope, true);
15193     }
15194 };
15195
15196 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15197     /**
15198      * @cfg {Function} handler
15199      * A function that will handle the click event of this menu item (defaults to undefined)
15200      */
15201     /**
15202      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15203      */
15204     canActivate : false,
15205     
15206      /**
15207      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15208      */
15209     hidden: false,
15210     
15211     /**
15212      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15213      */
15214     activeClass : "x-menu-item-active",
15215     /**
15216      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15217      */
15218     hideOnClick : true,
15219     /**
15220      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15221      */
15222     hideDelay : 100,
15223
15224     // private
15225     ctype: "Roo.menu.BaseItem",
15226
15227     // private
15228     actionMode : "container",
15229
15230     // private
15231     render : function(container, parentMenu){
15232         this.parentMenu = parentMenu;
15233         Roo.menu.BaseItem.superclass.render.call(this, container);
15234         this.container.menuItemId = this.id;
15235     },
15236
15237     // private
15238     onRender : function(container, position){
15239         this.el = Roo.get(this.el);
15240         container.dom.appendChild(this.el.dom);
15241     },
15242
15243     // private
15244     onClick : function(e){
15245         if(!this.disabled && this.fireEvent("click", this, e) !== false
15246                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15247             this.handleClick(e);
15248         }else{
15249             e.stopEvent();
15250         }
15251     },
15252
15253     // private
15254     activate : function(){
15255         if(this.disabled){
15256             return false;
15257         }
15258         var li = this.container;
15259         li.addClass(this.activeClass);
15260         this.region = li.getRegion().adjust(2, 2, -2, -2);
15261         this.fireEvent("activate", this);
15262         return true;
15263     },
15264
15265     // private
15266     deactivate : function(){
15267         this.container.removeClass(this.activeClass);
15268         this.fireEvent("deactivate", this);
15269     },
15270
15271     // private
15272     shouldDeactivate : function(e){
15273         return !this.region || !this.region.contains(e.getPoint());
15274     },
15275
15276     // private
15277     handleClick : function(e){
15278         if(this.hideOnClick){
15279             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15280         }
15281     },
15282
15283     // private
15284     expandMenu : function(autoActivate){
15285         // do nothing
15286     },
15287
15288     // private
15289     hideMenu : function(){
15290         // do nothing
15291     }
15292 });/*
15293  * Based on:
15294  * Ext JS Library 1.1.1
15295  * Copyright(c) 2006-2007, Ext JS, LLC.
15296  *
15297  * Originally Released Under LGPL - original licence link has changed is not relivant.
15298  *
15299  * Fork - LGPL
15300  * <script type="text/javascript">
15301  */
15302  
15303 /**
15304  * @class Roo.menu.Adapter
15305  * @extends Roo.menu.BaseItem
15306  * @abstract
15307  * 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.
15308  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15309  * @constructor
15310  * Creates a new Adapter
15311  * @param {Object} config Configuration options
15312  */
15313 Roo.menu.Adapter = function(component, config){
15314     Roo.menu.Adapter.superclass.constructor.call(this, config);
15315     this.component = component;
15316 };
15317 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15318     // private
15319     canActivate : true,
15320
15321     // private
15322     onRender : function(container, position){
15323         this.component.render(container);
15324         this.el = this.component.getEl();
15325     },
15326
15327     // private
15328     activate : function(){
15329         if(this.disabled){
15330             return false;
15331         }
15332         this.component.focus();
15333         this.fireEvent("activate", this);
15334         return true;
15335     },
15336
15337     // private
15338     deactivate : function(){
15339         this.fireEvent("deactivate", this);
15340     },
15341
15342     // private
15343     disable : function(){
15344         this.component.disable();
15345         Roo.menu.Adapter.superclass.disable.call(this);
15346     },
15347
15348     // private
15349     enable : function(){
15350         this.component.enable();
15351         Roo.menu.Adapter.superclass.enable.call(this);
15352     }
15353 });/*
15354  * Based on:
15355  * Ext JS Library 1.1.1
15356  * Copyright(c) 2006-2007, Ext JS, LLC.
15357  *
15358  * Originally Released Under LGPL - original licence link has changed is not relivant.
15359  *
15360  * Fork - LGPL
15361  * <script type="text/javascript">
15362  */
15363
15364 /**
15365  * @class Roo.menu.TextItem
15366  * @extends Roo.menu.BaseItem
15367  * Adds a static text string to a menu, usually used as either a heading or group separator.
15368  * Note: old style constructor with text is still supported.
15369  * 
15370  * @constructor
15371  * Creates a new TextItem
15372  * @param {Object} cfg Configuration
15373  */
15374 Roo.menu.TextItem = function(cfg){
15375     if (typeof(cfg) == 'string') {
15376         this.text = cfg;
15377     } else {
15378         Roo.apply(this,cfg);
15379     }
15380     
15381     Roo.menu.TextItem.superclass.constructor.call(this);
15382 };
15383
15384 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15385     /**
15386      * @cfg {String} text Text to show on item.
15387      */
15388     text : '',
15389     
15390     /**
15391      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15392      */
15393     hideOnClick : false,
15394     /**
15395      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15396      */
15397     itemCls : "x-menu-text",
15398
15399     // private
15400     onRender : function(){
15401         var s = document.createElement("span");
15402         s.className = this.itemCls;
15403         s.innerHTML = this.text;
15404         this.el = s;
15405         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15406     }
15407 });/*
15408  * Based on:
15409  * Ext JS Library 1.1.1
15410  * Copyright(c) 2006-2007, Ext JS, LLC.
15411  *
15412  * Originally Released Under LGPL - original licence link has changed is not relivant.
15413  *
15414  * Fork - LGPL
15415  * <script type="text/javascript">
15416  */
15417
15418 /**
15419  * @class Roo.menu.Separator
15420  * @extends Roo.menu.BaseItem
15421  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15422  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15423  * @constructor
15424  * @param {Object} config Configuration options
15425  */
15426 Roo.menu.Separator = function(config){
15427     Roo.menu.Separator.superclass.constructor.call(this, config);
15428 };
15429
15430 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15431     /**
15432      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15433      */
15434     itemCls : "x-menu-sep",
15435     /**
15436      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15437      */
15438     hideOnClick : false,
15439
15440     // private
15441     onRender : function(li){
15442         var s = document.createElement("span");
15443         s.className = this.itemCls;
15444         s.innerHTML = "&#160;";
15445         this.el = s;
15446         li.addClass("x-menu-sep-li");
15447         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15448     }
15449 });/*
15450  * Based on:
15451  * Ext JS Library 1.1.1
15452  * Copyright(c) 2006-2007, Ext JS, LLC.
15453  *
15454  * Originally Released Under LGPL - original licence link has changed is not relivant.
15455  *
15456  * Fork - LGPL
15457  * <script type="text/javascript">
15458  */
15459 /**
15460  * @class Roo.menu.Item
15461  * @extends Roo.menu.BaseItem
15462  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15463  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15464  * activation and click handling.
15465  * @constructor
15466  * Creates a new Item
15467  * @param {Object} config Configuration options
15468  */
15469 Roo.menu.Item = function(config){
15470     Roo.menu.Item.superclass.constructor.call(this, config);
15471     if(this.menu){
15472         this.menu = Roo.menu.MenuMgr.get(this.menu);
15473     }
15474 };
15475 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15476     /**
15477      * @cfg {Roo.menu.Menu} menu
15478      * A Sub menu
15479      */
15480     /**
15481      * @cfg {String} text
15482      * The text to show on the menu item.
15483      */
15484     text: '',
15485      /**
15486      * @cfg {String} HTML to render in menu
15487      * The text to show on the menu item (HTML version).
15488      */
15489     html: '',
15490     /**
15491      * @cfg {String} icon
15492      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15493      */
15494     icon: undefined,
15495     /**
15496      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15497      */
15498     itemCls : "x-menu-item",
15499     /**
15500      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15501      */
15502     canActivate : true,
15503     /**
15504      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15505      */
15506     showDelay: 200,
15507     // doc'd in BaseItem
15508     hideDelay: 200,
15509
15510     // private
15511     ctype: "Roo.menu.Item",
15512     
15513     // private
15514     onRender : function(container, position){
15515         var el = document.createElement("a");
15516         el.hideFocus = true;
15517         el.unselectable = "on";
15518         el.href = this.href || "#";
15519         if(this.hrefTarget){
15520             el.target = this.hrefTarget;
15521         }
15522         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15523         
15524         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15525         
15526         el.innerHTML = String.format(
15527                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15528                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15529         this.el = el;
15530         Roo.menu.Item.superclass.onRender.call(this, container, position);
15531     },
15532
15533     /**
15534      * Sets the text to display in this menu item
15535      * @param {String} text The text to display
15536      * @param {Boolean} isHTML true to indicate text is pure html.
15537      */
15538     setText : function(text, isHTML){
15539         if (isHTML) {
15540             this.html = text;
15541         } else {
15542             this.text = text;
15543             this.html = '';
15544         }
15545         if(this.rendered){
15546             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15547      
15548             this.el.update(String.format(
15549                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15550                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15551             this.parentMenu.autoWidth();
15552         }
15553     },
15554
15555     // private
15556     handleClick : function(e){
15557         if(!this.href){ // if no link defined, stop the event automatically
15558             e.stopEvent();
15559         }
15560         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15561     },
15562
15563     // private
15564     activate : function(autoExpand){
15565         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15566             this.focus();
15567             if(autoExpand){
15568                 this.expandMenu();
15569             }
15570         }
15571         return true;
15572     },
15573
15574     // private
15575     shouldDeactivate : function(e){
15576         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15577             if(this.menu && this.menu.isVisible()){
15578                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15579             }
15580             return true;
15581         }
15582         return false;
15583     },
15584
15585     // private
15586     deactivate : function(){
15587         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15588         this.hideMenu();
15589     },
15590
15591     // private
15592     expandMenu : function(autoActivate){
15593         if(!this.disabled && this.menu){
15594             clearTimeout(this.hideTimer);
15595             delete this.hideTimer;
15596             if(!this.menu.isVisible() && !this.showTimer){
15597                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15598             }else if (this.menu.isVisible() && autoActivate){
15599                 this.menu.tryActivate(0, 1);
15600             }
15601         }
15602     },
15603
15604     // private
15605     deferExpand : function(autoActivate){
15606         delete this.showTimer;
15607         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15608         if(autoActivate){
15609             this.menu.tryActivate(0, 1);
15610         }
15611     },
15612
15613     // private
15614     hideMenu : function(){
15615         clearTimeout(this.showTimer);
15616         delete this.showTimer;
15617         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15618             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15619         }
15620     },
15621
15622     // private
15623     deferHide : function(){
15624         delete this.hideTimer;
15625         this.menu.hide();
15626     }
15627 });/*
15628  * Based on:
15629  * Ext JS Library 1.1.1
15630  * Copyright(c) 2006-2007, Ext JS, LLC.
15631  *
15632  * Originally Released Under LGPL - original licence link has changed is not relivant.
15633  *
15634  * Fork - LGPL
15635  * <script type="text/javascript">
15636  */
15637  
15638 /**
15639  * @class Roo.menu.CheckItem
15640  * @extends Roo.menu.Item
15641  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15642  * @constructor
15643  * Creates a new CheckItem
15644  * @param {Object} config Configuration options
15645  */
15646 Roo.menu.CheckItem = function(config){
15647     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15648     this.addEvents({
15649         /**
15650          * @event beforecheckchange
15651          * Fires before the checked value is set, providing an opportunity to cancel if needed
15652          * @param {Roo.menu.CheckItem} this
15653          * @param {Boolean} checked The new checked value that will be set
15654          */
15655         "beforecheckchange" : true,
15656         /**
15657          * @event checkchange
15658          * Fires after the checked value has been set
15659          * @param {Roo.menu.CheckItem} this
15660          * @param {Boolean} checked The checked value that was set
15661          */
15662         "checkchange" : true
15663     });
15664     if(this.checkHandler){
15665         this.on('checkchange', this.checkHandler, this.scope);
15666     }
15667 };
15668 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15669     /**
15670      * @cfg {String} group
15671      * All check items with the same group name will automatically be grouped into a single-select
15672      * radio button group (defaults to '')
15673      */
15674     /**
15675      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15676      */
15677     itemCls : "x-menu-item x-menu-check-item",
15678     /**
15679      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15680      */
15681     groupClass : "x-menu-group-item",
15682
15683     /**
15684      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15685      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15686      * initialized with checked = true will be rendered as checked.
15687      */
15688     checked: false,
15689
15690     // private
15691     ctype: "Roo.menu.CheckItem",
15692
15693     // private
15694     onRender : function(c){
15695         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15696         if(this.group){
15697             this.el.addClass(this.groupClass);
15698         }
15699         Roo.menu.MenuMgr.registerCheckable(this);
15700         if(this.checked){
15701             this.checked = false;
15702             this.setChecked(true, true);
15703         }
15704     },
15705
15706     // private
15707     destroy : function(){
15708         if(this.rendered){
15709             Roo.menu.MenuMgr.unregisterCheckable(this);
15710         }
15711         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15712     },
15713
15714     /**
15715      * Set the checked state of this item
15716      * @param {Boolean} checked The new checked value
15717      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15718      */
15719     setChecked : function(state, suppressEvent){
15720         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15721             if(this.container){
15722                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15723             }
15724             this.checked = state;
15725             if(suppressEvent !== true){
15726                 this.fireEvent("checkchange", this, state);
15727             }
15728         }
15729     },
15730
15731     // private
15732     handleClick : function(e){
15733        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15734            this.setChecked(!this.checked);
15735        }
15736        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15737     }
15738 });/*
15739  * Based on:
15740  * Ext JS Library 1.1.1
15741  * Copyright(c) 2006-2007, Ext JS, LLC.
15742  *
15743  * Originally Released Under LGPL - original licence link has changed is not relivant.
15744  *
15745  * Fork - LGPL
15746  * <script type="text/javascript">
15747  */
15748  
15749 /**
15750  * @class Roo.menu.DateItem
15751  * @extends Roo.menu.Adapter
15752  * A menu item that wraps the {@link Roo.DatPicker} component.
15753  * @constructor
15754  * Creates a new DateItem
15755  * @param {Object} config Configuration options
15756  */
15757 Roo.menu.DateItem = function(config){
15758     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15759     /** The Roo.DatePicker object @type Roo.DatePicker */
15760     this.picker = this.component;
15761     this.addEvents({select: true});
15762     
15763     this.picker.on("render", function(picker){
15764         picker.getEl().swallowEvent("click");
15765         picker.container.addClass("x-menu-date-item");
15766     });
15767
15768     this.picker.on("select", this.onSelect, this);
15769 };
15770
15771 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15772     // private
15773     onSelect : function(picker, date){
15774         this.fireEvent("select", this, date, picker);
15775         Roo.menu.DateItem.superclass.handleClick.call(this);
15776     }
15777 });/*
15778  * Based on:
15779  * Ext JS Library 1.1.1
15780  * Copyright(c) 2006-2007, Ext JS, LLC.
15781  *
15782  * Originally Released Under LGPL - original licence link has changed is not relivant.
15783  *
15784  * Fork - LGPL
15785  * <script type="text/javascript">
15786  */
15787  
15788 /**
15789  * @class Roo.menu.ColorItem
15790  * @extends Roo.menu.Adapter
15791  * A menu item that wraps the {@link Roo.ColorPalette} component.
15792  * @constructor
15793  * Creates a new ColorItem
15794  * @param {Object} config Configuration options
15795  */
15796 Roo.menu.ColorItem = function(config){
15797     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15798     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15799     this.palette = this.component;
15800     this.relayEvents(this.palette, ["select"]);
15801     if(this.selectHandler){
15802         this.on('select', this.selectHandler, this.scope);
15803     }
15804 };
15805 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15806  * Based on:
15807  * Ext JS Library 1.1.1
15808  * Copyright(c) 2006-2007, Ext JS, LLC.
15809  *
15810  * Originally Released Under LGPL - original licence link has changed is not relivant.
15811  *
15812  * Fork - LGPL
15813  * <script type="text/javascript">
15814  */
15815  
15816
15817 /**
15818  * @class Roo.menu.DateMenu
15819  * @extends Roo.menu.Menu
15820  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15821  * @constructor
15822  * Creates a new DateMenu
15823  * @param {Object} config Configuration options
15824  */
15825 Roo.menu.DateMenu = function(config){
15826     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15827     this.plain = true;
15828     var di = new Roo.menu.DateItem(config);
15829     this.add(di);
15830     /**
15831      * The {@link Roo.DatePicker} instance for this DateMenu
15832      * @type DatePicker
15833      */
15834     this.picker = di.picker;
15835     /**
15836      * @event select
15837      * @param {DatePicker} picker
15838      * @param {Date} date
15839      */
15840     this.relayEvents(di, ["select"]);
15841     this.on('beforeshow', function(){
15842         if(this.picker){
15843             this.picker.hideMonthPicker(false);
15844         }
15845     }, this);
15846 };
15847 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15848     cls:'x-date-menu'
15849 });/*
15850  * Based on:
15851  * Ext JS Library 1.1.1
15852  * Copyright(c) 2006-2007, Ext JS, LLC.
15853  *
15854  * Originally Released Under LGPL - original licence link has changed is not relivant.
15855  *
15856  * Fork - LGPL
15857  * <script type="text/javascript">
15858  */
15859  
15860
15861 /**
15862  * @class Roo.menu.ColorMenu
15863  * @extends Roo.menu.Menu
15864  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15865  * @constructor
15866  * Creates a new ColorMenu
15867  * @param {Object} config Configuration options
15868  */
15869 Roo.menu.ColorMenu = function(config){
15870     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15871     this.plain = true;
15872     var ci = new Roo.menu.ColorItem(config);
15873     this.add(ci);
15874     /**
15875      * The {@link Roo.ColorPalette} instance for this ColorMenu
15876      * @type ColorPalette
15877      */
15878     this.palette = ci.palette;
15879     /**
15880      * @event select
15881      * @param {ColorPalette} palette
15882      * @param {String} color
15883      */
15884     this.relayEvents(ci, ["select"]);
15885 };
15886 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15887  * Based on:
15888  * Ext JS Library 1.1.1
15889  * Copyright(c) 2006-2007, Ext JS, LLC.
15890  *
15891  * Originally Released Under LGPL - original licence link has changed is not relivant.
15892  *
15893  * Fork - LGPL
15894  * <script type="text/javascript">
15895  */
15896  
15897 /**
15898  * @class Roo.form.TextItem
15899  * @extends Roo.BoxComponent
15900  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15901  * @constructor
15902  * Creates a new TextItem
15903  * @param {Object} config Configuration options
15904  */
15905 Roo.form.TextItem = function(config){
15906     Roo.form.TextItem.superclass.constructor.call(this, config);
15907 };
15908
15909 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15910     
15911     /**
15912      * @cfg {String} tag the tag for this item (default div)
15913      */
15914     tag : 'div',
15915     /**
15916      * @cfg {String} html the content for this item
15917      */
15918     html : '',
15919     
15920     getAutoCreate : function()
15921     {
15922         var cfg = {
15923             id: this.id,
15924             tag: this.tag,
15925             html: this.html,
15926             cls: 'x-form-item'
15927         };
15928         
15929         return cfg;
15930         
15931     },
15932     
15933     onRender : function(ct, position)
15934     {
15935         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15936         
15937         if(!this.el){
15938             var cfg = this.getAutoCreate();
15939             if(!cfg.name){
15940                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15941             }
15942             if (!cfg.name.length) {
15943                 delete cfg.name;
15944             }
15945             this.el = ct.createChild(cfg, position);
15946         }
15947     },
15948     /*
15949      * setHTML
15950      * @param {String} html update the Contents of the element.
15951      */
15952     setHTML : function(html)
15953     {
15954         this.fieldEl.dom.innerHTML = html;
15955     }
15956     
15957 });/*
15958  * Based on:
15959  * Ext JS Library 1.1.1
15960  * Copyright(c) 2006-2007, Ext JS, LLC.
15961  *
15962  * Originally Released Under LGPL - original licence link has changed is not relivant.
15963  *
15964  * Fork - LGPL
15965  * <script type="text/javascript">
15966  */
15967  
15968 /**
15969  * @class Roo.form.Field
15970  * @extends Roo.BoxComponent
15971  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15972  * @constructor
15973  * Creates a new Field
15974  * @param {Object} config Configuration options
15975  */
15976 Roo.form.Field = function(config){
15977     Roo.form.Field.superclass.constructor.call(this, config);
15978 };
15979
15980 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15981     /**
15982      * @cfg {String} fieldLabel Label to use when rendering a form.
15983      */
15984        /**
15985      * @cfg {String} qtip Mouse over tip
15986      */
15987      
15988     /**
15989      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15990      */
15991     invalidClass : "x-form-invalid",
15992     /**
15993      * @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")
15994      */
15995     invalidText : "The value in this field is invalid",
15996     /**
15997      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15998      */
15999     focusClass : "x-form-focus",
16000     /**
16001      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16002       automatic validation (defaults to "keyup").
16003      */
16004     validationEvent : "keyup",
16005     /**
16006      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16007      */
16008     validateOnBlur : true,
16009     /**
16010      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16011      */
16012     validationDelay : 250,
16013     /**
16014      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16015      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16016      */
16017     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16018     /**
16019      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16020      */
16021     fieldClass : "x-form-field",
16022     /**
16023      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16024      *<pre>
16025 Value         Description
16026 -----------   ----------------------------------------------------------------------
16027 qtip          Display a quick tip when the user hovers over the field
16028 title         Display a default browser title attribute popup
16029 under         Add a block div beneath the field containing the error text
16030 side          Add an error icon to the right of the field with a popup on hover
16031 [element id]  Add the error text directly to the innerHTML of the specified element
16032 </pre>
16033      */
16034     msgTarget : 'qtip',
16035     /**
16036      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16037      */
16038     msgFx : 'normal',
16039
16040     /**
16041      * @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.
16042      */
16043     readOnly : false,
16044
16045     /**
16046      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16047      */
16048     disabled : false,
16049
16050     /**
16051      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16052      */
16053     inputType : undefined,
16054     
16055     /**
16056      * @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).
16057          */
16058         tabIndex : undefined,
16059         
16060     // private
16061     isFormField : true,
16062
16063     // private
16064     hasFocus : false,
16065     /**
16066      * @property {Roo.Element} fieldEl
16067      * Element Containing the rendered Field (with label etc.)
16068      */
16069     /**
16070      * @cfg {Mixed} value A value to initialize this field with.
16071      */
16072     value : undefined,
16073
16074     /**
16075      * @cfg {String} name The field's HTML name attribute.
16076      */
16077     /**
16078      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16079      */
16080     // private
16081     loadedValue : false,
16082      
16083      
16084         // private ??
16085         initComponent : function(){
16086         Roo.form.Field.superclass.initComponent.call(this);
16087         this.addEvents({
16088             /**
16089              * @event focus
16090              * Fires when this field receives input focus.
16091              * @param {Roo.form.Field} this
16092              */
16093             focus : true,
16094             /**
16095              * @event blur
16096              * Fires when this field loses input focus.
16097              * @param {Roo.form.Field} this
16098              */
16099             blur : true,
16100             /**
16101              * @event specialkey
16102              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16103              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16104              * @param {Roo.form.Field} this
16105              * @param {Roo.EventObject} e The event object
16106              */
16107             specialkey : true,
16108             /**
16109              * @event change
16110              * Fires just before the field blurs if the field value has changed.
16111              * @param {Roo.form.Field} this
16112              * @param {Mixed} newValue The new value
16113              * @param {Mixed} oldValue The original value
16114              */
16115             change : true,
16116             /**
16117              * @event invalid
16118              * Fires after the field has been marked as invalid.
16119              * @param {Roo.form.Field} this
16120              * @param {String} msg The validation message
16121              */
16122             invalid : true,
16123             /**
16124              * @event valid
16125              * Fires after the field has been validated with no errors.
16126              * @param {Roo.form.Field} this
16127              */
16128             valid : true,
16129              /**
16130              * @event keyup
16131              * Fires after the key up
16132              * @param {Roo.form.Field} this
16133              * @param {Roo.EventObject}  e The event Object
16134              */
16135             keyup : true
16136         });
16137     },
16138
16139     /**
16140      * Returns the name attribute of the field if available
16141      * @return {String} name The field name
16142      */
16143     getName: function(){
16144          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16145     },
16146
16147     // private
16148     onRender : function(ct, position){
16149         Roo.form.Field.superclass.onRender.call(this, ct, position);
16150         if(!this.el){
16151             var cfg = this.getAutoCreate();
16152             if(!cfg.name){
16153                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16154             }
16155             if (!cfg.name.length) {
16156                 delete cfg.name;
16157             }
16158             if(this.inputType){
16159                 cfg.type = this.inputType;
16160             }
16161             this.el = ct.createChild(cfg, position);
16162         }
16163         var type = this.el.dom.type;
16164         if(type){
16165             if(type == 'password'){
16166                 type = 'text';
16167             }
16168             this.el.addClass('x-form-'+type);
16169         }
16170         if(this.readOnly){
16171             this.el.dom.readOnly = true;
16172         }
16173         if(this.tabIndex !== undefined){
16174             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16175         }
16176
16177         this.el.addClass([this.fieldClass, this.cls]);
16178         this.initValue();
16179     },
16180
16181     /**
16182      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16183      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16184      * @return {Roo.form.Field} this
16185      */
16186     applyTo : function(target){
16187         this.allowDomMove = false;
16188         this.el = Roo.get(target);
16189         this.render(this.el.dom.parentNode);
16190         return this;
16191     },
16192
16193     // private
16194     initValue : function(){
16195         if(this.value !== undefined){
16196             this.setValue(this.value);
16197         }else if(this.el.dom.value.length > 0){
16198             this.setValue(this.el.dom.value);
16199         }
16200     },
16201
16202     /**
16203      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16204      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16205      */
16206     isDirty : function() {
16207         if(this.disabled) {
16208             return false;
16209         }
16210         return String(this.getValue()) !== String(this.originalValue);
16211     },
16212
16213     /**
16214      * stores the current value in loadedValue
16215      */
16216     resetHasChanged : function()
16217     {
16218         this.loadedValue = String(this.getValue());
16219     },
16220     /**
16221      * checks the current value against the 'loaded' value.
16222      * Note - will return false if 'resetHasChanged' has not been called first.
16223      */
16224     hasChanged : function()
16225     {
16226         if(this.disabled || this.readOnly) {
16227             return false;
16228         }
16229         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16230     },
16231     
16232     
16233     
16234     // private
16235     afterRender : function(){
16236         Roo.form.Field.superclass.afterRender.call(this);
16237         this.initEvents();
16238     },
16239
16240     // private
16241     fireKey : function(e){
16242         //Roo.log('field ' + e.getKey());
16243         if(e.isNavKeyPress()){
16244             this.fireEvent("specialkey", this, e);
16245         }
16246     },
16247
16248     /**
16249      * Resets the current field value to the originally loaded value and clears any validation messages
16250      */
16251     reset : function(){
16252         this.setValue(this.resetValue);
16253         this.originalValue = this.getValue();
16254         this.clearInvalid();
16255     },
16256
16257     // private
16258     initEvents : function(){
16259         // safari killled keypress - so keydown is now used..
16260         this.el.on("keydown" , this.fireKey,  this);
16261         this.el.on("focus", this.onFocus,  this);
16262         this.el.on("blur", this.onBlur,  this);
16263         this.el.relayEvent('keyup', this);
16264
16265         // reference to original value for reset
16266         this.originalValue = this.getValue();
16267         this.resetValue =  this.getValue();
16268     },
16269
16270     // private
16271     onFocus : function(){
16272         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16273             this.el.addClass(this.focusClass);
16274         }
16275         if(!this.hasFocus){
16276             this.hasFocus = true;
16277             this.startValue = this.getValue();
16278             this.fireEvent("focus", this);
16279         }
16280     },
16281
16282     beforeBlur : Roo.emptyFn,
16283
16284     // private
16285     onBlur : function(){
16286         this.beforeBlur();
16287         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16288             this.el.removeClass(this.focusClass);
16289         }
16290         this.hasFocus = false;
16291         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16292             this.validate();
16293         }
16294         var v = this.getValue();
16295         if(String(v) !== String(this.startValue)){
16296             this.fireEvent('change', this, v, this.startValue);
16297         }
16298         this.fireEvent("blur", this);
16299     },
16300
16301     /**
16302      * Returns whether or not the field value is currently valid
16303      * @param {Boolean} preventMark True to disable marking the field invalid
16304      * @return {Boolean} True if the value is valid, else false
16305      */
16306     isValid : function(preventMark){
16307         if(this.disabled){
16308             return true;
16309         }
16310         var restore = this.preventMark;
16311         this.preventMark = preventMark === true;
16312         var v = this.validateValue(this.processValue(this.getRawValue()));
16313         this.preventMark = restore;
16314         return v;
16315     },
16316
16317     /**
16318      * Validates the field value
16319      * @return {Boolean} True if the value is valid, else false
16320      */
16321     validate : function(){
16322         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16323             this.clearInvalid();
16324             return true;
16325         }
16326         return false;
16327     },
16328
16329     processValue : function(value){
16330         return value;
16331     },
16332
16333     // private
16334     // Subclasses should provide the validation implementation by overriding this
16335     validateValue : function(value){
16336         return true;
16337     },
16338
16339     /**
16340      * Mark this field as invalid
16341      * @param {String} msg The validation message
16342      */
16343     markInvalid : function(msg){
16344         if(!this.rendered || this.preventMark){ // not rendered
16345             return;
16346         }
16347         
16348         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16349         
16350         obj.el.addClass(this.invalidClass);
16351         msg = msg || this.invalidText;
16352         switch(this.msgTarget){
16353             case 'qtip':
16354                 obj.el.dom.qtip = msg;
16355                 obj.el.dom.qclass = 'x-form-invalid-tip';
16356                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16357                     Roo.QuickTips.enable();
16358                 }
16359                 break;
16360             case 'title':
16361                 this.el.dom.title = msg;
16362                 break;
16363             case 'under':
16364                 if(!this.errorEl){
16365                     var elp = this.el.findParent('.x-form-element', 5, true);
16366                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16367                     this.errorEl.setWidth(elp.getWidth(true)-20);
16368                 }
16369                 this.errorEl.update(msg);
16370                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16371                 break;
16372             case 'side':
16373                 if(!this.errorIcon){
16374                     var elp = this.el.findParent('.x-form-element', 5, true);
16375                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16376                 }
16377                 this.alignErrorIcon();
16378                 this.errorIcon.dom.qtip = msg;
16379                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16380                 this.errorIcon.show();
16381                 this.on('resize', this.alignErrorIcon, this);
16382                 break;
16383             default:
16384                 var t = Roo.getDom(this.msgTarget);
16385                 t.innerHTML = msg;
16386                 t.style.display = this.msgDisplay;
16387                 break;
16388         }
16389         this.fireEvent('invalid', this, msg);
16390     },
16391
16392     // private
16393     alignErrorIcon : function(){
16394         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16395     },
16396
16397     /**
16398      * Clear any invalid styles/messages for this field
16399      */
16400     clearInvalid : function(){
16401         if(!this.rendered || this.preventMark){ // not rendered
16402             return;
16403         }
16404         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16405         
16406         obj.el.removeClass(this.invalidClass);
16407         switch(this.msgTarget){
16408             case 'qtip':
16409                 obj.el.dom.qtip = '';
16410                 break;
16411             case 'title':
16412                 this.el.dom.title = '';
16413                 break;
16414             case 'under':
16415                 if(this.errorEl){
16416                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16417                 }
16418                 break;
16419             case 'side':
16420                 if(this.errorIcon){
16421                     this.errorIcon.dom.qtip = '';
16422                     this.errorIcon.hide();
16423                     this.un('resize', this.alignErrorIcon, this);
16424                 }
16425                 break;
16426             default:
16427                 var t = Roo.getDom(this.msgTarget);
16428                 t.innerHTML = '';
16429                 t.style.display = 'none';
16430                 break;
16431         }
16432         this.fireEvent('valid', this);
16433     },
16434
16435     /**
16436      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16437      * @return {Mixed} value The field value
16438      */
16439     getRawValue : function(){
16440         var v = this.el.getValue();
16441         
16442         return v;
16443     },
16444
16445     /**
16446      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16447      * @return {Mixed} value The field value
16448      */
16449     getValue : function(){
16450         var v = this.el.getValue();
16451          
16452         return v;
16453     },
16454
16455     /**
16456      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16457      * @param {Mixed} value The value to set
16458      */
16459     setRawValue : function(v){
16460         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16461     },
16462
16463     /**
16464      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16465      * @param {Mixed} value The value to set
16466      */
16467     setValue : function(v){
16468         this.value = v;
16469         if(this.rendered){
16470             this.el.dom.value = (v === null || v === undefined ? '' : v);
16471              this.validate();
16472         }
16473     },
16474
16475     adjustSize : function(w, h){
16476         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16477         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16478         return s;
16479     },
16480
16481     adjustWidth : function(tag, w){
16482         tag = tag.toLowerCase();
16483         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16484             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16485                 if(tag == 'input'){
16486                     return w + 2;
16487                 }
16488                 if(tag == 'textarea'){
16489                     return w-2;
16490                 }
16491             }else if(Roo.isOpera){
16492                 if(tag == 'input'){
16493                     return w + 2;
16494                 }
16495                 if(tag == 'textarea'){
16496                     return w-2;
16497                 }
16498             }
16499         }
16500         return w;
16501     }
16502 });
16503
16504
16505 // anything other than normal should be considered experimental
16506 Roo.form.Field.msgFx = {
16507     normal : {
16508         show: function(msgEl, f){
16509             msgEl.setDisplayed('block');
16510         },
16511
16512         hide : function(msgEl, f){
16513             msgEl.setDisplayed(false).update('');
16514         }
16515     },
16516
16517     slide : {
16518         show: function(msgEl, f){
16519             msgEl.slideIn('t', {stopFx:true});
16520         },
16521
16522         hide : function(msgEl, f){
16523             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16524         }
16525     },
16526
16527     slideRight : {
16528         show: function(msgEl, f){
16529             msgEl.fixDisplay();
16530             msgEl.alignTo(f.el, 'tl-tr');
16531             msgEl.slideIn('l', {stopFx:true});
16532         },
16533
16534         hide : function(msgEl, f){
16535             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16536         }
16537     }
16538 };/*
16539  * Based on:
16540  * Ext JS Library 1.1.1
16541  * Copyright(c) 2006-2007, Ext JS, LLC.
16542  *
16543  * Originally Released Under LGPL - original licence link has changed is not relivant.
16544  *
16545  * Fork - LGPL
16546  * <script type="text/javascript">
16547  */
16548  
16549
16550 /**
16551  * @class Roo.form.TextField
16552  * @extends Roo.form.Field
16553  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16554  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16555  * @constructor
16556  * Creates a new TextField
16557  * @param {Object} config Configuration options
16558  */
16559 Roo.form.TextField = function(config){
16560     Roo.form.TextField.superclass.constructor.call(this, config);
16561     this.addEvents({
16562         /**
16563          * @event autosize
16564          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16565          * according to the default logic, but this event provides a hook for the developer to apply additional
16566          * logic at runtime to resize the field if needed.
16567              * @param {Roo.form.Field} this This text field
16568              * @param {Number} width The new field width
16569              */
16570         autosize : true
16571     });
16572 };
16573
16574 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16575     /**
16576      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16577      */
16578     grow : false,
16579     /**
16580      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16581      */
16582     growMin : 30,
16583     /**
16584      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16585      */
16586     growMax : 800,
16587     /**
16588      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16589      */
16590     vtype : null,
16591     /**
16592      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16593      */
16594     maskRe : null,
16595     /**
16596      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16597      */
16598     disableKeyFilter : false,
16599     /**
16600      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16601      */
16602     allowBlank : true,
16603     /**
16604      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16605      */
16606     minLength : 0,
16607     /**
16608      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16609      */
16610     maxLength : Number.MAX_VALUE,
16611     /**
16612      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16613      */
16614     minLengthText : "The minimum length for this field is {0}",
16615     /**
16616      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16617      */
16618     maxLengthText : "The maximum length for this field is {0}",
16619     /**
16620      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16621      */
16622     selectOnFocus : false,
16623     /**
16624      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16625      */    
16626     allowLeadingSpace : false,
16627     /**
16628      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16629      */
16630     blankText : "This field is required",
16631     /**
16632      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16633      * If available, this function will be called only after the basic validators all return true, and will be passed the
16634      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16635      */
16636     validator : null,
16637     /**
16638      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16639      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16640      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16641      */
16642     regex : null,
16643     /**
16644      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16645      */
16646     regexText : "",
16647     /**
16648      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16649      */
16650     emptyText : null,
16651    
16652
16653     // private
16654     initEvents : function()
16655     {
16656         if (this.emptyText) {
16657             this.el.attr('placeholder', this.emptyText);
16658         }
16659         
16660         Roo.form.TextField.superclass.initEvents.call(this);
16661         if(this.validationEvent == 'keyup'){
16662             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16663             this.el.on('keyup', this.filterValidation, this);
16664         }
16665         else if(this.validationEvent !== false){
16666             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16667         }
16668         
16669         if(this.selectOnFocus){
16670             this.on("focus", this.preFocus, this);
16671         }
16672         if (!this.allowLeadingSpace) {
16673             this.on('blur', this.cleanLeadingSpace, this);
16674         }
16675         
16676         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16677             this.el.on("keypress", this.filterKeys, this);
16678         }
16679         if(this.grow){
16680             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16681             this.el.on("click", this.autoSize,  this);
16682         }
16683         if(this.el.is('input[type=password]') && Roo.isSafari){
16684             this.el.on('keydown', this.SafariOnKeyDown, this);
16685         }
16686     },
16687
16688     processValue : function(value){
16689         if(this.stripCharsRe){
16690             var newValue = value.replace(this.stripCharsRe, '');
16691             if(newValue !== value){
16692                 this.setRawValue(newValue);
16693                 return newValue;
16694             }
16695         }
16696         return value;
16697     },
16698
16699     filterValidation : function(e){
16700         if(!e.isNavKeyPress()){
16701             this.validationTask.delay(this.validationDelay);
16702         }
16703     },
16704
16705     // private
16706     onKeyUp : function(e){
16707         if(!e.isNavKeyPress()){
16708             this.autoSize();
16709         }
16710     },
16711     // private - clean the leading white space
16712     cleanLeadingSpace : function(e)
16713     {
16714         if ( this.inputType == 'file') {
16715             return;
16716         }
16717         
16718         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16719     },
16720     /**
16721      * Resets the current field value to the originally-loaded value and clears any validation messages.
16722      *  
16723      */
16724     reset : function(){
16725         Roo.form.TextField.superclass.reset.call(this);
16726        
16727     }, 
16728     // private
16729     preFocus : function(){
16730         
16731         if(this.selectOnFocus){
16732             this.el.dom.select();
16733         }
16734     },
16735
16736     
16737     // private
16738     filterKeys : function(e){
16739         var k = e.getKey();
16740         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16741             return;
16742         }
16743         var c = e.getCharCode(), cc = String.fromCharCode(c);
16744         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16745             return;
16746         }
16747         if(!this.maskRe.test(cc)){
16748             e.stopEvent();
16749         }
16750     },
16751
16752     setValue : function(v){
16753         
16754         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16755         
16756         this.autoSize();
16757     },
16758
16759     /**
16760      * Validates a value according to the field's validation rules and marks the field as invalid
16761      * if the validation fails
16762      * @param {Mixed} value The value to validate
16763      * @return {Boolean} True if the value is valid, else false
16764      */
16765     validateValue : function(value){
16766         if(value.length < 1)  { // if it's blank
16767              if(this.allowBlank){
16768                 this.clearInvalid();
16769                 return true;
16770              }else{
16771                 this.markInvalid(this.blankText);
16772                 return false;
16773              }
16774         }
16775         if(value.length < this.minLength){
16776             this.markInvalid(String.format(this.minLengthText, this.minLength));
16777             return false;
16778         }
16779         if(value.length > this.maxLength){
16780             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16781             return false;
16782         }
16783         if(this.vtype){
16784             var vt = Roo.form.VTypes;
16785             if(!vt[this.vtype](value, this)){
16786                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16787                 return false;
16788             }
16789         }
16790         if(typeof this.validator == "function"){
16791             var msg = this.validator(value);
16792             if(msg !== true){
16793                 this.markInvalid(msg);
16794                 return false;
16795             }
16796         }
16797         if(this.regex && !this.regex.test(value)){
16798             this.markInvalid(this.regexText);
16799             return false;
16800         }
16801         return true;
16802     },
16803
16804     /**
16805      * Selects text in this field
16806      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16807      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16808      */
16809     selectText : function(start, end){
16810         var v = this.getRawValue();
16811         if(v.length > 0){
16812             start = start === undefined ? 0 : start;
16813             end = end === undefined ? v.length : end;
16814             var d = this.el.dom;
16815             if(d.setSelectionRange){
16816                 d.setSelectionRange(start, end);
16817             }else if(d.createTextRange){
16818                 var range = d.createTextRange();
16819                 range.moveStart("character", start);
16820                 range.moveEnd("character", v.length-end);
16821                 range.select();
16822             }
16823         }
16824     },
16825
16826     /**
16827      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16828      * This only takes effect if grow = true, and fires the autosize event.
16829      */
16830     autoSize : function(){
16831         if(!this.grow || !this.rendered){
16832             return;
16833         }
16834         if(!this.metrics){
16835             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16836         }
16837         var el = this.el;
16838         var v = el.dom.value;
16839         var d = document.createElement('div');
16840         d.appendChild(document.createTextNode(v));
16841         v = d.innerHTML;
16842         d = null;
16843         v += "&#160;";
16844         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16845         this.el.setWidth(w);
16846         this.fireEvent("autosize", this, w);
16847     },
16848     
16849     // private
16850     SafariOnKeyDown : function(event)
16851     {
16852         // this is a workaround for a password hang bug on chrome/ webkit.
16853         
16854         var isSelectAll = false;
16855         
16856         if(this.el.dom.selectionEnd > 0){
16857             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16858         }
16859         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16860             event.preventDefault();
16861             this.setValue('');
16862             return;
16863         }
16864         
16865         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16866             
16867             event.preventDefault();
16868             // this is very hacky as keydown always get's upper case.
16869             
16870             var cc = String.fromCharCode(event.getCharCode());
16871             
16872             
16873             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16874             
16875         }
16876         
16877         
16878     }
16879 });/*
16880  * Based on:
16881  * Ext JS Library 1.1.1
16882  * Copyright(c) 2006-2007, Ext JS, LLC.
16883  *
16884  * Originally Released Under LGPL - original licence link has changed is not relivant.
16885  *
16886  * Fork - LGPL
16887  * <script type="text/javascript">
16888  */
16889  
16890 /**
16891  * @class Roo.form.Hidden
16892  * @extends Roo.form.TextField
16893  * Simple Hidden element used on forms 
16894  * 
16895  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16896  * 
16897  * @constructor
16898  * Creates a new Hidden form element.
16899  * @param {Object} config Configuration options
16900  */
16901
16902
16903
16904 // easy hidden field...
16905 Roo.form.Hidden = function(config){
16906     Roo.form.Hidden.superclass.constructor.call(this, config);
16907 };
16908   
16909 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16910     fieldLabel:      '',
16911     inputType:      'hidden',
16912     width:          50,
16913     allowBlank:     true,
16914     labelSeparator: '',
16915     hidden:         true,
16916     itemCls :       'x-form-item-display-none'
16917
16918
16919 });
16920
16921
16922 /*
16923  * Based on:
16924  * Ext JS Library 1.1.1
16925  * Copyright(c) 2006-2007, Ext JS, LLC.
16926  *
16927  * Originally Released Under LGPL - original licence link has changed is not relivant.
16928  *
16929  * Fork - LGPL
16930  * <script type="text/javascript">
16931  */
16932  
16933 /**
16934  * @class Roo.form.TriggerField
16935  * @extends Roo.form.TextField
16936  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16937  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16938  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16939  * for which you can provide a custom implementation.  For example:
16940  * <pre><code>
16941 var trigger = new Roo.form.TriggerField();
16942 trigger.onTriggerClick = myTriggerFn;
16943 trigger.applyTo('my-field');
16944 </code></pre>
16945  *
16946  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16947  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16948  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16949  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16950  * @constructor
16951  * Create a new TriggerField.
16952  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16953  * to the base TextField)
16954  */
16955 Roo.form.TriggerField = function(config){
16956     this.mimicing = false;
16957     Roo.form.TriggerField.superclass.constructor.call(this, config);
16958 };
16959
16960 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16961     /**
16962      * @cfg {String} triggerClass A CSS class to apply to the trigger
16963      */
16964     /**
16965      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16966      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16967      */
16968     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16969     /**
16970      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16971      */
16972     hideTrigger:false,
16973
16974     /** @cfg {Boolean} grow @hide */
16975     /** @cfg {Number} growMin @hide */
16976     /** @cfg {Number} growMax @hide */
16977
16978     /**
16979      * @hide 
16980      * @method
16981      */
16982     autoSize: Roo.emptyFn,
16983     // private
16984     monitorTab : true,
16985     // private
16986     deferHeight : true,
16987
16988     
16989     actionMode : 'wrap',
16990     // private
16991     onResize : function(w, h){
16992         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16993         if(typeof w == 'number'){
16994             var x = w - this.trigger.getWidth();
16995             this.el.setWidth(this.adjustWidth('input', x));
16996             this.trigger.setStyle('left', x+'px');
16997         }
16998     },
16999
17000     // private
17001     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17002
17003     // private
17004     getResizeEl : function(){
17005         return this.wrap;
17006     },
17007
17008     // private
17009     getPositionEl : function(){
17010         return this.wrap;
17011     },
17012
17013     // private
17014     alignErrorIcon : function(){
17015         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17016     },
17017
17018     // private
17019     onRender : function(ct, position){
17020         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17021         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17022         this.trigger = this.wrap.createChild(this.triggerConfig ||
17023                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17024         if(this.hideTrigger){
17025             this.trigger.setDisplayed(false);
17026         }
17027         this.initTrigger();
17028         if(!this.width){
17029             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17030         }
17031     },
17032
17033     // private
17034     initTrigger : function(){
17035         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17036         this.trigger.addClassOnOver('x-form-trigger-over');
17037         this.trigger.addClassOnClick('x-form-trigger-click');
17038     },
17039
17040     // private
17041     onDestroy : function(){
17042         if(this.trigger){
17043             this.trigger.removeAllListeners();
17044             this.trigger.remove();
17045         }
17046         if(this.wrap){
17047             this.wrap.remove();
17048         }
17049         Roo.form.TriggerField.superclass.onDestroy.call(this);
17050     },
17051
17052     // private
17053     onFocus : function(){
17054         Roo.form.TriggerField.superclass.onFocus.call(this);
17055         if(!this.mimicing){
17056             this.wrap.addClass('x-trigger-wrap-focus');
17057             this.mimicing = true;
17058             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17059             if(this.monitorTab){
17060                 this.el.on("keydown", this.checkTab, this);
17061             }
17062         }
17063     },
17064
17065     // private
17066     checkTab : function(e){
17067         if(e.getKey() == e.TAB){
17068             this.triggerBlur();
17069         }
17070     },
17071
17072     // private
17073     onBlur : function(){
17074         // do nothing
17075     },
17076
17077     // private
17078     mimicBlur : function(e, t){
17079         if(!this.wrap.contains(t) && this.validateBlur()){
17080             this.triggerBlur();
17081         }
17082     },
17083
17084     // private
17085     triggerBlur : function(){
17086         this.mimicing = false;
17087         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17088         if(this.monitorTab){
17089             this.el.un("keydown", this.checkTab, this);
17090         }
17091         this.wrap.removeClass('x-trigger-wrap-focus');
17092         Roo.form.TriggerField.superclass.onBlur.call(this);
17093     },
17094
17095     // private
17096     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17097     validateBlur : function(e, t){
17098         return true;
17099     },
17100
17101     // private
17102     onDisable : function(){
17103         Roo.form.TriggerField.superclass.onDisable.call(this);
17104         if(this.wrap){
17105             this.wrap.addClass('x-item-disabled');
17106         }
17107     },
17108
17109     // private
17110     onEnable : function(){
17111         Roo.form.TriggerField.superclass.onEnable.call(this);
17112         if(this.wrap){
17113             this.wrap.removeClass('x-item-disabled');
17114         }
17115     },
17116
17117     // private
17118     onShow : function(){
17119         var ae = this.getActionEl();
17120         
17121         if(ae){
17122             ae.dom.style.display = '';
17123             ae.dom.style.visibility = 'visible';
17124         }
17125     },
17126
17127     // private
17128     
17129     onHide : function(){
17130         var ae = this.getActionEl();
17131         ae.dom.style.display = 'none';
17132     },
17133
17134     /**
17135      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17136      * by an implementing function.
17137      * @method
17138      * @param {EventObject} e
17139      */
17140     onTriggerClick : Roo.emptyFn
17141 });
17142
17143 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17144 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17145 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17146 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17147     initComponent : function(){
17148         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17149
17150         this.triggerConfig = {
17151             tag:'span', cls:'x-form-twin-triggers', cn:[
17152             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17153             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17154         ]};
17155     },
17156
17157     getTrigger : function(index){
17158         return this.triggers[index];
17159     },
17160
17161     initTrigger : function(){
17162         var ts = this.trigger.select('.x-form-trigger', true);
17163         this.wrap.setStyle('overflow', 'hidden');
17164         var triggerField = this;
17165         ts.each(function(t, all, index){
17166             t.hide = function(){
17167                 var w = triggerField.wrap.getWidth();
17168                 this.dom.style.display = 'none';
17169                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17170             };
17171             t.show = function(){
17172                 var w = triggerField.wrap.getWidth();
17173                 this.dom.style.display = '';
17174                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17175             };
17176             var triggerIndex = 'Trigger'+(index+1);
17177
17178             if(this['hide'+triggerIndex]){
17179                 t.dom.style.display = 'none';
17180             }
17181             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17182             t.addClassOnOver('x-form-trigger-over');
17183             t.addClassOnClick('x-form-trigger-click');
17184         }, this);
17185         this.triggers = ts.elements;
17186     },
17187
17188     onTrigger1Click : Roo.emptyFn,
17189     onTrigger2Click : Roo.emptyFn
17190 });/*
17191  * Based on:
17192  * Ext JS Library 1.1.1
17193  * Copyright(c) 2006-2007, Ext JS, LLC.
17194  *
17195  * Originally Released Under LGPL - original licence link has changed is not relivant.
17196  *
17197  * Fork - LGPL
17198  * <script type="text/javascript">
17199  */
17200  
17201 /**
17202  * @class Roo.form.TextArea
17203  * @extends Roo.form.TextField
17204  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17205  * support for auto-sizing.
17206  * @constructor
17207  * Creates a new TextArea
17208  * @param {Object} config Configuration options
17209  */
17210 Roo.form.TextArea = function(config){
17211     Roo.form.TextArea.superclass.constructor.call(this, config);
17212     // these are provided exchanges for backwards compat
17213     // minHeight/maxHeight were replaced by growMin/growMax to be
17214     // compatible with TextField growing config values
17215     if(this.minHeight !== undefined){
17216         this.growMin = this.minHeight;
17217     }
17218     if(this.maxHeight !== undefined){
17219         this.growMax = this.maxHeight;
17220     }
17221 };
17222
17223 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17224     /**
17225      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17226      */
17227     growMin : 60,
17228     /**
17229      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17230      */
17231     growMax: 1000,
17232     /**
17233      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17234      * in the field (equivalent to setting overflow: hidden, defaults to false)
17235      */
17236     preventScrollbars: false,
17237     /**
17238      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17239      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17240      */
17241
17242     // private
17243     onRender : function(ct, position){
17244         if(!this.el){
17245             this.defaultAutoCreate = {
17246                 tag: "textarea",
17247                 style:"width:300px;height:60px;",
17248                 autocomplete: "new-password"
17249             };
17250         }
17251         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17252         if(this.grow){
17253             this.textSizeEl = Roo.DomHelper.append(document.body, {
17254                 tag: "pre", cls: "x-form-grow-sizer"
17255             });
17256             if(this.preventScrollbars){
17257                 this.el.setStyle("overflow", "hidden");
17258             }
17259             this.el.setHeight(this.growMin);
17260         }
17261     },
17262
17263     onDestroy : function(){
17264         if(this.textSizeEl){
17265             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17266         }
17267         Roo.form.TextArea.superclass.onDestroy.call(this);
17268     },
17269
17270     // private
17271     onKeyUp : function(e){
17272         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17273             this.autoSize();
17274         }
17275     },
17276
17277     /**
17278      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17279      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17280      */
17281     autoSize : function(){
17282         if(!this.grow || !this.textSizeEl){
17283             return;
17284         }
17285         var el = this.el;
17286         var v = el.dom.value;
17287         var ts = this.textSizeEl;
17288
17289         ts.innerHTML = '';
17290         ts.appendChild(document.createTextNode(v));
17291         v = ts.innerHTML;
17292
17293         Roo.fly(ts).setWidth(this.el.getWidth());
17294         if(v.length < 1){
17295             v = "&#160;&#160;";
17296         }else{
17297             if(Roo.isIE){
17298                 v = v.replace(/\n/g, '<p>&#160;</p>');
17299             }
17300             v += "&#160;\n&#160;";
17301         }
17302         ts.innerHTML = v;
17303         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17304         if(h != this.lastHeight){
17305             this.lastHeight = h;
17306             this.el.setHeight(h);
17307             this.fireEvent("autosize", this, h);
17308         }
17309     }
17310 });/*
17311  * Based on:
17312  * Ext JS Library 1.1.1
17313  * Copyright(c) 2006-2007, Ext JS, LLC.
17314  *
17315  * Originally Released Under LGPL - original licence link has changed is not relivant.
17316  *
17317  * Fork - LGPL
17318  * <script type="text/javascript">
17319  */
17320  
17321
17322 /**
17323  * @class Roo.form.NumberField
17324  * @extends Roo.form.TextField
17325  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17326  * @constructor
17327  * Creates a new NumberField
17328  * @param {Object} config Configuration options
17329  */
17330 Roo.form.NumberField = function(config){
17331     Roo.form.NumberField.superclass.constructor.call(this, config);
17332 };
17333
17334 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17335     /**
17336      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17337      */
17338     fieldClass: "x-form-field x-form-num-field",
17339     /**
17340      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17341      */
17342     allowDecimals : true,
17343     /**
17344      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17345      */
17346     decimalSeparator : ".",
17347     /**
17348      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17349      */
17350     decimalPrecision : 2,
17351     /**
17352      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17353      */
17354     allowNegative : true,
17355     /**
17356      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17357      */
17358     minValue : Number.NEGATIVE_INFINITY,
17359     /**
17360      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17361      */
17362     maxValue : Number.MAX_VALUE,
17363     /**
17364      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17365      */
17366     minText : "The minimum value for this field is {0}",
17367     /**
17368      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17369      */
17370     maxText : "The maximum value for this field is {0}",
17371     /**
17372      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17373      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17374      */
17375     nanText : "{0} is not a valid number",
17376
17377     // private
17378     initEvents : function(){
17379         Roo.form.NumberField.superclass.initEvents.call(this);
17380         var allowed = "0123456789";
17381         if(this.allowDecimals){
17382             allowed += this.decimalSeparator;
17383         }
17384         if(this.allowNegative){
17385             allowed += "-";
17386         }
17387         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17388         var keyPress = function(e){
17389             var k = e.getKey();
17390             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17391                 return;
17392             }
17393             var c = e.getCharCode();
17394             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17395                 e.stopEvent();
17396             }
17397         };
17398         this.el.on("keypress", keyPress, this);
17399     },
17400
17401     // private
17402     validateValue : function(value){
17403         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17404             return false;
17405         }
17406         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17407              return true;
17408         }
17409         var num = this.parseValue(value);
17410         if(isNaN(num)){
17411             this.markInvalid(String.format(this.nanText, value));
17412             return false;
17413         }
17414         if(num < this.minValue){
17415             this.markInvalid(String.format(this.minText, this.minValue));
17416             return false;
17417         }
17418         if(num > this.maxValue){
17419             this.markInvalid(String.format(this.maxText, this.maxValue));
17420             return false;
17421         }
17422         return true;
17423     },
17424
17425     getValue : function(){
17426         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17427     },
17428
17429     // private
17430     parseValue : function(value){
17431         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17432         return isNaN(value) ? '' : value;
17433     },
17434
17435     // private
17436     fixPrecision : function(value){
17437         var nan = isNaN(value);
17438         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17439             return nan ? '' : value;
17440         }
17441         return parseFloat(value).toFixed(this.decimalPrecision);
17442     },
17443
17444     setValue : function(v){
17445         v = this.fixPrecision(v);
17446         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17447     },
17448
17449     // private
17450     decimalPrecisionFcn : function(v){
17451         return Math.floor(v);
17452     },
17453
17454     beforeBlur : function(){
17455         var v = this.parseValue(this.getRawValue());
17456         if(v){
17457             this.setValue(v);
17458         }
17459     }
17460 });/*
17461  * Based on:
17462  * Ext JS Library 1.1.1
17463  * Copyright(c) 2006-2007, Ext JS, LLC.
17464  *
17465  * Originally Released Under LGPL - original licence link has changed is not relivant.
17466  *
17467  * Fork - LGPL
17468  * <script type="text/javascript">
17469  */
17470  
17471 /**
17472  * @class Roo.form.DateField
17473  * @extends Roo.form.TriggerField
17474  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17475 * @constructor
17476 * Create a new DateField
17477 * @param {Object} config
17478  */
17479 Roo.form.DateField = function(config)
17480 {
17481     Roo.form.DateField.superclass.constructor.call(this, config);
17482     
17483       this.addEvents({
17484          
17485         /**
17486          * @event select
17487          * Fires when a date is selected
17488              * @param {Roo.form.DateField} combo This combo box
17489              * @param {Date} date The date selected
17490              */
17491         'select' : true
17492          
17493     });
17494     
17495     
17496     if(typeof this.minValue == "string") {
17497         this.minValue = this.parseDate(this.minValue);
17498     }
17499     if(typeof this.maxValue == "string") {
17500         this.maxValue = this.parseDate(this.maxValue);
17501     }
17502     this.ddMatch = null;
17503     if(this.disabledDates){
17504         var dd = this.disabledDates;
17505         var re = "(?:";
17506         for(var i = 0; i < dd.length; i++){
17507             re += dd[i];
17508             if(i != dd.length-1) {
17509                 re += "|";
17510             }
17511         }
17512         this.ddMatch = new RegExp(re + ")");
17513     }
17514 };
17515
17516 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17517     /**
17518      * @cfg {String} format
17519      * The default date format string which can be overriden for localization support.  The format must be
17520      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17521      */
17522     format : "m/d/y",
17523     /**
17524      * @cfg {String} altFormats
17525      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17526      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17527      */
17528     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17529     /**
17530      * @cfg {Array} disabledDays
17531      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17532      */
17533     disabledDays : null,
17534     /**
17535      * @cfg {String} disabledDaysText
17536      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17537      */
17538     disabledDaysText : "Disabled",
17539     /**
17540      * @cfg {Array} disabledDates
17541      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17542      * expression so they are very powerful. Some examples:
17543      * <ul>
17544      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17545      * <li>["03/08", "09/16"] would disable those days for every year</li>
17546      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17547      * <li>["03/../2006"] would disable every day in March 2006</li>
17548      * <li>["^03"] would disable every day in every March</li>
17549      * </ul>
17550      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17551      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17552      */
17553     disabledDates : null,
17554     /**
17555      * @cfg {String} disabledDatesText
17556      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17557      */
17558     disabledDatesText : "Disabled",
17559     /**
17560      * @cfg {Date/String} minValue
17561      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17562      * valid format (defaults to null).
17563      */
17564     minValue : null,
17565     /**
17566      * @cfg {Date/String} maxValue
17567      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17568      * valid format (defaults to null).
17569      */
17570     maxValue : null,
17571     /**
17572      * @cfg {String} minText
17573      * The error text to display when the date in the cell is before minValue (defaults to
17574      * 'The date in this field must be after {minValue}').
17575      */
17576     minText : "The date in this field must be equal to or after {0}",
17577     /**
17578      * @cfg {String} maxText
17579      * The error text to display when the date in the cell is after maxValue (defaults to
17580      * 'The date in this field must be before {maxValue}').
17581      */
17582     maxText : "The date in this field must be equal to or before {0}",
17583     /**
17584      * @cfg {String} invalidText
17585      * The error text to display when the date in the field is invalid (defaults to
17586      * '{value} is not a valid date - it must be in the format {format}').
17587      */
17588     invalidText : "{0} is not a valid date - it must be in the format {1}",
17589     /**
17590      * @cfg {String} triggerClass
17591      * An additional CSS class used to style the trigger button.  The trigger will always get the
17592      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17593      * which displays a calendar icon).
17594      */
17595     triggerClass : 'x-form-date-trigger',
17596     
17597
17598     /**
17599      * @cfg {Boolean} useIso
17600      * if enabled, then the date field will use a hidden field to store the 
17601      * real value as iso formated date. default (false)
17602      */ 
17603     useIso : false,
17604     /**
17605      * @cfg {String/Object} autoCreate
17606      * A DomHelper element spec, or true for a default element spec (defaults to
17607      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17608      */ 
17609     // private
17610     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17611     
17612     // private
17613     hiddenField: false,
17614     
17615     onRender : function(ct, position)
17616     {
17617         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17618         if (this.useIso) {
17619             //this.el.dom.removeAttribute('name'); 
17620             Roo.log("Changing name?");
17621             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17622             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17623                     'before', true);
17624             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17625             // prevent input submission
17626             this.hiddenName = this.name;
17627         }
17628             
17629             
17630     },
17631     
17632     // private
17633     validateValue : function(value)
17634     {
17635         value = this.formatDate(value);
17636         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17637             Roo.log('super failed');
17638             return false;
17639         }
17640         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17641              return true;
17642         }
17643         var svalue = value;
17644         value = this.parseDate(value);
17645         if(!value){
17646             Roo.log('parse date failed' + svalue);
17647             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17648             return false;
17649         }
17650         var time = value.getTime();
17651         if(this.minValue && time < this.minValue.getTime()){
17652             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17653             return false;
17654         }
17655         if(this.maxValue && time > this.maxValue.getTime()){
17656             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17657             return false;
17658         }
17659         if(this.disabledDays){
17660             var day = value.getDay();
17661             for(var i = 0; i < this.disabledDays.length; i++) {
17662                 if(day === this.disabledDays[i]){
17663                     this.markInvalid(this.disabledDaysText);
17664                     return false;
17665                 }
17666             }
17667         }
17668         var fvalue = this.formatDate(value);
17669         if(this.ddMatch && this.ddMatch.test(fvalue)){
17670             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17671             return false;
17672         }
17673         return true;
17674     },
17675
17676     // private
17677     // Provides logic to override the default TriggerField.validateBlur which just returns true
17678     validateBlur : function(){
17679         return !this.menu || !this.menu.isVisible();
17680     },
17681     
17682     getName: function()
17683     {
17684         // returns hidden if it's set..
17685         if (!this.rendered) {return ''};
17686         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17687         
17688     },
17689
17690     /**
17691      * Returns the current date value of the date field.
17692      * @return {Date} The date value
17693      */
17694     getValue : function(){
17695         
17696         return  this.hiddenField ?
17697                 this.hiddenField.value :
17698                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17699     },
17700
17701     /**
17702      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17703      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17704      * (the default format used is "m/d/y").
17705      * <br />Usage:
17706      * <pre><code>
17707 //All of these calls set the same date value (May 4, 2006)
17708
17709 //Pass a date object:
17710 var dt = new Date('5/4/06');
17711 dateField.setValue(dt);
17712
17713 //Pass a date string (default format):
17714 dateField.setValue('5/4/06');
17715
17716 //Pass a date string (custom format):
17717 dateField.format = 'Y-m-d';
17718 dateField.setValue('2006-5-4');
17719 </code></pre>
17720      * @param {String/Date} date The date or valid date string
17721      */
17722     setValue : function(date){
17723         if (this.hiddenField) {
17724             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17725         }
17726         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17727         // make sure the value field is always stored as a date..
17728         this.value = this.parseDate(date);
17729         
17730         
17731     },
17732
17733     // private
17734     parseDate : function(value){
17735         if(!value || value instanceof Date){
17736             return value;
17737         }
17738         var v = Date.parseDate(value, this.format);
17739          if (!v && this.useIso) {
17740             v = Date.parseDate(value, 'Y-m-d');
17741         }
17742         if(!v && this.altFormats){
17743             if(!this.altFormatsArray){
17744                 this.altFormatsArray = this.altFormats.split("|");
17745             }
17746             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17747                 v = Date.parseDate(value, this.altFormatsArray[i]);
17748             }
17749         }
17750         return v;
17751     },
17752
17753     // private
17754     formatDate : function(date, fmt){
17755         return (!date || !(date instanceof Date)) ?
17756                date : date.dateFormat(fmt || this.format);
17757     },
17758
17759     // private
17760     menuListeners : {
17761         select: function(m, d){
17762             
17763             this.setValue(d);
17764             this.fireEvent('select', this, d);
17765         },
17766         show : function(){ // retain focus styling
17767             this.onFocus();
17768         },
17769         hide : function(){
17770             this.focus.defer(10, this);
17771             var ml = this.menuListeners;
17772             this.menu.un("select", ml.select,  this);
17773             this.menu.un("show", ml.show,  this);
17774             this.menu.un("hide", ml.hide,  this);
17775         }
17776     },
17777
17778     // private
17779     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17780     onTriggerClick : function(){
17781         if(this.disabled){
17782             return;
17783         }
17784         if(this.menu == null){
17785             this.menu = new Roo.menu.DateMenu();
17786         }
17787         Roo.apply(this.menu.picker,  {
17788             showClear: this.allowBlank,
17789             minDate : this.minValue,
17790             maxDate : this.maxValue,
17791             disabledDatesRE : this.ddMatch,
17792             disabledDatesText : this.disabledDatesText,
17793             disabledDays : this.disabledDays,
17794             disabledDaysText : this.disabledDaysText,
17795             format : this.useIso ? 'Y-m-d' : this.format,
17796             minText : String.format(this.minText, this.formatDate(this.minValue)),
17797             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17798         });
17799         this.menu.on(Roo.apply({}, this.menuListeners, {
17800             scope:this
17801         }));
17802         this.menu.picker.setValue(this.getValue() || new Date());
17803         this.menu.show(this.el, "tl-bl?");
17804     },
17805
17806     beforeBlur : function(){
17807         var v = this.parseDate(this.getRawValue());
17808         if(v){
17809             this.setValue(v);
17810         }
17811     },
17812
17813     /*@
17814      * overide
17815      * 
17816      */
17817     isDirty : function() {
17818         if(this.disabled) {
17819             return false;
17820         }
17821         
17822         if(typeof(this.startValue) === 'undefined'){
17823             return false;
17824         }
17825         
17826         return String(this.getValue()) !== String(this.startValue);
17827         
17828     },
17829     // @overide
17830     cleanLeadingSpace : function(e)
17831     {
17832        return;
17833     }
17834     
17835 });/*
17836  * Based on:
17837  * Ext JS Library 1.1.1
17838  * Copyright(c) 2006-2007, Ext JS, LLC.
17839  *
17840  * Originally Released Under LGPL - original licence link has changed is not relivant.
17841  *
17842  * Fork - LGPL
17843  * <script type="text/javascript">
17844  */
17845  
17846 /**
17847  * @class Roo.form.MonthField
17848  * @extends Roo.form.TriggerField
17849  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17850 * @constructor
17851 * Create a new MonthField
17852 * @param {Object} config
17853  */
17854 Roo.form.MonthField = function(config){
17855     
17856     Roo.form.MonthField.superclass.constructor.call(this, config);
17857     
17858       this.addEvents({
17859          
17860         /**
17861          * @event select
17862          * Fires when a date is selected
17863              * @param {Roo.form.MonthFieeld} combo This combo box
17864              * @param {Date} date The date selected
17865              */
17866         'select' : true
17867          
17868     });
17869     
17870     
17871     if(typeof this.minValue == "string") {
17872         this.minValue = this.parseDate(this.minValue);
17873     }
17874     if(typeof this.maxValue == "string") {
17875         this.maxValue = this.parseDate(this.maxValue);
17876     }
17877     this.ddMatch = null;
17878     if(this.disabledDates){
17879         var dd = this.disabledDates;
17880         var re = "(?:";
17881         for(var i = 0; i < dd.length; i++){
17882             re += dd[i];
17883             if(i != dd.length-1) {
17884                 re += "|";
17885             }
17886         }
17887         this.ddMatch = new RegExp(re + ")");
17888     }
17889 };
17890
17891 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17892     /**
17893      * @cfg {String} format
17894      * The default date format string which can be overriden for localization support.  The format must be
17895      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17896      */
17897     format : "M Y",
17898     /**
17899      * @cfg {String} altFormats
17900      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17901      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17902      */
17903     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17904     /**
17905      * @cfg {Array} disabledDays
17906      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17907      */
17908     disabledDays : [0,1,2,3,4,5,6],
17909     /**
17910      * @cfg {String} disabledDaysText
17911      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17912      */
17913     disabledDaysText : "Disabled",
17914     /**
17915      * @cfg {Array} disabledDates
17916      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17917      * expression so they are very powerful. Some examples:
17918      * <ul>
17919      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17920      * <li>["03/08", "09/16"] would disable those days for every year</li>
17921      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17922      * <li>["03/../2006"] would disable every day in March 2006</li>
17923      * <li>["^03"] would disable every day in every March</li>
17924      * </ul>
17925      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17926      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17927      */
17928     disabledDates : null,
17929     /**
17930      * @cfg {String} disabledDatesText
17931      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17932      */
17933     disabledDatesText : "Disabled",
17934     /**
17935      * @cfg {Date/String} minValue
17936      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17937      * valid format (defaults to null).
17938      */
17939     minValue : null,
17940     /**
17941      * @cfg {Date/String} maxValue
17942      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17943      * valid format (defaults to null).
17944      */
17945     maxValue : null,
17946     /**
17947      * @cfg {String} minText
17948      * The error text to display when the date in the cell is before minValue (defaults to
17949      * 'The date in this field must be after {minValue}').
17950      */
17951     minText : "The date in this field must be equal to or after {0}",
17952     /**
17953      * @cfg {String} maxTextf
17954      * The error text to display when the date in the cell is after maxValue (defaults to
17955      * 'The date in this field must be before {maxValue}').
17956      */
17957     maxText : "The date in this field must be equal to or before {0}",
17958     /**
17959      * @cfg {String} invalidText
17960      * The error text to display when the date in the field is invalid (defaults to
17961      * '{value} is not a valid date - it must be in the format {format}').
17962      */
17963     invalidText : "{0} is not a valid date - it must be in the format {1}",
17964     /**
17965      * @cfg {String} triggerClass
17966      * An additional CSS class used to style the trigger button.  The trigger will always get the
17967      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17968      * which displays a calendar icon).
17969      */
17970     triggerClass : 'x-form-date-trigger',
17971     
17972
17973     /**
17974      * @cfg {Boolean} useIso
17975      * if enabled, then the date field will use a hidden field to store the 
17976      * real value as iso formated date. default (true)
17977      */ 
17978     useIso : true,
17979     /**
17980      * @cfg {String/Object} autoCreate
17981      * A DomHelper element spec, or true for a default element spec (defaults to
17982      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17983      */ 
17984     // private
17985     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17986     
17987     // private
17988     hiddenField: false,
17989     
17990     hideMonthPicker : false,
17991     
17992     onRender : function(ct, position)
17993     {
17994         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17995         if (this.useIso) {
17996             this.el.dom.removeAttribute('name'); 
17997             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17998                     'before', true);
17999             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18000             // prevent input submission
18001             this.hiddenName = this.name;
18002         }
18003             
18004             
18005     },
18006     
18007     // private
18008     validateValue : function(value)
18009     {
18010         value = this.formatDate(value);
18011         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18012             return false;
18013         }
18014         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18015              return true;
18016         }
18017         var svalue = value;
18018         value = this.parseDate(value);
18019         if(!value){
18020             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18021             return false;
18022         }
18023         var time = value.getTime();
18024         if(this.minValue && time < this.minValue.getTime()){
18025             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18026             return false;
18027         }
18028         if(this.maxValue && time > this.maxValue.getTime()){
18029             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18030             return false;
18031         }
18032         /*if(this.disabledDays){
18033             var day = value.getDay();
18034             for(var i = 0; i < this.disabledDays.length; i++) {
18035                 if(day === this.disabledDays[i]){
18036                     this.markInvalid(this.disabledDaysText);
18037                     return false;
18038                 }
18039             }
18040         }
18041         */
18042         var fvalue = this.formatDate(value);
18043         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18044             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18045             return false;
18046         }
18047         */
18048         return true;
18049     },
18050
18051     // private
18052     // Provides logic to override the default TriggerField.validateBlur which just returns true
18053     validateBlur : function(){
18054         return !this.menu || !this.menu.isVisible();
18055     },
18056
18057     /**
18058      * Returns the current date value of the date field.
18059      * @return {Date} The date value
18060      */
18061     getValue : function(){
18062         
18063         
18064         
18065         return  this.hiddenField ?
18066                 this.hiddenField.value :
18067                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18068     },
18069
18070     /**
18071      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18072      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18073      * (the default format used is "m/d/y").
18074      * <br />Usage:
18075      * <pre><code>
18076 //All of these calls set the same date value (May 4, 2006)
18077
18078 //Pass a date object:
18079 var dt = new Date('5/4/06');
18080 monthField.setValue(dt);
18081
18082 //Pass a date string (default format):
18083 monthField.setValue('5/4/06');
18084
18085 //Pass a date string (custom format):
18086 monthField.format = 'Y-m-d';
18087 monthField.setValue('2006-5-4');
18088 </code></pre>
18089      * @param {String/Date} date The date or valid date string
18090      */
18091     setValue : function(date){
18092         Roo.log('month setValue' + date);
18093         // can only be first of month..
18094         
18095         var val = this.parseDate(date);
18096         
18097         if (this.hiddenField) {
18098             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18099         }
18100         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18101         this.value = this.parseDate(date);
18102     },
18103
18104     // private
18105     parseDate : function(value){
18106         if(!value || value instanceof Date){
18107             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18108             return value;
18109         }
18110         var v = Date.parseDate(value, this.format);
18111         if (!v && this.useIso) {
18112             v = Date.parseDate(value, 'Y-m-d');
18113         }
18114         if (v) {
18115             // 
18116             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18117         }
18118         
18119         
18120         if(!v && this.altFormats){
18121             if(!this.altFormatsArray){
18122                 this.altFormatsArray = this.altFormats.split("|");
18123             }
18124             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18125                 v = Date.parseDate(value, this.altFormatsArray[i]);
18126             }
18127         }
18128         return v;
18129     },
18130
18131     // private
18132     formatDate : function(date, fmt){
18133         return (!date || !(date instanceof Date)) ?
18134                date : date.dateFormat(fmt || this.format);
18135     },
18136
18137     // private
18138     menuListeners : {
18139         select: function(m, d){
18140             this.setValue(d);
18141             this.fireEvent('select', this, d);
18142         },
18143         show : function(){ // retain focus styling
18144             this.onFocus();
18145         },
18146         hide : function(){
18147             this.focus.defer(10, this);
18148             var ml = this.menuListeners;
18149             this.menu.un("select", ml.select,  this);
18150             this.menu.un("show", ml.show,  this);
18151             this.menu.un("hide", ml.hide,  this);
18152         }
18153     },
18154     // private
18155     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18156     onTriggerClick : function(){
18157         if(this.disabled){
18158             return;
18159         }
18160         if(this.menu == null){
18161             this.menu = new Roo.menu.DateMenu();
18162            
18163         }
18164         
18165         Roo.apply(this.menu.picker,  {
18166             
18167             showClear: this.allowBlank,
18168             minDate : this.minValue,
18169             maxDate : this.maxValue,
18170             disabledDatesRE : this.ddMatch,
18171             disabledDatesText : this.disabledDatesText,
18172             
18173             format : this.useIso ? 'Y-m-d' : this.format,
18174             minText : String.format(this.minText, this.formatDate(this.minValue)),
18175             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18176             
18177         });
18178          this.menu.on(Roo.apply({}, this.menuListeners, {
18179             scope:this
18180         }));
18181        
18182         
18183         var m = this.menu;
18184         var p = m.picker;
18185         
18186         // hide month picker get's called when we called by 'before hide';
18187         
18188         var ignorehide = true;
18189         p.hideMonthPicker  = function(disableAnim){
18190             if (ignorehide) {
18191                 return;
18192             }
18193              if(this.monthPicker){
18194                 Roo.log("hideMonthPicker called");
18195                 if(disableAnim === true){
18196                     this.monthPicker.hide();
18197                 }else{
18198                     this.monthPicker.slideOut('t', {duration:.2});
18199                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18200                     p.fireEvent("select", this, this.value);
18201                     m.hide();
18202                 }
18203             }
18204         }
18205         
18206         Roo.log('picker set value');
18207         Roo.log(this.getValue());
18208         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18209         m.show(this.el, 'tl-bl?');
18210         ignorehide  = false;
18211         // this will trigger hideMonthPicker..
18212         
18213         
18214         // hidden the day picker
18215         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18216         
18217         
18218         
18219       
18220         
18221         p.showMonthPicker.defer(100, p);
18222     
18223         
18224        
18225     },
18226
18227     beforeBlur : function(){
18228         var v = this.parseDate(this.getRawValue());
18229         if(v){
18230             this.setValue(v);
18231         }
18232     }
18233
18234     /** @cfg {Boolean} grow @hide */
18235     /** @cfg {Number} growMin @hide */
18236     /** @cfg {Number} growMax @hide */
18237     /**
18238      * @hide
18239      * @method autoSize
18240      */
18241 });/*
18242  * Based on:
18243  * Ext JS Library 1.1.1
18244  * Copyright(c) 2006-2007, Ext JS, LLC.
18245  *
18246  * Originally Released Under LGPL - original licence link has changed is not relivant.
18247  *
18248  * Fork - LGPL
18249  * <script type="text/javascript">
18250  */
18251  
18252
18253 /**
18254  * @class Roo.form.ComboBox
18255  * @extends Roo.form.TriggerField
18256  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18257  * @constructor
18258  * Create a new ComboBox.
18259  * @param {Object} config Configuration options
18260  */
18261 Roo.form.ComboBox = function(config){
18262     Roo.form.ComboBox.superclass.constructor.call(this, config);
18263     this.addEvents({
18264         /**
18265          * @event expand
18266          * Fires when the dropdown list is expanded
18267              * @param {Roo.form.ComboBox} combo This combo box
18268              */
18269         'expand' : true,
18270         /**
18271          * @event collapse
18272          * Fires when the dropdown list is collapsed
18273              * @param {Roo.form.ComboBox} combo This combo box
18274              */
18275         'collapse' : true,
18276         /**
18277          * @event beforeselect
18278          * Fires before a list item is selected. Return false to cancel the selection.
18279              * @param {Roo.form.ComboBox} combo This combo box
18280              * @param {Roo.data.Record} record The data record returned from the underlying store
18281              * @param {Number} index The index of the selected item in the dropdown list
18282              */
18283         'beforeselect' : true,
18284         /**
18285          * @event select
18286          * Fires when a list item is selected
18287              * @param {Roo.form.ComboBox} combo This combo box
18288              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18289              * @param {Number} index The index of the selected item in the dropdown list
18290              */
18291         'select' : true,
18292         /**
18293          * @event beforequery
18294          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18295          * The event object passed has these properties:
18296              * @param {Roo.form.ComboBox} combo This combo box
18297              * @param {String} query The query
18298              * @param {Boolean} forceAll true to force "all" query
18299              * @param {Boolean} cancel true to cancel the query
18300              * @param {Object} e The query event object
18301              */
18302         'beforequery': true,
18303          /**
18304          * @event add
18305          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18306              * @param {Roo.form.ComboBox} combo This combo box
18307              */
18308         'add' : true,
18309         /**
18310          * @event edit
18311          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18312              * @param {Roo.form.ComboBox} combo This combo box
18313              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18314              */
18315         'edit' : true
18316         
18317         
18318     });
18319     if(this.transform){
18320         this.allowDomMove = false;
18321         var s = Roo.getDom(this.transform);
18322         if(!this.hiddenName){
18323             this.hiddenName = s.name;
18324         }
18325         if(!this.store){
18326             this.mode = 'local';
18327             var d = [], opts = s.options;
18328             for(var i = 0, len = opts.length;i < len; i++){
18329                 var o = opts[i];
18330                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18331                 if(o.selected) {
18332                     this.value = value;
18333                 }
18334                 d.push([value, o.text]);
18335             }
18336             this.store = new Roo.data.SimpleStore({
18337                 'id': 0,
18338                 fields: ['value', 'text'],
18339                 data : d
18340             });
18341             this.valueField = 'value';
18342             this.displayField = 'text';
18343         }
18344         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18345         if(!this.lazyRender){
18346             this.target = true;
18347             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18348             s.parentNode.removeChild(s); // remove it
18349             this.render(this.el.parentNode);
18350         }else{
18351             s.parentNode.removeChild(s); // remove it
18352         }
18353
18354     }
18355     if (this.store) {
18356         this.store = Roo.factory(this.store, Roo.data);
18357     }
18358     
18359     this.selectedIndex = -1;
18360     if(this.mode == 'local'){
18361         if(config.queryDelay === undefined){
18362             this.queryDelay = 10;
18363         }
18364         if(config.minChars === undefined){
18365             this.minChars = 0;
18366         }
18367     }
18368 };
18369
18370 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18371     /**
18372      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18373      */
18374     /**
18375      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18376      * rendering into an Roo.Editor, defaults to false)
18377      */
18378     /**
18379      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18380      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18381      */
18382     /**
18383      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18384      */
18385     /**
18386      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18387      * the dropdown list (defaults to undefined, with no header element)
18388      */
18389
18390      /**
18391      * @cfg {String/Roo.Template} tpl The template to use to render the output
18392      */
18393      
18394     // private
18395     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18396     /**
18397      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18398      */
18399     listWidth: undefined,
18400     /**
18401      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18402      * mode = 'remote' or 'text' if mode = 'local')
18403      */
18404     displayField: undefined,
18405     /**
18406      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18407      * mode = 'remote' or 'value' if mode = 'local'). 
18408      * Note: use of a valueField requires the user make a selection
18409      * in order for a value to be mapped.
18410      */
18411     valueField: undefined,
18412     
18413     
18414     /**
18415      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18416      * field's data value (defaults to the underlying DOM element's name)
18417      */
18418     hiddenName: undefined,
18419     /**
18420      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18421      */
18422     listClass: '',
18423     /**
18424      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18425      */
18426     selectedClass: 'x-combo-selected',
18427     /**
18428      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18429      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18430      * which displays a downward arrow icon).
18431      */
18432     triggerClass : 'x-form-arrow-trigger',
18433     /**
18434      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18435      */
18436     shadow:'sides',
18437     /**
18438      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18439      * anchor positions (defaults to 'tl-bl')
18440      */
18441     listAlign: 'tl-bl?',
18442     /**
18443      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18444      */
18445     maxHeight: 300,
18446     /**
18447      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18448      * query specified by the allQuery config option (defaults to 'query')
18449      */
18450     triggerAction: 'query',
18451     /**
18452      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18453      * (defaults to 4, does not apply if editable = false)
18454      */
18455     minChars : 4,
18456     /**
18457      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18458      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18459      */
18460     typeAhead: false,
18461     /**
18462      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18463      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18464      */
18465     queryDelay: 500,
18466     /**
18467      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18468      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18469      */
18470     pageSize: 0,
18471     /**
18472      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18473      * when editable = true (defaults to false)
18474      */
18475     selectOnFocus:false,
18476     /**
18477      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18478      */
18479     queryParam: 'query',
18480     /**
18481      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18482      * when mode = 'remote' (defaults to 'Loading...')
18483      */
18484     loadingText: 'Loading...',
18485     /**
18486      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18487      */
18488     resizable: false,
18489     /**
18490      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18491      */
18492     handleHeight : 8,
18493     /**
18494      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18495      * traditional select (defaults to true)
18496      */
18497     editable: true,
18498     /**
18499      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18500      */
18501     allQuery: '',
18502     /**
18503      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18504      */
18505     mode: 'remote',
18506     /**
18507      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18508      * listWidth has a higher value)
18509      */
18510     minListWidth : 70,
18511     /**
18512      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18513      * allow the user to set arbitrary text into the field (defaults to false)
18514      */
18515     forceSelection:false,
18516     /**
18517      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18518      * if typeAhead = true (defaults to 250)
18519      */
18520     typeAheadDelay : 250,
18521     /**
18522      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18523      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18524      */
18525     valueNotFoundText : undefined,
18526     /**
18527      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18528      */
18529     blockFocus : false,
18530     
18531     /**
18532      * @cfg {Boolean} disableClear Disable showing of clear button.
18533      */
18534     disableClear : false,
18535     /**
18536      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18537      */
18538     alwaysQuery : false,
18539     
18540     //private
18541     addicon : false,
18542     editicon: false,
18543     
18544     // element that contains real text value.. (when hidden is used..)
18545      
18546     // private
18547     onRender : function(ct, position)
18548     {
18549         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18550         
18551         if(this.hiddenName){
18552             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18553                     'before', true);
18554             this.hiddenField.value =
18555                 this.hiddenValue !== undefined ? this.hiddenValue :
18556                 this.value !== undefined ? this.value : '';
18557
18558             // prevent input submission
18559             this.el.dom.removeAttribute('name');
18560              
18561              
18562         }
18563         
18564         if(Roo.isGecko){
18565             this.el.dom.setAttribute('autocomplete', 'off');
18566         }
18567
18568         var cls = 'x-combo-list';
18569
18570         this.list = new Roo.Layer({
18571             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18572         });
18573
18574         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18575         this.list.setWidth(lw);
18576         this.list.swallowEvent('mousewheel');
18577         this.assetHeight = 0;
18578
18579         if(this.title){
18580             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18581             this.assetHeight += this.header.getHeight();
18582         }
18583
18584         this.innerList = this.list.createChild({cls:cls+'-inner'});
18585         this.innerList.on('mouseover', this.onViewOver, this);
18586         this.innerList.on('mousemove', this.onViewMove, this);
18587         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18588         
18589         if(this.allowBlank && !this.pageSize && !this.disableClear){
18590             this.footer = this.list.createChild({cls:cls+'-ft'});
18591             this.pageTb = new Roo.Toolbar(this.footer);
18592            
18593         }
18594         if(this.pageSize){
18595             this.footer = this.list.createChild({cls:cls+'-ft'});
18596             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18597                     {pageSize: this.pageSize});
18598             
18599         }
18600         
18601         if (this.pageTb && this.allowBlank && !this.disableClear) {
18602             var _this = this;
18603             this.pageTb.add(new Roo.Toolbar.Fill(), {
18604                 cls: 'x-btn-icon x-btn-clear',
18605                 text: '&#160;',
18606                 handler: function()
18607                 {
18608                     _this.collapse();
18609                     _this.clearValue();
18610                     _this.onSelect(false, -1);
18611                 }
18612             });
18613         }
18614         if (this.footer) {
18615             this.assetHeight += this.footer.getHeight();
18616         }
18617         
18618
18619         if(!this.tpl){
18620             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18621         }
18622
18623         this.view = new Roo.View(this.innerList, this.tpl, {
18624             singleSelect:true,
18625             store: this.store,
18626             selectedClass: this.selectedClass
18627         });
18628
18629         this.view.on('click', this.onViewClick, this);
18630
18631         this.store.on('beforeload', this.onBeforeLoad, this);
18632         this.store.on('load', this.onLoad, this);
18633         this.store.on('loadexception', this.onLoadException, this);
18634
18635         if(this.resizable){
18636             this.resizer = new Roo.Resizable(this.list,  {
18637                pinned:true, handles:'se'
18638             });
18639             this.resizer.on('resize', function(r, w, h){
18640                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18641                 this.listWidth = w;
18642                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18643                 this.restrictHeight();
18644             }, this);
18645             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18646         }
18647         if(!this.editable){
18648             this.editable = true;
18649             this.setEditable(false);
18650         }  
18651         
18652         
18653         if (typeof(this.events.add.listeners) != 'undefined') {
18654             
18655             this.addicon = this.wrap.createChild(
18656                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18657        
18658             this.addicon.on('click', function(e) {
18659                 this.fireEvent('add', this);
18660             }, this);
18661         }
18662         if (typeof(this.events.edit.listeners) != 'undefined') {
18663             
18664             this.editicon = this.wrap.createChild(
18665                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18666             if (this.addicon) {
18667                 this.editicon.setStyle('margin-left', '40px');
18668             }
18669             this.editicon.on('click', function(e) {
18670                 
18671                 // we fire even  if inothing is selected..
18672                 this.fireEvent('edit', this, this.lastData );
18673                 
18674             }, this);
18675         }
18676         
18677         
18678         
18679     },
18680
18681     // private
18682     initEvents : function(){
18683         Roo.form.ComboBox.superclass.initEvents.call(this);
18684
18685         this.keyNav = new Roo.KeyNav(this.el, {
18686             "up" : function(e){
18687                 this.inKeyMode = true;
18688                 this.selectPrev();
18689             },
18690
18691             "down" : function(e){
18692                 if(!this.isExpanded()){
18693                     this.onTriggerClick();
18694                 }else{
18695                     this.inKeyMode = true;
18696                     this.selectNext();
18697                 }
18698             },
18699
18700             "enter" : function(e){
18701                 this.onViewClick();
18702                 //return true;
18703             },
18704
18705             "esc" : function(e){
18706                 this.collapse();
18707             },
18708
18709             "tab" : function(e){
18710                 this.onViewClick(false);
18711                 this.fireEvent("specialkey", this, e);
18712                 return true;
18713             },
18714
18715             scope : this,
18716
18717             doRelay : function(foo, bar, hname){
18718                 if(hname == 'down' || this.scope.isExpanded()){
18719                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18720                 }
18721                 return true;
18722             },
18723
18724             forceKeyDown: true
18725         });
18726         this.queryDelay = Math.max(this.queryDelay || 10,
18727                 this.mode == 'local' ? 10 : 250);
18728         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18729         if(this.typeAhead){
18730             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18731         }
18732         if(this.editable !== false){
18733             this.el.on("keyup", this.onKeyUp, this);
18734         }
18735         if(this.forceSelection){
18736             this.on('blur', this.doForce, this);
18737         }
18738     },
18739
18740     onDestroy : function(){
18741         if(this.view){
18742             this.view.setStore(null);
18743             this.view.el.removeAllListeners();
18744             this.view.el.remove();
18745             this.view.purgeListeners();
18746         }
18747         if(this.list){
18748             this.list.destroy();
18749         }
18750         if(this.store){
18751             this.store.un('beforeload', this.onBeforeLoad, this);
18752             this.store.un('load', this.onLoad, this);
18753             this.store.un('loadexception', this.onLoadException, this);
18754         }
18755         Roo.form.ComboBox.superclass.onDestroy.call(this);
18756     },
18757
18758     // private
18759     fireKey : function(e){
18760         if(e.isNavKeyPress() && !this.list.isVisible()){
18761             this.fireEvent("specialkey", this, e);
18762         }
18763     },
18764
18765     // private
18766     onResize: function(w, h){
18767         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18768         
18769         if(typeof w != 'number'){
18770             // we do not handle it!?!?
18771             return;
18772         }
18773         var tw = this.trigger.getWidth();
18774         tw += this.addicon ? this.addicon.getWidth() : 0;
18775         tw += this.editicon ? this.editicon.getWidth() : 0;
18776         var x = w - tw;
18777         this.el.setWidth( this.adjustWidth('input', x));
18778             
18779         this.trigger.setStyle('left', x+'px');
18780         
18781         if(this.list && this.listWidth === undefined){
18782             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18783             this.list.setWidth(lw);
18784             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18785         }
18786         
18787     
18788         
18789     },
18790
18791     /**
18792      * Allow or prevent the user from directly editing the field text.  If false is passed,
18793      * the user will only be able to select from the items defined in the dropdown list.  This method
18794      * is the runtime equivalent of setting the 'editable' config option at config time.
18795      * @param {Boolean} value True to allow the user to directly edit the field text
18796      */
18797     setEditable : function(value){
18798         if(value == this.editable){
18799             return;
18800         }
18801         this.editable = value;
18802         if(!value){
18803             this.el.dom.setAttribute('readOnly', true);
18804             this.el.on('mousedown', this.onTriggerClick,  this);
18805             this.el.addClass('x-combo-noedit');
18806         }else{
18807             this.el.dom.setAttribute('readOnly', false);
18808             this.el.un('mousedown', this.onTriggerClick,  this);
18809             this.el.removeClass('x-combo-noedit');
18810         }
18811     },
18812
18813     // private
18814     onBeforeLoad : function(){
18815         if(!this.hasFocus){
18816             return;
18817         }
18818         this.innerList.update(this.loadingText ?
18819                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18820         this.restrictHeight();
18821         this.selectedIndex = -1;
18822     },
18823
18824     // private
18825     onLoad : function(){
18826         if(!this.hasFocus){
18827             return;
18828         }
18829         if(this.store.getCount() > 0){
18830             this.expand();
18831             this.restrictHeight();
18832             if(this.lastQuery == this.allQuery){
18833                 if(this.editable){
18834                     this.el.dom.select();
18835                 }
18836                 if(!this.selectByValue(this.value, true)){
18837                     this.select(0, true);
18838                 }
18839             }else{
18840                 this.selectNext();
18841                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18842                     this.taTask.delay(this.typeAheadDelay);
18843                 }
18844             }
18845         }else{
18846             this.onEmptyResults();
18847         }
18848         //this.el.focus();
18849     },
18850     // private
18851     onLoadException : function()
18852     {
18853         this.collapse();
18854         Roo.log(this.store.reader.jsonData);
18855         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18856             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18857         }
18858         
18859         
18860     },
18861     // private
18862     onTypeAhead : function(){
18863         if(this.store.getCount() > 0){
18864             var r = this.store.getAt(0);
18865             var newValue = r.data[this.displayField];
18866             var len = newValue.length;
18867             var selStart = this.getRawValue().length;
18868             if(selStart != len){
18869                 this.setRawValue(newValue);
18870                 this.selectText(selStart, newValue.length);
18871             }
18872         }
18873     },
18874
18875     // private
18876     onSelect : function(record, index){
18877         if(this.fireEvent('beforeselect', this, record, index) !== false){
18878             this.setFromData(index > -1 ? record.data : false);
18879             this.collapse();
18880             this.fireEvent('select', this, record, index);
18881         }
18882     },
18883
18884     /**
18885      * Returns the currently selected field value or empty string if no value is set.
18886      * @return {String} value The selected value
18887      */
18888     getValue : function(){
18889         if(this.valueField){
18890             return typeof this.value != 'undefined' ? this.value : '';
18891         }
18892         return Roo.form.ComboBox.superclass.getValue.call(this);
18893     },
18894
18895     /**
18896      * Clears any text/value currently set in the field
18897      */
18898     clearValue : function(){
18899         if(this.hiddenField){
18900             this.hiddenField.value = '';
18901         }
18902         this.value = '';
18903         this.setRawValue('');
18904         this.lastSelectionText = '';
18905         
18906     },
18907
18908     /**
18909      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18910      * will be displayed in the field.  If the value does not match the data value of an existing item,
18911      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18912      * Otherwise the field will be blank (although the value will still be set).
18913      * @param {String} value The value to match
18914      */
18915     setValue : function(v){
18916         var text = v;
18917         if(this.valueField){
18918             var r = this.findRecord(this.valueField, v);
18919             if(r){
18920                 text = r.data[this.displayField];
18921             }else if(this.valueNotFoundText !== undefined){
18922                 text = this.valueNotFoundText;
18923             }
18924         }
18925         this.lastSelectionText = text;
18926         if(this.hiddenField){
18927             this.hiddenField.value = v;
18928         }
18929         Roo.form.ComboBox.superclass.setValue.call(this, text);
18930         this.value = v;
18931     },
18932     /**
18933      * @property {Object} the last set data for the element
18934      */
18935     
18936     lastData : false,
18937     /**
18938      * Sets the value of the field based on a object which is related to the record format for the store.
18939      * @param {Object} value the value to set as. or false on reset?
18940      */
18941     setFromData : function(o){
18942         var dv = ''; // display value
18943         var vv = ''; // value value..
18944         this.lastData = o;
18945         if (this.displayField) {
18946             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18947         } else {
18948             // this is an error condition!!!
18949             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18950         }
18951         
18952         if(this.valueField){
18953             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18954         }
18955         if(this.hiddenField){
18956             this.hiddenField.value = vv;
18957             
18958             this.lastSelectionText = dv;
18959             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18960             this.value = vv;
18961             return;
18962         }
18963         // no hidden field.. - we store the value in 'value', but still display
18964         // display field!!!!
18965         this.lastSelectionText = dv;
18966         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18967         this.value = vv;
18968         
18969         
18970     },
18971     // private
18972     reset : function(){
18973         // overridden so that last data is reset..
18974         this.setValue(this.resetValue);
18975         this.originalValue = this.getValue();
18976         this.clearInvalid();
18977         this.lastData = false;
18978         if (this.view) {
18979             this.view.clearSelections();
18980         }
18981     },
18982     // private
18983     findRecord : function(prop, value){
18984         var record;
18985         if(this.store.getCount() > 0){
18986             this.store.each(function(r){
18987                 if(r.data[prop] == value){
18988                     record = r;
18989                     return false;
18990                 }
18991                 return true;
18992             });
18993         }
18994         return record;
18995     },
18996     
18997     getName: function()
18998     {
18999         // returns hidden if it's set..
19000         if (!this.rendered) {return ''};
19001         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19002         
19003     },
19004     // private
19005     onViewMove : function(e, t){
19006         this.inKeyMode = false;
19007     },
19008
19009     // private
19010     onViewOver : function(e, t){
19011         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19012             return;
19013         }
19014         var item = this.view.findItemFromChild(t);
19015         if(item){
19016             var index = this.view.indexOf(item);
19017             this.select(index, false);
19018         }
19019     },
19020
19021     // private
19022     onViewClick : function(doFocus)
19023     {
19024         var index = this.view.getSelectedIndexes()[0];
19025         var r = this.store.getAt(index);
19026         if(r){
19027             this.onSelect(r, index);
19028         }
19029         if(doFocus !== false && !this.blockFocus){
19030             this.el.focus();
19031         }
19032     },
19033
19034     // private
19035     restrictHeight : function(){
19036         this.innerList.dom.style.height = '';
19037         var inner = this.innerList.dom;
19038         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19039         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19040         this.list.beginUpdate();
19041         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19042         this.list.alignTo(this.el, this.listAlign);
19043         this.list.endUpdate();
19044     },
19045
19046     // private
19047     onEmptyResults : function(){
19048         this.collapse();
19049     },
19050
19051     /**
19052      * Returns true if the dropdown list is expanded, else false.
19053      */
19054     isExpanded : function(){
19055         return this.list.isVisible();
19056     },
19057
19058     /**
19059      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19060      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19061      * @param {String} value The data value of the item to select
19062      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19063      * selected item if it is not currently in view (defaults to true)
19064      * @return {Boolean} True if the value matched an item in the list, else false
19065      */
19066     selectByValue : function(v, scrollIntoView){
19067         if(v !== undefined && v !== null){
19068             var r = this.findRecord(this.valueField || this.displayField, v);
19069             if(r){
19070                 this.select(this.store.indexOf(r), scrollIntoView);
19071                 return true;
19072             }
19073         }
19074         return false;
19075     },
19076
19077     /**
19078      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19079      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19080      * @param {Number} index The zero-based index of the list item to select
19081      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19082      * selected item if it is not currently in view (defaults to true)
19083      */
19084     select : function(index, scrollIntoView){
19085         this.selectedIndex = index;
19086         this.view.select(index);
19087         if(scrollIntoView !== false){
19088             var el = this.view.getNode(index);
19089             if(el){
19090                 this.innerList.scrollChildIntoView(el, false);
19091             }
19092         }
19093     },
19094
19095     // private
19096     selectNext : function(){
19097         var ct = this.store.getCount();
19098         if(ct > 0){
19099             if(this.selectedIndex == -1){
19100                 this.select(0);
19101             }else if(this.selectedIndex < ct-1){
19102                 this.select(this.selectedIndex+1);
19103             }
19104         }
19105     },
19106
19107     // private
19108     selectPrev : function(){
19109         var ct = this.store.getCount();
19110         if(ct > 0){
19111             if(this.selectedIndex == -1){
19112                 this.select(0);
19113             }else if(this.selectedIndex != 0){
19114                 this.select(this.selectedIndex-1);
19115             }
19116         }
19117     },
19118
19119     // private
19120     onKeyUp : function(e){
19121         if(this.editable !== false && !e.isSpecialKey()){
19122             this.lastKey = e.getKey();
19123             this.dqTask.delay(this.queryDelay);
19124         }
19125     },
19126
19127     // private
19128     validateBlur : function(){
19129         return !this.list || !this.list.isVisible();   
19130     },
19131
19132     // private
19133     initQuery : function(){
19134         this.doQuery(this.getRawValue());
19135     },
19136
19137     // private
19138     doForce : function(){
19139         if(this.el.dom.value.length > 0){
19140             this.el.dom.value =
19141                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19142              
19143         }
19144     },
19145
19146     /**
19147      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19148      * query allowing the query action to be canceled if needed.
19149      * @param {String} query The SQL query to execute
19150      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19151      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19152      * saved in the current store (defaults to false)
19153      */
19154     doQuery : function(q, forceAll){
19155         if(q === undefined || q === null){
19156             q = '';
19157         }
19158         var qe = {
19159             query: q,
19160             forceAll: forceAll,
19161             combo: this,
19162             cancel:false
19163         };
19164         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19165             return false;
19166         }
19167         q = qe.query;
19168         forceAll = qe.forceAll;
19169         if(forceAll === true || (q.length >= this.minChars)){
19170             if(this.lastQuery != q || this.alwaysQuery){
19171                 this.lastQuery = q;
19172                 if(this.mode == 'local'){
19173                     this.selectedIndex = -1;
19174                     if(forceAll){
19175                         this.store.clearFilter();
19176                     }else{
19177                         this.store.filter(this.displayField, q);
19178                     }
19179                     this.onLoad();
19180                 }else{
19181                     this.store.baseParams[this.queryParam] = q;
19182                     this.store.load({
19183                         params: this.getParams(q)
19184                     });
19185                     this.expand();
19186                 }
19187             }else{
19188                 this.selectedIndex = -1;
19189                 this.onLoad();   
19190             }
19191         }
19192     },
19193
19194     // private
19195     getParams : function(q){
19196         var p = {};
19197         //p[this.queryParam] = q;
19198         if(this.pageSize){
19199             p.start = 0;
19200             p.limit = this.pageSize;
19201         }
19202         return p;
19203     },
19204
19205     /**
19206      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19207      */
19208     collapse : function(){
19209         if(!this.isExpanded()){
19210             return;
19211         }
19212         this.list.hide();
19213         Roo.get(document).un('mousedown', this.collapseIf, this);
19214         Roo.get(document).un('mousewheel', this.collapseIf, this);
19215         if (!this.editable) {
19216             Roo.get(document).un('keydown', this.listKeyPress, this);
19217         }
19218         this.fireEvent('collapse', this);
19219     },
19220
19221     // private
19222     collapseIf : function(e){
19223         if(!e.within(this.wrap) && !e.within(this.list)){
19224             this.collapse();
19225         }
19226     },
19227
19228     /**
19229      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19230      */
19231     expand : function(){
19232         if(this.isExpanded() || !this.hasFocus){
19233             return;
19234         }
19235         this.list.alignTo(this.el, this.listAlign);
19236         this.list.show();
19237         Roo.get(document).on('mousedown', this.collapseIf, this);
19238         Roo.get(document).on('mousewheel', this.collapseIf, this);
19239         if (!this.editable) {
19240             Roo.get(document).on('keydown', this.listKeyPress, this);
19241         }
19242         
19243         this.fireEvent('expand', this);
19244     },
19245
19246     // private
19247     // Implements the default empty TriggerField.onTriggerClick function
19248     onTriggerClick : function(){
19249         if(this.disabled){
19250             return;
19251         }
19252         if(this.isExpanded()){
19253             this.collapse();
19254             if (!this.blockFocus) {
19255                 this.el.focus();
19256             }
19257             
19258         }else {
19259             this.hasFocus = true;
19260             if(this.triggerAction == 'all') {
19261                 this.doQuery(this.allQuery, true);
19262             } else {
19263                 this.doQuery(this.getRawValue());
19264             }
19265             if (!this.blockFocus) {
19266                 this.el.focus();
19267             }
19268         }
19269     },
19270     listKeyPress : function(e)
19271     {
19272         //Roo.log('listkeypress');
19273         // scroll to first matching element based on key pres..
19274         if (e.isSpecialKey()) {
19275             return false;
19276         }
19277         var k = String.fromCharCode(e.getKey()).toUpperCase();
19278         //Roo.log(k);
19279         var match  = false;
19280         var csel = this.view.getSelectedNodes();
19281         var cselitem = false;
19282         if (csel.length) {
19283             var ix = this.view.indexOf(csel[0]);
19284             cselitem  = this.store.getAt(ix);
19285             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19286                 cselitem = false;
19287             }
19288             
19289         }
19290         
19291         this.store.each(function(v) { 
19292             if (cselitem) {
19293                 // start at existing selection.
19294                 if (cselitem.id == v.id) {
19295                     cselitem = false;
19296                 }
19297                 return;
19298             }
19299                 
19300             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19301                 match = this.store.indexOf(v);
19302                 return false;
19303             }
19304         }, this);
19305         
19306         if (match === false) {
19307             return true; // no more action?
19308         }
19309         // scroll to?
19310         this.view.select(match);
19311         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19312         sn.scrollIntoView(sn.dom.parentNode, false);
19313     } 
19314
19315     /** 
19316     * @cfg {Boolean} grow 
19317     * @hide 
19318     */
19319     /** 
19320     * @cfg {Number} growMin 
19321     * @hide 
19322     */
19323     /** 
19324     * @cfg {Number} growMax 
19325     * @hide 
19326     */
19327     /**
19328      * @hide
19329      * @method autoSize
19330      */
19331 });/*
19332  * Copyright(c) 2010-2012, Roo J Solutions Limited
19333  *
19334  * Licence LGPL
19335  *
19336  */
19337
19338 /**
19339  * @class Roo.form.ComboBoxArray
19340  * @extends Roo.form.TextField
19341  * A facebook style adder... for lists of email / people / countries  etc...
19342  * pick multiple items from a combo box, and shows each one.
19343  *
19344  *  Fred [x]  Brian [x]  [Pick another |v]
19345  *
19346  *
19347  *  For this to work: it needs various extra information
19348  *    - normal combo problay has
19349  *      name, hiddenName
19350  *    + displayField, valueField
19351  *
19352  *    For our purpose...
19353  *
19354  *
19355  *   If we change from 'extends' to wrapping...
19356  *   
19357  *  
19358  *
19359  
19360  
19361  * @constructor
19362  * Create a new ComboBoxArray.
19363  * @param {Object} config Configuration options
19364  */
19365  
19366
19367 Roo.form.ComboBoxArray = function(config)
19368 {
19369     this.addEvents({
19370         /**
19371          * @event beforeremove
19372          * Fires before remove the value from the list
19373              * @param {Roo.form.ComboBoxArray} _self This combo box array
19374              * @param {Roo.form.ComboBoxArray.Item} item removed item
19375              */
19376         'beforeremove' : true,
19377         /**
19378          * @event remove
19379          * Fires when remove the value from the list
19380              * @param {Roo.form.ComboBoxArray} _self This combo box array
19381              * @param {Roo.form.ComboBoxArray.Item} item removed item
19382              */
19383         'remove' : true
19384         
19385         
19386     });
19387     
19388     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19389     
19390     this.items = new Roo.util.MixedCollection(false);
19391     
19392     // construct the child combo...
19393     
19394     
19395     
19396     
19397    
19398     
19399 }
19400
19401  
19402 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19403
19404     /**
19405      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19406      */
19407     
19408     lastData : false,
19409     
19410     // behavies liek a hiddne field
19411     inputType:      'hidden',
19412     /**
19413      * @cfg {Number} width The width of the box that displays the selected element
19414      */ 
19415     width:          300,
19416
19417     
19418     
19419     /**
19420      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19421      */
19422     name : false,
19423     /**
19424      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19425      */
19426     hiddenName : false,
19427       /**
19428      * @cfg {String} seperator    The value seperator normally ',' 
19429      */
19430     seperator : ',',
19431     
19432     // private the array of items that are displayed..
19433     items  : false,
19434     // private - the hidden field el.
19435     hiddenEl : false,
19436     // private - the filed el..
19437     el : false,
19438     
19439     //validateValue : function() { return true; }, // all values are ok!
19440     //onAddClick: function() { },
19441     
19442     onRender : function(ct, position) 
19443     {
19444         
19445         // create the standard hidden element
19446         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19447         
19448         
19449         // give fake names to child combo;
19450         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19451         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19452         
19453         this.combo = Roo.factory(this.combo, Roo.form);
19454         this.combo.onRender(ct, position);
19455         if (typeof(this.combo.width) != 'undefined') {
19456             this.combo.onResize(this.combo.width,0);
19457         }
19458         
19459         this.combo.initEvents();
19460         
19461         // assigned so form know we need to do this..
19462         this.store          = this.combo.store;
19463         this.valueField     = this.combo.valueField;
19464         this.displayField   = this.combo.displayField ;
19465         
19466         
19467         this.combo.wrap.addClass('x-cbarray-grp');
19468         
19469         var cbwrap = this.combo.wrap.createChild(
19470             {tag: 'div', cls: 'x-cbarray-cb'},
19471             this.combo.el.dom
19472         );
19473         
19474              
19475         this.hiddenEl = this.combo.wrap.createChild({
19476             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19477         });
19478         this.el = this.combo.wrap.createChild({
19479             tag: 'input',  type:'hidden' , name: this.name, value : ''
19480         });
19481          //   this.el.dom.removeAttribute("name");
19482         
19483         
19484         this.outerWrap = this.combo.wrap;
19485         this.wrap = cbwrap;
19486         
19487         this.outerWrap.setWidth(this.width);
19488         this.outerWrap.dom.removeChild(this.el.dom);
19489         
19490         this.wrap.dom.appendChild(this.el.dom);
19491         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19492         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19493         
19494         this.combo.trigger.setStyle('position','relative');
19495         this.combo.trigger.setStyle('left', '0px');
19496         this.combo.trigger.setStyle('top', '2px');
19497         
19498         this.combo.el.setStyle('vertical-align', 'text-bottom');
19499         
19500         //this.trigger.setStyle('vertical-align', 'top');
19501         
19502         // this should use the code from combo really... on('add' ....)
19503         if (this.adder) {
19504             
19505         
19506             this.adder = this.outerWrap.createChild(
19507                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19508             var _t = this;
19509             this.adder.on('click', function(e) {
19510                 _t.fireEvent('adderclick', this, e);
19511             }, _t);
19512         }
19513         //var _t = this;
19514         //this.adder.on('click', this.onAddClick, _t);
19515         
19516         
19517         this.combo.on('select', function(cb, rec, ix) {
19518             this.addItem(rec.data);
19519             
19520             cb.setValue('');
19521             cb.el.dom.value = '';
19522             //cb.lastData = rec.data;
19523             // add to list
19524             
19525         }, this);
19526         
19527         
19528     },
19529     
19530     
19531     getName: function()
19532     {
19533         // returns hidden if it's set..
19534         if (!this.rendered) {return ''};
19535         return  this.hiddenName ? this.hiddenName : this.name;
19536         
19537     },
19538     
19539     
19540     onResize: function(w, h){
19541         
19542         return;
19543         // not sure if this is needed..
19544         //this.combo.onResize(w,h);
19545         
19546         if(typeof w != 'number'){
19547             // we do not handle it!?!?
19548             return;
19549         }
19550         var tw = this.combo.trigger.getWidth();
19551         tw += this.addicon ? this.addicon.getWidth() : 0;
19552         tw += this.editicon ? this.editicon.getWidth() : 0;
19553         var x = w - tw;
19554         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19555             
19556         this.combo.trigger.setStyle('left', '0px');
19557         
19558         if(this.list && this.listWidth === undefined){
19559             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19560             this.list.setWidth(lw);
19561             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19562         }
19563         
19564     
19565         
19566     },
19567     
19568     addItem: function(rec)
19569     {
19570         var valueField = this.combo.valueField;
19571         var displayField = this.combo.displayField;
19572         
19573         if (this.items.indexOfKey(rec[valueField]) > -1) {
19574             //console.log("GOT " + rec.data.id);
19575             return;
19576         }
19577         
19578         var x = new Roo.form.ComboBoxArray.Item({
19579             //id : rec[this.idField],
19580             data : rec,
19581             displayField : displayField ,
19582             tipField : displayField ,
19583             cb : this
19584         });
19585         // use the 
19586         this.items.add(rec[valueField],x);
19587         // add it before the element..
19588         this.updateHiddenEl();
19589         x.render(this.outerWrap, this.wrap.dom);
19590         // add the image handler..
19591     },
19592     
19593     updateHiddenEl : function()
19594     {
19595         this.validate();
19596         if (!this.hiddenEl) {
19597             return;
19598         }
19599         var ar = [];
19600         var idField = this.combo.valueField;
19601         
19602         this.items.each(function(f) {
19603             ar.push(f.data[idField]);
19604         });
19605         this.hiddenEl.dom.value = ar.join(this.seperator);
19606         this.validate();
19607     },
19608     
19609     reset : function()
19610     {
19611         this.items.clear();
19612         
19613         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19614            el.remove();
19615         });
19616         
19617         this.el.dom.value = '';
19618         if (this.hiddenEl) {
19619             this.hiddenEl.dom.value = '';
19620         }
19621         
19622     },
19623     getValue: function()
19624     {
19625         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19626     },
19627     setValue: function(v) // not a valid action - must use addItems..
19628     {
19629         
19630         this.reset();
19631          
19632         if (this.store.isLocal && (typeof(v) == 'string')) {
19633             // then we can use the store to find the values..
19634             // comma seperated at present.. this needs to allow JSON based encoding..
19635             this.hiddenEl.value  = v;
19636             var v_ar = [];
19637             Roo.each(v.split(this.seperator), function(k) {
19638                 Roo.log("CHECK " + this.valueField + ',' + k);
19639                 var li = this.store.query(this.valueField, k);
19640                 if (!li.length) {
19641                     return;
19642                 }
19643                 var add = {};
19644                 add[this.valueField] = k;
19645                 add[this.displayField] = li.item(0).data[this.displayField];
19646                 
19647                 this.addItem(add);
19648             }, this) 
19649              
19650         }
19651         if (typeof(v) == 'object' ) {
19652             // then let's assume it's an array of objects..
19653             Roo.each(v, function(l) {
19654                 var add = l;
19655                 if (typeof(l) == 'string') {
19656                     add = {};
19657                     add[this.valueField] = l;
19658                     add[this.displayField] = l
19659                 }
19660                 this.addItem(add);
19661             }, this);
19662              
19663         }
19664         
19665         
19666     },
19667     setFromData: function(v)
19668     {
19669         // this recieves an object, if setValues is called.
19670         this.reset();
19671         this.el.dom.value = v[this.displayField];
19672         this.hiddenEl.dom.value = v[this.valueField];
19673         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19674             return;
19675         }
19676         var kv = v[this.valueField];
19677         var dv = v[this.displayField];
19678         kv = typeof(kv) != 'string' ? '' : kv;
19679         dv = typeof(dv) != 'string' ? '' : dv;
19680         
19681         
19682         var keys = kv.split(this.seperator);
19683         var display = dv.split(this.seperator);
19684         for (var i = 0 ; i < keys.length; i++) {
19685             add = {};
19686             add[this.valueField] = keys[i];
19687             add[this.displayField] = display[i];
19688             this.addItem(add);
19689         }
19690       
19691         
19692     },
19693     
19694     /**
19695      * Validates the combox array value
19696      * @return {Boolean} True if the value is valid, else false
19697      */
19698     validate : function(){
19699         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19700             this.clearInvalid();
19701             return true;
19702         }
19703         return false;
19704     },
19705     
19706     validateValue : function(value){
19707         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19708         
19709     },
19710     
19711     /*@
19712      * overide
19713      * 
19714      */
19715     isDirty : function() {
19716         if(this.disabled) {
19717             return false;
19718         }
19719         
19720         try {
19721             var d = Roo.decode(String(this.originalValue));
19722         } catch (e) {
19723             return String(this.getValue()) !== String(this.originalValue);
19724         }
19725         
19726         var originalValue = [];
19727         
19728         for (var i = 0; i < d.length; i++){
19729             originalValue.push(d[i][this.valueField]);
19730         }
19731         
19732         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19733         
19734     }
19735     
19736 });
19737
19738
19739
19740 /**
19741  * @class Roo.form.ComboBoxArray.Item
19742  * @extends Roo.BoxComponent
19743  * A selected item in the list
19744  *  Fred [x]  Brian [x]  [Pick another |v]
19745  * 
19746  * @constructor
19747  * Create a new item.
19748  * @param {Object} config Configuration options
19749  */
19750  
19751 Roo.form.ComboBoxArray.Item = function(config) {
19752     config.id = Roo.id();
19753     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19754 }
19755
19756 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19757     data : {},
19758     cb: false,
19759     displayField : false,
19760     tipField : false,
19761     
19762     
19763     defaultAutoCreate : {
19764         tag: 'div',
19765         cls: 'x-cbarray-item',
19766         cn : [ 
19767             { tag: 'div' },
19768             {
19769                 tag: 'img',
19770                 width:16,
19771                 height : 16,
19772                 src : Roo.BLANK_IMAGE_URL ,
19773                 align: 'center'
19774             }
19775         ]
19776         
19777     },
19778     
19779  
19780     onRender : function(ct, position)
19781     {
19782         Roo.form.Field.superclass.onRender.call(this, ct, position);
19783         
19784         if(!this.el){
19785             var cfg = this.getAutoCreate();
19786             this.el = ct.createChild(cfg, position);
19787         }
19788         
19789         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19790         
19791         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19792             this.cb.renderer(this.data) :
19793             String.format('{0}',this.data[this.displayField]);
19794         
19795             
19796         this.el.child('div').dom.setAttribute('qtip',
19797                         String.format('{0}',this.data[this.tipField])
19798         );
19799         
19800         this.el.child('img').on('click', this.remove, this);
19801         
19802     },
19803    
19804     remove : function()
19805     {
19806         if(this.cb.disabled){
19807             return;
19808         }
19809         
19810         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19811             this.cb.items.remove(this);
19812             this.el.child('img').un('click', this.remove, this);
19813             this.el.remove();
19814             this.cb.updateHiddenEl();
19815
19816             this.cb.fireEvent('remove', this.cb, this);
19817         }
19818         
19819     }
19820 });/*
19821  * RooJS Library 1.1.1
19822  * Copyright(c) 2008-2011  Alan Knowles
19823  *
19824  * License - LGPL
19825  */
19826  
19827
19828 /**
19829  * @class Roo.form.ComboNested
19830  * @extends Roo.form.ComboBox
19831  * A combobox for that allows selection of nested items in a list,
19832  * eg.
19833  *
19834  *  Book
19835  *    -> red
19836  *    -> green
19837  *  Table
19838  *    -> square
19839  *      ->red
19840  *      ->green
19841  *    -> rectangle
19842  *      ->green
19843  *      
19844  * 
19845  * @constructor
19846  * Create a new ComboNested
19847  * @param {Object} config Configuration options
19848  */
19849 Roo.form.ComboNested = function(config){
19850     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19851     // should verify some data...
19852     // like
19853     // hiddenName = required..
19854     // displayField = required
19855     // valudField == required
19856     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19857     var _t = this;
19858     Roo.each(req, function(e) {
19859         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19860             throw "Roo.form.ComboNested : missing value for: " + e;
19861         }
19862     });
19863      
19864     
19865 };
19866
19867 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19868    
19869     /*
19870      * @config {Number} max Number of columns to show
19871      */
19872     
19873     maxColumns : 3,
19874    
19875     list : null, // the outermost div..
19876     innerLists : null, // the
19877     views : null,
19878     stores : null,
19879     // private
19880     loadingChildren : false,
19881     
19882     onRender : function(ct, position)
19883     {
19884         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19885         
19886         if(this.hiddenName){
19887             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19888                     'before', true);
19889             this.hiddenField.value =
19890                 this.hiddenValue !== undefined ? this.hiddenValue :
19891                 this.value !== undefined ? this.value : '';
19892
19893             // prevent input submission
19894             this.el.dom.removeAttribute('name');
19895              
19896              
19897         }
19898         
19899         if(Roo.isGecko){
19900             this.el.dom.setAttribute('autocomplete', 'off');
19901         }
19902
19903         var cls = 'x-combo-list';
19904
19905         this.list = new Roo.Layer({
19906             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19907         });
19908
19909         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19910         this.list.setWidth(lw);
19911         this.list.swallowEvent('mousewheel');
19912         this.assetHeight = 0;
19913
19914         if(this.title){
19915             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19916             this.assetHeight += this.header.getHeight();
19917         }
19918         this.innerLists = [];
19919         this.views = [];
19920         this.stores = [];
19921         for (var i =0 ; i < this.maxColumns; i++) {
19922             this.onRenderList( cls, i);
19923         }
19924         
19925         // always needs footer, as we are going to have an 'OK' button.
19926         this.footer = this.list.createChild({cls:cls+'-ft'});
19927         this.pageTb = new Roo.Toolbar(this.footer);  
19928         var _this = this;
19929         this.pageTb.add(  {
19930             
19931             text: 'Done',
19932             handler: function()
19933             {
19934                 _this.collapse();
19935             }
19936         });
19937         
19938         if ( this.allowBlank && !this.disableClear) {
19939             
19940             this.pageTb.add(new Roo.Toolbar.Fill(), {
19941                 cls: 'x-btn-icon x-btn-clear',
19942                 text: '&#160;',
19943                 handler: function()
19944                 {
19945                     _this.collapse();
19946                     _this.clearValue();
19947                     _this.onSelect(false, -1);
19948                 }
19949             });
19950         }
19951         if (this.footer) {
19952             this.assetHeight += this.footer.getHeight();
19953         }
19954         
19955     },
19956     onRenderList : function (  cls, i)
19957     {
19958         
19959         var lw = Math.floor(
19960                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19961         );
19962         
19963         this.list.setWidth(lw); // default to '1'
19964
19965         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19966         //il.on('mouseover', this.onViewOver, this, { list:  i });
19967         //il.on('mousemove', this.onViewMove, this, { list:  i });
19968         il.setWidth(lw);
19969         il.setStyle({ 'overflow-x' : 'hidden'});
19970
19971         if(!this.tpl){
19972             this.tpl = new Roo.Template({
19973                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19974                 isEmpty: function (value, allValues) {
19975                     //Roo.log(value);
19976                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19977                     return dl ? 'has-children' : 'no-children'
19978                 }
19979             });
19980         }
19981         
19982         var store  = this.store;
19983         if (i > 0) {
19984             store  = new Roo.data.SimpleStore({
19985                 //fields : this.store.reader.meta.fields,
19986                 reader : this.store.reader,
19987                 data : [ ]
19988             });
19989         }
19990         this.stores[i]  = store;
19991                   
19992         var view = this.views[i] = new Roo.View(
19993             il,
19994             this.tpl,
19995             {
19996                 singleSelect:true,
19997                 store: store,
19998                 selectedClass: this.selectedClass
19999             }
20000         );
20001         view.getEl().setWidth(lw);
20002         view.getEl().setStyle({
20003             position: i < 1 ? 'relative' : 'absolute',
20004             top: 0,
20005             left: (i * lw ) + 'px',
20006             display : i > 0 ? 'none' : 'block'
20007         });
20008         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20009         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20010         //view.on('click', this.onViewClick, this, { list : i });
20011
20012         store.on('beforeload', this.onBeforeLoad, this);
20013         store.on('load',  this.onLoad, this, { list  : i});
20014         store.on('loadexception', this.onLoadException, this);
20015
20016         // hide the other vies..
20017         
20018         
20019         
20020     },
20021       
20022     restrictHeight : function()
20023     {
20024         var mh = 0;
20025         Roo.each(this.innerLists, function(il,i) {
20026             var el = this.views[i].getEl();
20027             el.dom.style.height = '';
20028             var inner = el.dom;
20029             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20030             // only adjust heights on other ones..
20031             mh = Math.max(h, mh);
20032             if (i < 1) {
20033                 
20034                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20035                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20036                
20037             }
20038             
20039             
20040         }, this);
20041         
20042         this.list.beginUpdate();
20043         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20044         this.list.alignTo(this.el, this.listAlign);
20045         this.list.endUpdate();
20046         
20047     },
20048      
20049     
20050     // -- store handlers..
20051     // private
20052     onBeforeLoad : function()
20053     {
20054         if(!this.hasFocus){
20055             return;
20056         }
20057         this.innerLists[0].update(this.loadingText ?
20058                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20059         this.restrictHeight();
20060         this.selectedIndex = -1;
20061     },
20062     // private
20063     onLoad : function(a,b,c,d)
20064     {
20065         if (!this.loadingChildren) {
20066             // then we are loading the top level. - hide the children
20067             for (var i = 1;i < this.views.length; i++) {
20068                 this.views[i].getEl().setStyle({ display : 'none' });
20069             }
20070             var lw = Math.floor(
20071                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20072             );
20073         
20074              this.list.setWidth(lw); // default to '1'
20075
20076             
20077         }
20078         if(!this.hasFocus){
20079             return;
20080         }
20081         
20082         if(this.store.getCount() > 0) {
20083             this.expand();
20084             this.restrictHeight();   
20085         } else {
20086             this.onEmptyResults();
20087         }
20088         
20089         if (!this.loadingChildren) {
20090             this.selectActive();
20091         }
20092         /*
20093         this.stores[1].loadData([]);
20094         this.stores[2].loadData([]);
20095         this.views
20096         */    
20097     
20098         //this.el.focus();
20099     },
20100     
20101     
20102     // private
20103     onLoadException : function()
20104     {
20105         this.collapse();
20106         Roo.log(this.store.reader.jsonData);
20107         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20108             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20109         }
20110         
20111         
20112     },
20113     // no cleaning of leading spaces on blur here.
20114     cleanLeadingSpace : function(e) { },
20115     
20116
20117     onSelectChange : function (view, sels, opts )
20118     {
20119         var ix = view.getSelectedIndexes();
20120          
20121         if (opts.list > this.maxColumns - 2) {
20122             if (view.store.getCount()<  1) {
20123                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20124
20125             } else  {
20126                 if (ix.length) {
20127                     // used to clear ?? but if we are loading unselected 
20128                     this.setFromData(view.store.getAt(ix[0]).data);
20129                 }
20130                 
20131             }
20132             
20133             return;
20134         }
20135         
20136         if (!ix.length) {
20137             // this get's fired when trigger opens..
20138            // this.setFromData({});
20139             var str = this.stores[opts.list+1];
20140             str.data.clear(); // removeall wihtout the fire events..
20141             return;
20142         }
20143         
20144         var rec = view.store.getAt(ix[0]);
20145          
20146         this.setFromData(rec.data);
20147         this.fireEvent('select', this, rec, ix[0]);
20148         
20149         var lw = Math.floor(
20150              (
20151                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20152              ) / this.maxColumns
20153         );
20154         this.loadingChildren = true;
20155         this.stores[opts.list+1].loadDataFromChildren( rec );
20156         this.loadingChildren = false;
20157         var dl = this.stores[opts.list+1]. getTotalCount();
20158         
20159         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20160         
20161         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20162         for (var i = opts.list+2; i < this.views.length;i++) {
20163             this.views[i].getEl().setStyle({ display : 'none' });
20164         }
20165         
20166         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20167         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20168         
20169         if (this.isLoading) {
20170            // this.selectActive(opts.list);
20171         }
20172          
20173     },
20174     
20175     
20176     
20177     
20178     onDoubleClick : function()
20179     {
20180         this.collapse(); //??
20181     },
20182     
20183      
20184     
20185     
20186     
20187     // private
20188     recordToStack : function(store, prop, value, stack)
20189     {
20190         var cstore = new Roo.data.SimpleStore({
20191             //fields : this.store.reader.meta.fields, // we need array reader.. for
20192             reader : this.store.reader,
20193             data : [ ]
20194         });
20195         var _this = this;
20196         var record  = false;
20197         var srec = false;
20198         if(store.getCount() < 1){
20199             return false;
20200         }
20201         store.each(function(r){
20202             if(r.data[prop] == value){
20203                 record = r;
20204             srec = r;
20205                 return false;
20206             }
20207             if (r.data.cn && r.data.cn.length) {
20208                 cstore.loadDataFromChildren( r);
20209                 var cret = _this.recordToStack(cstore, prop, value, stack);
20210                 if (cret !== false) {
20211                     record = cret;
20212                     srec = r;
20213                     return false;
20214                 }
20215             }
20216              
20217             return true;
20218         });
20219         if (record == false) {
20220             return false
20221         }
20222         stack.unshift(srec);
20223         return record;
20224     },
20225     
20226     /*
20227      * find the stack of stores that match our value.
20228      *
20229      * 
20230      */
20231     
20232     selectActive : function ()
20233     {
20234         // if store is not loaded, then we will need to wait for that to happen first.
20235         var stack = [];
20236         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20237         for (var i = 0; i < stack.length; i++ ) {
20238             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20239         }
20240         
20241     }
20242         
20243          
20244     
20245     
20246     
20247     
20248 });/*
20249  * Based on:
20250  * Ext JS Library 1.1.1
20251  * Copyright(c) 2006-2007, Ext JS, LLC.
20252  *
20253  * Originally Released Under LGPL - original licence link has changed is not relivant.
20254  *
20255  * Fork - LGPL
20256  * <script type="text/javascript">
20257  */
20258 /**
20259  * @class Roo.form.Checkbox
20260  * @extends Roo.form.Field
20261  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20262  * @constructor
20263  * Creates a new Checkbox
20264  * @param {Object} config Configuration options
20265  */
20266 Roo.form.Checkbox = function(config){
20267     Roo.form.Checkbox.superclass.constructor.call(this, config);
20268     this.addEvents({
20269         /**
20270          * @event check
20271          * Fires when the checkbox is checked or unchecked.
20272              * @param {Roo.form.Checkbox} this This checkbox
20273              * @param {Boolean} checked The new checked value
20274              */
20275         check : true
20276     });
20277 };
20278
20279 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20280     /**
20281      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20282      */
20283     focusClass : undefined,
20284     /**
20285      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20286      */
20287     fieldClass: "x-form-field",
20288     /**
20289      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20290      */
20291     checked: false,
20292     /**
20293      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20294      * {tag: "input", type: "checkbox", autocomplete: "off"})
20295      */
20296     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20297     /**
20298      * @cfg {String} boxLabel The text that appears beside the checkbox
20299      */
20300     boxLabel : "",
20301     /**
20302      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20303      */  
20304     inputValue : '1',
20305     /**
20306      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20307      */
20308      valueOff: '0', // value when not checked..
20309
20310     actionMode : 'viewEl', 
20311     //
20312     // private
20313     itemCls : 'x-menu-check-item x-form-item',
20314     groupClass : 'x-menu-group-item',
20315     inputType : 'hidden',
20316     
20317     
20318     inSetChecked: false, // check that we are not calling self...
20319     
20320     inputElement: false, // real input element?
20321     basedOn: false, // ????
20322     
20323     isFormField: true, // not sure where this is needed!!!!
20324
20325     onResize : function(){
20326         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20327         if(!this.boxLabel){
20328             this.el.alignTo(this.wrap, 'c-c');
20329         }
20330     },
20331
20332     initEvents : function(){
20333         Roo.form.Checkbox.superclass.initEvents.call(this);
20334         this.el.on("click", this.onClick,  this);
20335         this.el.on("change", this.onClick,  this);
20336     },
20337
20338
20339     getResizeEl : function(){
20340         return this.wrap;
20341     },
20342
20343     getPositionEl : function(){
20344         return this.wrap;
20345     },
20346
20347     // private
20348     onRender : function(ct, position){
20349         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20350         /*
20351         if(this.inputValue !== undefined){
20352             this.el.dom.value = this.inputValue;
20353         }
20354         */
20355         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20356         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20357         var viewEl = this.wrap.createChild({ 
20358             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20359         this.viewEl = viewEl;   
20360         this.wrap.on('click', this.onClick,  this); 
20361         
20362         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20363         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20364         
20365         
20366         
20367         if(this.boxLabel){
20368             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20369         //    viewEl.on('click', this.onClick,  this); 
20370         }
20371         //if(this.checked){
20372             this.setChecked(this.checked);
20373         //}else{
20374             //this.checked = this.el.dom;
20375         //}
20376
20377     },
20378
20379     // private
20380     initValue : Roo.emptyFn,
20381
20382     /**
20383      * Returns the checked state of the checkbox.
20384      * @return {Boolean} True if checked, else false
20385      */
20386     getValue : function(){
20387         if(this.el){
20388             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20389         }
20390         return this.valueOff;
20391         
20392     },
20393
20394         // private
20395     onClick : function(){ 
20396         if (this.disabled) {
20397             return;
20398         }
20399         this.setChecked(!this.checked);
20400
20401         //if(this.el.dom.checked != this.checked){
20402         //    this.setValue(this.el.dom.checked);
20403        // }
20404     },
20405
20406     /**
20407      * Sets the checked state of the checkbox.
20408      * On is always based on a string comparison between inputValue and the param.
20409      * @param {Boolean/String} value - the value to set 
20410      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20411      */
20412     setValue : function(v,suppressEvent){
20413         
20414         
20415         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20416         //if(this.el && this.el.dom){
20417         //    this.el.dom.checked = this.checked;
20418         //    this.el.dom.defaultChecked = this.checked;
20419         //}
20420         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20421         //this.fireEvent("check", this, this.checked);
20422     },
20423     // private..
20424     setChecked : function(state,suppressEvent)
20425     {
20426         if (this.inSetChecked) {
20427             this.checked = state;
20428             return;
20429         }
20430         
20431     
20432         if(this.wrap){
20433             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20434         }
20435         this.checked = state;
20436         if(suppressEvent !== true){
20437             this.fireEvent('check', this, state);
20438         }
20439         this.inSetChecked = true;
20440         this.el.dom.value = state ? this.inputValue : this.valueOff;
20441         this.inSetChecked = false;
20442         
20443     },
20444     // handle setting of hidden value by some other method!!?!?
20445     setFromHidden: function()
20446     {
20447         if(!this.el){
20448             return;
20449         }
20450         //console.log("SET FROM HIDDEN");
20451         //alert('setFrom hidden');
20452         this.setValue(this.el.dom.value);
20453     },
20454     
20455     onDestroy : function()
20456     {
20457         if(this.viewEl){
20458             Roo.get(this.viewEl).remove();
20459         }
20460          
20461         Roo.form.Checkbox.superclass.onDestroy.call(this);
20462     },
20463     
20464     setBoxLabel : function(str)
20465     {
20466         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20467     }
20468
20469 });/*
20470  * Based on:
20471  * Ext JS Library 1.1.1
20472  * Copyright(c) 2006-2007, Ext JS, LLC.
20473  *
20474  * Originally Released Under LGPL - original licence link has changed is not relivant.
20475  *
20476  * Fork - LGPL
20477  * <script type="text/javascript">
20478  */
20479  
20480 /**
20481  * @class Roo.form.Radio
20482  * @extends Roo.form.Checkbox
20483  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20484  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20485  * @constructor
20486  * Creates a new Radio
20487  * @param {Object} config Configuration options
20488  */
20489 Roo.form.Radio = function(){
20490     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20491 };
20492 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20493     inputType: 'radio',
20494
20495     /**
20496      * If this radio is part of a group, it will return the selected value
20497      * @return {String}
20498      */
20499     getGroupValue : function(){
20500         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20501     },
20502     
20503     
20504     onRender : function(ct, position){
20505         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20506         
20507         if(this.inputValue !== undefined){
20508             this.el.dom.value = this.inputValue;
20509         }
20510          
20511         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20512         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20513         //var viewEl = this.wrap.createChild({ 
20514         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20515         //this.viewEl = viewEl;   
20516         //this.wrap.on('click', this.onClick,  this); 
20517         
20518         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20519         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20520         
20521         
20522         
20523         if(this.boxLabel){
20524             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20525         //    viewEl.on('click', this.onClick,  this); 
20526         }
20527          if(this.checked){
20528             this.el.dom.checked =   'checked' ;
20529         }
20530          
20531     } 
20532     
20533     
20534 });//<script type="text/javascript">
20535
20536 /*
20537  * Based  Ext JS Library 1.1.1
20538  * Copyright(c) 2006-2007, Ext JS, LLC.
20539  * LGPL
20540  *
20541  */
20542  
20543 /**
20544  * @class Roo.HtmlEditorCore
20545  * @extends Roo.Component
20546  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20547  *
20548  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20549  */
20550
20551 Roo.HtmlEditorCore = function(config){
20552     
20553     
20554     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20555     
20556     
20557     this.addEvents({
20558         /**
20559          * @event initialize
20560          * Fires when the editor is fully initialized (including the iframe)
20561          * @param {Roo.HtmlEditorCore} this
20562          */
20563         initialize: true,
20564         /**
20565          * @event activate
20566          * Fires when the editor is first receives the focus. Any insertion must wait
20567          * until after this event.
20568          * @param {Roo.HtmlEditorCore} this
20569          */
20570         activate: true,
20571          /**
20572          * @event beforesync
20573          * Fires before the textarea is updated with content from the editor iframe. Return false
20574          * to cancel the sync.
20575          * @param {Roo.HtmlEditorCore} this
20576          * @param {String} html
20577          */
20578         beforesync: true,
20579          /**
20580          * @event beforepush
20581          * Fires before the iframe editor is updated with content from the textarea. Return false
20582          * to cancel the push.
20583          * @param {Roo.HtmlEditorCore} this
20584          * @param {String} html
20585          */
20586         beforepush: true,
20587          /**
20588          * @event sync
20589          * Fires when the textarea is updated with content from the editor iframe.
20590          * @param {Roo.HtmlEditorCore} this
20591          * @param {String} html
20592          */
20593         sync: true,
20594          /**
20595          * @event push
20596          * Fires when the iframe editor is updated with content from the textarea.
20597          * @param {Roo.HtmlEditorCore} this
20598          * @param {String} html
20599          */
20600         push: true,
20601         
20602         /**
20603          * @event editorevent
20604          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20605          * @param {Roo.HtmlEditorCore} this
20606          */
20607         editorevent: true
20608         
20609     });
20610     
20611     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20612     
20613     // defaults : white / black...
20614     this.applyBlacklists();
20615     
20616     
20617     
20618 };
20619
20620
20621 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20622
20623
20624      /**
20625      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20626      */
20627     
20628     owner : false,
20629     
20630      /**
20631      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20632      *                        Roo.resizable.
20633      */
20634     resizable : false,
20635      /**
20636      * @cfg {Number} height (in pixels)
20637      */   
20638     height: 300,
20639    /**
20640      * @cfg {Number} width (in pixels)
20641      */   
20642     width: 500,
20643     
20644     /**
20645      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20646      * 
20647      */
20648     stylesheets: false,
20649     
20650     /**
20651      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20652      */
20653     allowComments: false,
20654     // id of frame..
20655     frameId: false,
20656     
20657     // private properties
20658     validationEvent : false,
20659     deferHeight: true,
20660     initialized : false,
20661     activated : false,
20662     sourceEditMode : false,
20663     onFocus : Roo.emptyFn,
20664     iframePad:3,
20665     hideMode:'offsets',
20666     
20667     clearUp: true,
20668     
20669     // blacklist + whitelisted elements..
20670     black: false,
20671     white: false,
20672      
20673     bodyCls : '',
20674
20675     /**
20676      * Protected method that will not generally be called directly. It
20677      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20678      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20679      */
20680     getDocMarkup : function(){
20681         // body styles..
20682         var st = '';
20683         
20684         // inherit styels from page...?? 
20685         if (this.stylesheets === false) {
20686             
20687             Roo.get(document.head).select('style').each(function(node) {
20688                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20689             });
20690             
20691             Roo.get(document.head).select('link').each(function(node) { 
20692                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20693             });
20694             
20695         } else if (!this.stylesheets.length) {
20696                 // simple..
20697                 st = '<style type="text/css">' +
20698                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20699                    '</style>';
20700         } else {
20701             for (var i in this.stylesheets) { 
20702                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20703             }
20704             
20705         }
20706         
20707         st +=  '<style type="text/css">' +
20708             'IMG { cursor: pointer } ' +
20709         '</style>';
20710
20711         var cls = 'roo-htmleditor-body';
20712         
20713         if(this.bodyCls.length){
20714             cls += ' ' + this.bodyCls;
20715         }
20716         
20717         return '<html><head>' + st  +
20718             //<style type="text/css">' +
20719             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20720             //'</style>' +
20721             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20722     },
20723
20724     // private
20725     onRender : function(ct, position)
20726     {
20727         var _t = this;
20728         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20729         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20730         
20731         
20732         this.el.dom.style.border = '0 none';
20733         this.el.dom.setAttribute('tabIndex', -1);
20734         this.el.addClass('x-hidden hide');
20735         
20736         
20737         
20738         if(Roo.isIE){ // fix IE 1px bogus margin
20739             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20740         }
20741        
20742         
20743         this.frameId = Roo.id();
20744         
20745          
20746         
20747         var iframe = this.owner.wrap.createChild({
20748             tag: 'iframe',
20749             cls: 'form-control', // bootstrap..
20750             id: this.frameId,
20751             name: this.frameId,
20752             frameBorder : 'no',
20753             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20754         }, this.el
20755         );
20756         
20757         
20758         this.iframe = iframe.dom;
20759
20760          this.assignDocWin();
20761         
20762         this.doc.designMode = 'on';
20763        
20764         this.doc.open();
20765         this.doc.write(this.getDocMarkup());
20766         this.doc.close();
20767
20768         
20769         var task = { // must defer to wait for browser to be ready
20770             run : function(){
20771                 //console.log("run task?" + this.doc.readyState);
20772                 this.assignDocWin();
20773                 if(this.doc.body || this.doc.readyState == 'complete'){
20774                     try {
20775                         this.doc.designMode="on";
20776                     } catch (e) {
20777                         return;
20778                     }
20779                     Roo.TaskMgr.stop(task);
20780                     this.initEditor.defer(10, this);
20781                 }
20782             },
20783             interval : 10,
20784             duration: 10000,
20785             scope: this
20786         };
20787         Roo.TaskMgr.start(task);
20788
20789     },
20790
20791     // private
20792     onResize : function(w, h)
20793     {
20794          Roo.log('resize: ' +w + ',' + h );
20795         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20796         if(!this.iframe){
20797             return;
20798         }
20799         if(typeof w == 'number'){
20800             
20801             this.iframe.style.width = w + 'px';
20802         }
20803         if(typeof h == 'number'){
20804             
20805             this.iframe.style.height = h + 'px';
20806             if(this.doc){
20807                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20808             }
20809         }
20810         
20811     },
20812
20813     /**
20814      * Toggles the editor between standard and source edit mode.
20815      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20816      */
20817     toggleSourceEdit : function(sourceEditMode){
20818         
20819         this.sourceEditMode = sourceEditMode === true;
20820         
20821         if(this.sourceEditMode){
20822  
20823             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20824             
20825         }else{
20826             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20827             //this.iframe.className = '';
20828             this.deferFocus();
20829         }
20830         //this.setSize(this.owner.wrap.getSize());
20831         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20832     },
20833
20834     
20835   
20836
20837     /**
20838      * Protected method that will not generally be called directly. If you need/want
20839      * custom HTML cleanup, this is the method you should override.
20840      * @param {String} html The HTML to be cleaned
20841      * return {String} The cleaned HTML
20842      */
20843     cleanHtml : function(html){
20844         html = String(html);
20845         if(html.length > 5){
20846             if(Roo.isSafari){ // strip safari nonsense
20847                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20848             }
20849         }
20850         if(html == '&nbsp;'){
20851             html = '';
20852         }
20853         return html;
20854     },
20855
20856     /**
20857      * HTML Editor -> Textarea
20858      * Protected method that will not generally be called directly. Syncs the contents
20859      * of the editor iframe with the textarea.
20860      */
20861     syncValue : function(){
20862         if(this.initialized){
20863             var bd = (this.doc.body || this.doc.documentElement);
20864             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20865             var html = bd.innerHTML;
20866             if(Roo.isSafari){
20867                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20868                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20869                 if(m && m[1]){
20870                     html = '<div style="'+m[0]+'">' + html + '</div>';
20871                 }
20872             }
20873             html = this.cleanHtml(html);
20874             // fix up the special chars.. normaly like back quotes in word...
20875             // however we do not want to do this with chinese..
20876             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20877                 
20878                 var cc = match.charCodeAt();
20879
20880                 // Get the character value, handling surrogate pairs
20881                 if (match.length == 2) {
20882                     // It's a surrogate pair, calculate the Unicode code point
20883                     var high = match.charCodeAt(0) - 0xD800;
20884                     var low  = match.charCodeAt(1) - 0xDC00;
20885                     cc = (high * 0x400) + low + 0x10000;
20886                 }  else if (
20887                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20888                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20889                     (cc >= 0xf900 && cc < 0xfb00 )
20890                 ) {
20891                         return match;
20892                 }  
20893          
20894                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20895                 return "&#" + cc + ";";
20896                 
20897                 
20898             });
20899             
20900             
20901              
20902             if(this.owner.fireEvent('beforesync', this, html) !== false){
20903                 this.el.dom.value = html;
20904                 this.owner.fireEvent('sync', this, html);
20905             }
20906         }
20907     },
20908
20909     /**
20910      * Protected method that will not generally be called directly. Pushes the value of the textarea
20911      * into the iframe editor.
20912      */
20913     pushValue : function(){
20914         if(this.initialized){
20915             var v = this.el.dom.value.trim();
20916             
20917 //            if(v.length < 1){
20918 //                v = '&#160;';
20919 //            }
20920             
20921             if(this.owner.fireEvent('beforepush', this, v) !== false){
20922                 var d = (this.doc.body || this.doc.documentElement);
20923                 d.innerHTML = v;
20924                 this.cleanUpPaste();
20925                 this.el.dom.value = d.innerHTML;
20926                 this.owner.fireEvent('push', this, v);
20927             }
20928         }
20929     },
20930
20931     // private
20932     deferFocus : function(){
20933         this.focus.defer(10, this);
20934     },
20935
20936     // doc'ed in Field
20937     focus : function(){
20938         if(this.win && !this.sourceEditMode){
20939             this.win.focus();
20940         }else{
20941             this.el.focus();
20942         }
20943     },
20944     
20945     assignDocWin: function()
20946     {
20947         var iframe = this.iframe;
20948         
20949          if(Roo.isIE){
20950             this.doc = iframe.contentWindow.document;
20951             this.win = iframe.contentWindow;
20952         } else {
20953 //            if (!Roo.get(this.frameId)) {
20954 //                return;
20955 //            }
20956 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20957 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20958             
20959             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20960                 return;
20961             }
20962             
20963             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20964             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20965         }
20966     },
20967     
20968     // private
20969     initEditor : function(){
20970         //console.log("INIT EDITOR");
20971         this.assignDocWin();
20972         
20973         
20974         
20975         this.doc.designMode="on";
20976         this.doc.open();
20977         this.doc.write(this.getDocMarkup());
20978         this.doc.close();
20979         
20980         var dbody = (this.doc.body || this.doc.documentElement);
20981         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20982         // this copies styles from the containing element into thsi one..
20983         // not sure why we need all of this..
20984         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20985         
20986         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20987         //ss['background-attachment'] = 'fixed'; // w3c
20988         dbody.bgProperties = 'fixed'; // ie
20989         //Roo.DomHelper.applyStyles(dbody, ss);
20990         Roo.EventManager.on(this.doc, {
20991             //'mousedown': this.onEditorEvent,
20992             'mouseup': this.onEditorEvent,
20993             'dblclick': this.onEditorEvent,
20994             'click': this.onEditorEvent,
20995             'keyup': this.onEditorEvent,
20996             buffer:100,
20997             scope: this
20998         });
20999         if(Roo.isGecko){
21000             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21001         }
21002         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21003             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21004         }
21005         this.initialized = true;
21006
21007         this.owner.fireEvent('initialize', this);
21008         this.pushValue();
21009     },
21010
21011     // private
21012     onDestroy : function(){
21013         
21014         
21015         
21016         if(this.rendered){
21017             
21018             //for (var i =0; i < this.toolbars.length;i++) {
21019             //    // fixme - ask toolbars for heights?
21020             //    this.toolbars[i].onDestroy();
21021            // }
21022             
21023             //this.wrap.dom.innerHTML = '';
21024             //this.wrap.remove();
21025         }
21026     },
21027
21028     // private
21029     onFirstFocus : function(){
21030         
21031         this.assignDocWin();
21032         
21033         
21034         this.activated = true;
21035          
21036     
21037         if(Roo.isGecko){ // prevent silly gecko errors
21038             this.win.focus();
21039             var s = this.win.getSelection();
21040             if(!s.focusNode || s.focusNode.nodeType != 3){
21041                 var r = s.getRangeAt(0);
21042                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21043                 r.collapse(true);
21044                 this.deferFocus();
21045             }
21046             try{
21047                 this.execCmd('useCSS', true);
21048                 this.execCmd('styleWithCSS', false);
21049             }catch(e){}
21050         }
21051         this.owner.fireEvent('activate', this);
21052     },
21053
21054     // private
21055     adjustFont: function(btn){
21056         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21057         //if(Roo.isSafari){ // safari
21058         //    adjust *= 2;
21059        // }
21060         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21061         if(Roo.isSafari){ // safari
21062             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21063             v =  (v < 10) ? 10 : v;
21064             v =  (v > 48) ? 48 : v;
21065             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21066             
21067         }
21068         
21069         
21070         v = Math.max(1, v+adjust);
21071         
21072         this.execCmd('FontSize', v  );
21073     },
21074
21075     onEditorEvent : function(e)
21076     {
21077         this.owner.fireEvent('editorevent', this, e);
21078       //  this.updateToolbar();
21079         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21080     },
21081
21082     insertTag : function(tg)
21083     {
21084         // could be a bit smarter... -> wrap the current selected tRoo..
21085         if (tg.toLowerCase() == 'span' ||
21086             tg.toLowerCase() == 'code' ||
21087             tg.toLowerCase() == 'sup' ||
21088             tg.toLowerCase() == 'sub' 
21089             ) {
21090             
21091             range = this.createRange(this.getSelection());
21092             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21093             wrappingNode.appendChild(range.extractContents());
21094             range.insertNode(wrappingNode);
21095
21096             return;
21097             
21098             
21099             
21100         }
21101         this.execCmd("formatblock",   tg);
21102         
21103     },
21104     
21105     insertText : function(txt)
21106     {
21107         
21108         
21109         var range = this.createRange();
21110         range.deleteContents();
21111                //alert(Sender.getAttribute('label'));
21112                
21113         range.insertNode(this.doc.createTextNode(txt));
21114     } ,
21115     
21116      
21117
21118     /**
21119      * Executes a Midas editor command on the editor document and performs necessary focus and
21120      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21121      * @param {String} cmd The Midas command
21122      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21123      */
21124     relayCmd : function(cmd, value){
21125         this.win.focus();
21126         this.execCmd(cmd, value);
21127         this.owner.fireEvent('editorevent', this);
21128         //this.updateToolbar();
21129         this.owner.deferFocus();
21130     },
21131
21132     /**
21133      * Executes a Midas editor command directly on the editor document.
21134      * For visual commands, you should use {@link #relayCmd} instead.
21135      * <b>This should only be called after the editor is initialized.</b>
21136      * @param {String} cmd The Midas command
21137      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21138      */
21139     execCmd : function(cmd, value){
21140         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21141         this.syncValue();
21142     },
21143  
21144  
21145    
21146     /**
21147      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21148      * to insert tRoo.
21149      * @param {String} text | dom node.. 
21150      */
21151     insertAtCursor : function(text)
21152     {
21153         
21154         if(!this.activated){
21155             return;
21156         }
21157         /*
21158         if(Roo.isIE){
21159             this.win.focus();
21160             var r = this.doc.selection.createRange();
21161             if(r){
21162                 r.collapse(true);
21163                 r.pasteHTML(text);
21164                 this.syncValue();
21165                 this.deferFocus();
21166             
21167             }
21168             return;
21169         }
21170         */
21171         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21172             this.win.focus();
21173             
21174             
21175             // from jquery ui (MIT licenced)
21176             var range, node;
21177             var win = this.win;
21178             
21179             if (win.getSelection && win.getSelection().getRangeAt) {
21180                 range = win.getSelection().getRangeAt(0);
21181                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21182                 range.insertNode(node);
21183             } else if (win.document.selection && win.document.selection.createRange) {
21184                 // no firefox support
21185                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21186                 win.document.selection.createRange().pasteHTML(txt);
21187             } else {
21188                 // no firefox support
21189                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21190                 this.execCmd('InsertHTML', txt);
21191             } 
21192             
21193             this.syncValue();
21194             
21195             this.deferFocus();
21196         }
21197     },
21198  // private
21199     mozKeyPress : function(e){
21200         if(e.ctrlKey){
21201             var c = e.getCharCode(), cmd;
21202           
21203             if(c > 0){
21204                 c = String.fromCharCode(c).toLowerCase();
21205                 switch(c){
21206                     case 'b':
21207                         cmd = 'bold';
21208                         break;
21209                     case 'i':
21210                         cmd = 'italic';
21211                         break;
21212                     
21213                     case 'u':
21214                         cmd = 'underline';
21215                         break;
21216                     
21217                     case 'v':
21218                         this.cleanUpPaste.defer(100, this);
21219                         return;
21220                         
21221                 }
21222                 if(cmd){
21223                     this.win.focus();
21224                     this.execCmd(cmd);
21225                     this.deferFocus();
21226                     e.preventDefault();
21227                 }
21228                 
21229             }
21230         }
21231     },
21232
21233     // private
21234     fixKeys : function(){ // load time branching for fastest keydown performance
21235         if(Roo.isIE){
21236             return function(e){
21237                 var k = e.getKey(), r;
21238                 if(k == e.TAB){
21239                     e.stopEvent();
21240                     r = this.doc.selection.createRange();
21241                     if(r){
21242                         r.collapse(true);
21243                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21244                         this.deferFocus();
21245                     }
21246                     return;
21247                 }
21248                 
21249                 if(k == e.ENTER){
21250                     r = this.doc.selection.createRange();
21251                     if(r){
21252                         var target = r.parentElement();
21253                         if(!target || target.tagName.toLowerCase() != 'li'){
21254                             e.stopEvent();
21255                             r.pasteHTML('<br />');
21256                             r.collapse(false);
21257                             r.select();
21258                         }
21259                     }
21260                 }
21261                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21262                     this.cleanUpPaste.defer(100, this);
21263                     return;
21264                 }
21265                 
21266                 
21267             };
21268         }else if(Roo.isOpera){
21269             return function(e){
21270                 var k = e.getKey();
21271                 if(k == e.TAB){
21272                     e.stopEvent();
21273                     this.win.focus();
21274                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21275                     this.deferFocus();
21276                 }
21277                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21278                     this.cleanUpPaste.defer(100, this);
21279                     return;
21280                 }
21281                 
21282             };
21283         }else if(Roo.isSafari){
21284             return function(e){
21285                 var k = e.getKey();
21286                 
21287                 if(k == e.TAB){
21288                     e.stopEvent();
21289                     this.execCmd('InsertText','\t');
21290                     this.deferFocus();
21291                     return;
21292                 }
21293                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21294                     this.cleanUpPaste.defer(100, this);
21295                     return;
21296                 }
21297                 
21298              };
21299         }
21300     }(),
21301     
21302     getAllAncestors: function()
21303     {
21304         var p = this.getSelectedNode();
21305         var a = [];
21306         if (!p) {
21307             a.push(p); // push blank onto stack..
21308             p = this.getParentElement();
21309         }
21310         
21311         
21312         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21313             a.push(p);
21314             p = p.parentNode;
21315         }
21316         a.push(this.doc.body);
21317         return a;
21318     },
21319     lastSel : false,
21320     lastSelNode : false,
21321     
21322     
21323     getSelection : function() 
21324     {
21325         this.assignDocWin();
21326         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21327     },
21328     
21329     getSelectedNode: function() 
21330     {
21331         // this may only work on Gecko!!!
21332         
21333         // should we cache this!!!!
21334         
21335         
21336         
21337          
21338         var range = this.createRange(this.getSelection()).cloneRange();
21339         
21340         if (Roo.isIE) {
21341             var parent = range.parentElement();
21342             while (true) {
21343                 var testRange = range.duplicate();
21344                 testRange.moveToElementText(parent);
21345                 if (testRange.inRange(range)) {
21346                     break;
21347                 }
21348                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21349                     break;
21350                 }
21351                 parent = parent.parentElement;
21352             }
21353             return parent;
21354         }
21355         
21356         // is ancestor a text element.
21357         var ac =  range.commonAncestorContainer;
21358         if (ac.nodeType == 3) {
21359             ac = ac.parentNode;
21360         }
21361         
21362         var ar = ac.childNodes;
21363          
21364         var nodes = [];
21365         var other_nodes = [];
21366         var has_other_nodes = false;
21367         for (var i=0;i<ar.length;i++) {
21368             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21369                 continue;
21370             }
21371             // fullly contained node.
21372             
21373             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21374                 nodes.push(ar[i]);
21375                 continue;
21376             }
21377             
21378             // probably selected..
21379             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21380                 other_nodes.push(ar[i]);
21381                 continue;
21382             }
21383             // outer..
21384             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21385                 continue;
21386             }
21387             
21388             
21389             has_other_nodes = true;
21390         }
21391         if (!nodes.length && other_nodes.length) {
21392             nodes= other_nodes;
21393         }
21394         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21395             return false;
21396         }
21397         
21398         return nodes[0];
21399     },
21400     createRange: function(sel)
21401     {
21402         // this has strange effects when using with 
21403         // top toolbar - not sure if it's a great idea.
21404         //this.editor.contentWindow.focus();
21405         if (typeof sel != "undefined") {
21406             try {
21407                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21408             } catch(e) {
21409                 return this.doc.createRange();
21410             }
21411         } else {
21412             return this.doc.createRange();
21413         }
21414     },
21415     getParentElement: function()
21416     {
21417         
21418         this.assignDocWin();
21419         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21420         
21421         var range = this.createRange(sel);
21422          
21423         try {
21424             var p = range.commonAncestorContainer;
21425             while (p.nodeType == 3) { // text node
21426                 p = p.parentNode;
21427             }
21428             return p;
21429         } catch (e) {
21430             return null;
21431         }
21432     
21433     },
21434     /***
21435      *
21436      * Range intersection.. the hard stuff...
21437      *  '-1' = before
21438      *  '0' = hits..
21439      *  '1' = after.
21440      *         [ -- selected range --- ]
21441      *   [fail]                        [fail]
21442      *
21443      *    basically..
21444      *      if end is before start or  hits it. fail.
21445      *      if start is after end or hits it fail.
21446      *
21447      *   if either hits (but other is outside. - then it's not 
21448      *   
21449      *    
21450      **/
21451     
21452     
21453     // @see http://www.thismuchiknow.co.uk/?p=64.
21454     rangeIntersectsNode : function(range, node)
21455     {
21456         var nodeRange = node.ownerDocument.createRange();
21457         try {
21458             nodeRange.selectNode(node);
21459         } catch (e) {
21460             nodeRange.selectNodeContents(node);
21461         }
21462     
21463         var rangeStartRange = range.cloneRange();
21464         rangeStartRange.collapse(true);
21465     
21466         var rangeEndRange = range.cloneRange();
21467         rangeEndRange.collapse(false);
21468     
21469         var nodeStartRange = nodeRange.cloneRange();
21470         nodeStartRange.collapse(true);
21471     
21472         var nodeEndRange = nodeRange.cloneRange();
21473         nodeEndRange.collapse(false);
21474     
21475         return rangeStartRange.compareBoundaryPoints(
21476                  Range.START_TO_START, nodeEndRange) == -1 &&
21477                rangeEndRange.compareBoundaryPoints(
21478                  Range.START_TO_START, nodeStartRange) == 1;
21479         
21480          
21481     },
21482     rangeCompareNode : function(range, node)
21483     {
21484         var nodeRange = node.ownerDocument.createRange();
21485         try {
21486             nodeRange.selectNode(node);
21487         } catch (e) {
21488             nodeRange.selectNodeContents(node);
21489         }
21490         
21491         
21492         range.collapse(true);
21493     
21494         nodeRange.collapse(true);
21495      
21496         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21497         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21498          
21499         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21500         
21501         var nodeIsBefore   =  ss == 1;
21502         var nodeIsAfter    = ee == -1;
21503         
21504         if (nodeIsBefore && nodeIsAfter) {
21505             return 0; // outer
21506         }
21507         if (!nodeIsBefore && nodeIsAfter) {
21508             return 1; //right trailed.
21509         }
21510         
21511         if (nodeIsBefore && !nodeIsAfter) {
21512             return 2;  // left trailed.
21513         }
21514         // fully contined.
21515         return 3;
21516     },
21517
21518     // private? - in a new class?
21519     cleanUpPaste :  function()
21520     {
21521         // cleans up the whole document..
21522         Roo.log('cleanuppaste');
21523         
21524         this.cleanUpChildren(this.doc.body);
21525         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21526         if (clean != this.doc.body.innerHTML) {
21527             this.doc.body.innerHTML = clean;
21528         }
21529         
21530     },
21531     
21532     cleanWordChars : function(input) {// change the chars to hex code
21533         var he = Roo.HtmlEditorCore;
21534         
21535         var output = input;
21536         Roo.each(he.swapCodes, function(sw) { 
21537             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21538             
21539             output = output.replace(swapper, sw[1]);
21540         });
21541         
21542         return output;
21543     },
21544     
21545     
21546     cleanUpChildren : function (n)
21547     {
21548         if (!n.childNodes.length) {
21549             return;
21550         }
21551         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21552            this.cleanUpChild(n.childNodes[i]);
21553         }
21554     },
21555     
21556     
21557         
21558     
21559     cleanUpChild : function (node)
21560     {
21561         var ed = this;
21562         //console.log(node);
21563         if (node.nodeName == "#text") {
21564             // clean up silly Windows -- stuff?
21565             return; 
21566         }
21567         if (node.nodeName == "#comment") {
21568             if (!this.allowComments) {
21569                 node.parentNode.removeChild(node);
21570             }
21571             // clean up silly Windows -- stuff?
21572             return; 
21573         }
21574         var lcname = node.tagName.toLowerCase();
21575         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21576         // whitelist of tags..
21577         
21578         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21579             // remove node.
21580             node.parentNode.removeChild(node);
21581             return;
21582             
21583         }
21584         
21585         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21586         
21587         // spans with no attributes - just remove them..
21588         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21589             remove_keep_children = true;
21590         }
21591         
21592         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21593         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21594         
21595         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21596         //    remove_keep_children = true;
21597         //}
21598         
21599         if (remove_keep_children) {
21600             this.cleanUpChildren(node);
21601             // inserts everything just before this node...
21602             while (node.childNodes.length) {
21603                 var cn = node.childNodes[0];
21604                 node.removeChild(cn);
21605                 node.parentNode.insertBefore(cn, node);
21606             }
21607             node.parentNode.removeChild(node);
21608             return;
21609         }
21610         
21611         if (!node.attributes || !node.attributes.length) {
21612             
21613           
21614             
21615             
21616             this.cleanUpChildren(node);
21617             return;
21618         }
21619         
21620         function cleanAttr(n,v)
21621         {
21622             
21623             if (v.match(/^\./) || v.match(/^\//)) {
21624                 return;
21625             }
21626             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21627                 return;
21628             }
21629             if (v.match(/^#/)) {
21630                 return;
21631             }
21632             if (v.match(/^\{/)) { // allow template editing.
21633                 return;
21634             }
21635 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21636             node.removeAttribute(n);
21637             
21638         }
21639         
21640         var cwhite = this.cwhite;
21641         var cblack = this.cblack;
21642             
21643         function cleanStyle(n,v)
21644         {
21645             if (v.match(/expression/)) { //XSS?? should we even bother..
21646                 node.removeAttribute(n);
21647                 return;
21648             }
21649             
21650             var parts = v.split(/;/);
21651             var clean = [];
21652             
21653             Roo.each(parts, function(p) {
21654                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21655                 if (!p.length) {
21656                     return true;
21657                 }
21658                 var l = p.split(':').shift().replace(/\s+/g,'');
21659                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21660                 
21661                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21662 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21663                     //node.removeAttribute(n);
21664                     return true;
21665                 }
21666                 //Roo.log()
21667                 // only allow 'c whitelisted system attributes'
21668                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21669 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21670                     //node.removeAttribute(n);
21671                     return true;
21672                 }
21673                 
21674                 
21675                  
21676                 
21677                 clean.push(p);
21678                 return true;
21679             });
21680             if (clean.length) { 
21681                 node.setAttribute(n, clean.join(';'));
21682             } else {
21683                 node.removeAttribute(n);
21684             }
21685             
21686         }
21687         
21688         
21689         for (var i = node.attributes.length-1; i > -1 ; i--) {
21690             var a = node.attributes[i];
21691             //console.log(a);
21692             
21693             if (a.name.toLowerCase().substr(0,2)=='on')  {
21694                 node.removeAttribute(a.name);
21695                 continue;
21696             }
21697             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21698                 node.removeAttribute(a.name);
21699                 continue;
21700             }
21701             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21702                 cleanAttr(a.name,a.value); // fixme..
21703                 continue;
21704             }
21705             if (a.name == 'style') {
21706                 cleanStyle(a.name,a.value);
21707                 continue;
21708             }
21709             /// clean up MS crap..
21710             // tecnically this should be a list of valid class'es..
21711             
21712             
21713             if (a.name == 'class') {
21714                 if (a.value.match(/^Mso/)) {
21715                     node.removeAttribute('class');
21716                 }
21717                 
21718                 if (a.value.match(/^body$/)) {
21719                     node.removeAttribute('class');
21720                 }
21721                 continue;
21722             }
21723             
21724             // style cleanup!?
21725             // class cleanup?
21726             
21727         }
21728         
21729         
21730         this.cleanUpChildren(node);
21731         
21732         
21733     },
21734     
21735     /**
21736      * Clean up MS wordisms...
21737      */
21738     cleanWord : function(node)
21739     {
21740         if (!node) {
21741             this.cleanWord(this.doc.body);
21742             return;
21743         }
21744         
21745         if(
21746                 node.nodeName == 'SPAN' &&
21747                 !node.hasAttributes() &&
21748                 node.childNodes.length == 1 &&
21749                 node.firstChild.nodeName == "#text"  
21750         ) {
21751             var textNode = node.firstChild;
21752             node.removeChild(textNode);
21753             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21754                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21755             }
21756             node.parentNode.insertBefore(textNode, node);
21757             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21758                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21759             }
21760             node.parentNode.removeChild(node);
21761         }
21762         
21763         if (node.nodeName == "#text") {
21764             // clean up silly Windows -- stuff?
21765             return; 
21766         }
21767         if (node.nodeName == "#comment") {
21768             node.parentNode.removeChild(node);
21769             // clean up silly Windows -- stuff?
21770             return; 
21771         }
21772         
21773         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21774             node.parentNode.removeChild(node);
21775             return;
21776         }
21777         //Roo.log(node.tagName);
21778         // remove - but keep children..
21779         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21780             //Roo.log('-- removed');
21781             while (node.childNodes.length) {
21782                 var cn = node.childNodes[0];
21783                 node.removeChild(cn);
21784                 node.parentNode.insertBefore(cn, node);
21785                 // move node to parent - and clean it..
21786                 this.cleanWord(cn);
21787             }
21788             node.parentNode.removeChild(node);
21789             /// no need to iterate chidlren = it's got none..
21790             //this.iterateChildren(node, this.cleanWord);
21791             return;
21792         }
21793         // clean styles
21794         if (node.className.length) {
21795             
21796             var cn = node.className.split(/\W+/);
21797             var cna = [];
21798             Roo.each(cn, function(cls) {
21799                 if (cls.match(/Mso[a-zA-Z]+/)) {
21800                     return;
21801                 }
21802                 cna.push(cls);
21803             });
21804             node.className = cna.length ? cna.join(' ') : '';
21805             if (!cna.length) {
21806                 node.removeAttribute("class");
21807             }
21808         }
21809         
21810         if (node.hasAttribute("lang")) {
21811             node.removeAttribute("lang");
21812         }
21813         
21814         if (node.hasAttribute("style")) {
21815             
21816             var styles = node.getAttribute("style").split(";");
21817             var nstyle = [];
21818             Roo.each(styles, function(s) {
21819                 if (!s.match(/:/)) {
21820                     return;
21821                 }
21822                 var kv = s.split(":");
21823                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21824                     return;
21825                 }
21826                 // what ever is left... we allow.
21827                 nstyle.push(s);
21828             });
21829             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21830             if (!nstyle.length) {
21831                 node.removeAttribute('style');
21832             }
21833         }
21834         this.iterateChildren(node, this.cleanWord);
21835         
21836         
21837         
21838     },
21839     /**
21840      * iterateChildren of a Node, calling fn each time, using this as the scole..
21841      * @param {DomNode} node node to iterate children of.
21842      * @param {Function} fn method of this class to call on each item.
21843      */
21844     iterateChildren : function(node, fn)
21845     {
21846         if (!node.childNodes.length) {
21847                 return;
21848         }
21849         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21850            fn.call(this, node.childNodes[i])
21851         }
21852     },
21853     
21854     
21855     /**
21856      * cleanTableWidths.
21857      *
21858      * Quite often pasting from word etc.. results in tables with column and widths.
21859      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21860      *
21861      */
21862     cleanTableWidths : function(node)
21863     {
21864          
21865          
21866         if (!node) {
21867             this.cleanTableWidths(this.doc.body);
21868             return;
21869         }
21870         
21871         // ignore list...
21872         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21873             return; 
21874         }
21875         Roo.log(node.tagName);
21876         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21877             this.iterateChildren(node, this.cleanTableWidths);
21878             return;
21879         }
21880         if (node.hasAttribute('width')) {
21881             node.removeAttribute('width');
21882         }
21883         
21884          
21885         if (node.hasAttribute("style")) {
21886             // pretty basic...
21887             
21888             var styles = node.getAttribute("style").split(";");
21889             var nstyle = [];
21890             Roo.each(styles, function(s) {
21891                 if (!s.match(/:/)) {
21892                     return;
21893                 }
21894                 var kv = s.split(":");
21895                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21896                     return;
21897                 }
21898                 // what ever is left... we allow.
21899                 nstyle.push(s);
21900             });
21901             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21902             if (!nstyle.length) {
21903                 node.removeAttribute('style');
21904             }
21905         }
21906         
21907         this.iterateChildren(node, this.cleanTableWidths);
21908         
21909         
21910     },
21911     
21912     
21913     
21914     
21915     domToHTML : function(currentElement, depth, nopadtext) {
21916         
21917         depth = depth || 0;
21918         nopadtext = nopadtext || false;
21919     
21920         if (!currentElement) {
21921             return this.domToHTML(this.doc.body);
21922         }
21923         
21924         //Roo.log(currentElement);
21925         var j;
21926         var allText = false;
21927         var nodeName = currentElement.nodeName;
21928         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21929         
21930         if  (nodeName == '#text') {
21931             
21932             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21933         }
21934         
21935         
21936         var ret = '';
21937         if (nodeName != 'BODY') {
21938              
21939             var i = 0;
21940             // Prints the node tagName, such as <A>, <IMG>, etc
21941             if (tagName) {
21942                 var attr = [];
21943                 for(i = 0; i < currentElement.attributes.length;i++) {
21944                     // quoting?
21945                     var aname = currentElement.attributes.item(i).name;
21946                     if (!currentElement.attributes.item(i).value.length) {
21947                         continue;
21948                     }
21949                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21950                 }
21951                 
21952                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21953             } 
21954             else {
21955                 
21956                 // eack
21957             }
21958         } else {
21959             tagName = false;
21960         }
21961         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21962             return ret;
21963         }
21964         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21965             nopadtext = true;
21966         }
21967         
21968         
21969         // Traverse the tree
21970         i = 0;
21971         var currentElementChild = currentElement.childNodes.item(i);
21972         var allText = true;
21973         var innerHTML  = '';
21974         lastnode = '';
21975         while (currentElementChild) {
21976             // Formatting code (indent the tree so it looks nice on the screen)
21977             var nopad = nopadtext;
21978             if (lastnode == 'SPAN') {
21979                 nopad  = true;
21980             }
21981             // text
21982             if  (currentElementChild.nodeName == '#text') {
21983                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21984                 toadd = nopadtext ? toadd : toadd.trim();
21985                 if (!nopad && toadd.length > 80) {
21986                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21987                 }
21988                 innerHTML  += toadd;
21989                 
21990                 i++;
21991                 currentElementChild = currentElement.childNodes.item(i);
21992                 lastNode = '';
21993                 continue;
21994             }
21995             allText = false;
21996             
21997             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21998                 
21999             // Recursively traverse the tree structure of the child node
22000             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22001             lastnode = currentElementChild.nodeName;
22002             i++;
22003             currentElementChild=currentElement.childNodes.item(i);
22004         }
22005         
22006         ret += innerHTML;
22007         
22008         if (!allText) {
22009                 // The remaining code is mostly for formatting the tree
22010             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22011         }
22012         
22013         
22014         if (tagName) {
22015             ret+= "</"+tagName+">";
22016         }
22017         return ret;
22018         
22019     },
22020         
22021     applyBlacklists : function()
22022     {
22023         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22024         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22025         
22026         this.white = [];
22027         this.black = [];
22028         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22029             if (b.indexOf(tag) > -1) {
22030                 return;
22031             }
22032             this.white.push(tag);
22033             
22034         }, this);
22035         
22036         Roo.each(w, function(tag) {
22037             if (b.indexOf(tag) > -1) {
22038                 return;
22039             }
22040             if (this.white.indexOf(tag) > -1) {
22041                 return;
22042             }
22043             this.white.push(tag);
22044             
22045         }, this);
22046         
22047         
22048         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22049             if (w.indexOf(tag) > -1) {
22050                 return;
22051             }
22052             this.black.push(tag);
22053             
22054         }, this);
22055         
22056         Roo.each(b, function(tag) {
22057             if (w.indexOf(tag) > -1) {
22058                 return;
22059             }
22060             if (this.black.indexOf(tag) > -1) {
22061                 return;
22062             }
22063             this.black.push(tag);
22064             
22065         }, this);
22066         
22067         
22068         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22069         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22070         
22071         this.cwhite = [];
22072         this.cblack = [];
22073         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22074             if (b.indexOf(tag) > -1) {
22075                 return;
22076             }
22077             this.cwhite.push(tag);
22078             
22079         }, this);
22080         
22081         Roo.each(w, function(tag) {
22082             if (b.indexOf(tag) > -1) {
22083                 return;
22084             }
22085             if (this.cwhite.indexOf(tag) > -1) {
22086                 return;
22087             }
22088             this.cwhite.push(tag);
22089             
22090         }, this);
22091         
22092         
22093         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22094             if (w.indexOf(tag) > -1) {
22095                 return;
22096             }
22097             this.cblack.push(tag);
22098             
22099         }, this);
22100         
22101         Roo.each(b, function(tag) {
22102             if (w.indexOf(tag) > -1) {
22103                 return;
22104             }
22105             if (this.cblack.indexOf(tag) > -1) {
22106                 return;
22107             }
22108             this.cblack.push(tag);
22109             
22110         }, this);
22111     },
22112     
22113     setStylesheets : function(stylesheets)
22114     {
22115         if(typeof(stylesheets) == 'string'){
22116             Roo.get(this.iframe.contentDocument.head).createChild({
22117                 tag : 'link',
22118                 rel : 'stylesheet',
22119                 type : 'text/css',
22120                 href : stylesheets
22121             });
22122             
22123             return;
22124         }
22125         var _this = this;
22126      
22127         Roo.each(stylesheets, function(s) {
22128             if(!s.length){
22129                 return;
22130             }
22131             
22132             Roo.get(_this.iframe.contentDocument.head).createChild({
22133                 tag : 'link',
22134                 rel : 'stylesheet',
22135                 type : 'text/css',
22136                 href : s
22137             });
22138         });
22139
22140         
22141     },
22142     
22143     removeStylesheets : function()
22144     {
22145         var _this = this;
22146         
22147         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22148             s.remove();
22149         });
22150     },
22151     
22152     setStyle : function(style)
22153     {
22154         Roo.get(this.iframe.contentDocument.head).createChild({
22155             tag : 'style',
22156             type : 'text/css',
22157             html : style
22158         });
22159
22160         return;
22161     }
22162     
22163     // hide stuff that is not compatible
22164     /**
22165      * @event blur
22166      * @hide
22167      */
22168     /**
22169      * @event change
22170      * @hide
22171      */
22172     /**
22173      * @event focus
22174      * @hide
22175      */
22176     /**
22177      * @event specialkey
22178      * @hide
22179      */
22180     /**
22181      * @cfg {String} fieldClass @hide
22182      */
22183     /**
22184      * @cfg {String} focusClass @hide
22185      */
22186     /**
22187      * @cfg {String} autoCreate @hide
22188      */
22189     /**
22190      * @cfg {String} inputType @hide
22191      */
22192     /**
22193      * @cfg {String} invalidClass @hide
22194      */
22195     /**
22196      * @cfg {String} invalidText @hide
22197      */
22198     /**
22199      * @cfg {String} msgFx @hide
22200      */
22201     /**
22202      * @cfg {String} validateOnBlur @hide
22203      */
22204 });
22205
22206 Roo.HtmlEditorCore.white = [
22207         'area', 'br', 'img', 'input', 'hr', 'wbr',
22208         
22209        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22210        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22211        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22212        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22213        'table',   'ul',         'xmp', 
22214        
22215        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22216       'thead',   'tr', 
22217      
22218       'dir', 'menu', 'ol', 'ul', 'dl',
22219        
22220       'embed',  'object'
22221 ];
22222
22223
22224 Roo.HtmlEditorCore.black = [
22225     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22226         'applet', // 
22227         'base',   'basefont', 'bgsound', 'blink',  'body', 
22228         'frame',  'frameset', 'head',    'html',   'ilayer', 
22229         'iframe', 'layer',  'link',     'meta',    'object',   
22230         'script', 'style' ,'title',  'xml' // clean later..
22231 ];
22232 Roo.HtmlEditorCore.clean = [
22233     'script', 'style', 'title', 'xml'
22234 ];
22235 Roo.HtmlEditorCore.remove = [
22236     'font'
22237 ];
22238 // attributes..
22239
22240 Roo.HtmlEditorCore.ablack = [
22241     'on'
22242 ];
22243     
22244 Roo.HtmlEditorCore.aclean = [ 
22245     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22246 ];
22247
22248 // protocols..
22249 Roo.HtmlEditorCore.pwhite= [
22250         'http',  'https',  'mailto'
22251 ];
22252
22253 // white listed style attributes.
22254 Roo.HtmlEditorCore.cwhite= [
22255       //  'text-align', /// default is to allow most things..
22256       
22257          
22258 //        'font-size'//??
22259 ];
22260
22261 // black listed style attributes.
22262 Roo.HtmlEditorCore.cblack= [
22263       //  'font-size' -- this can be set by the project 
22264 ];
22265
22266
22267 Roo.HtmlEditorCore.swapCodes   =[ 
22268     [    8211, "&#8211;" ], 
22269     [    8212, "&#8212;" ], 
22270     [    8216,  "'" ],  
22271     [    8217, "'" ],  
22272     [    8220, '"' ],  
22273     [    8221, '"' ],  
22274     [    8226, "*" ],  
22275     [    8230, "..." ]
22276 ]; 
22277
22278     //<script type="text/javascript">
22279
22280 /*
22281  * Ext JS Library 1.1.1
22282  * Copyright(c) 2006-2007, Ext JS, LLC.
22283  * Licence LGPL
22284  * 
22285  */
22286  
22287  
22288 Roo.form.HtmlEditor = function(config){
22289     
22290     
22291     
22292     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22293     
22294     if (!this.toolbars) {
22295         this.toolbars = [];
22296     }
22297     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22298     
22299     
22300 };
22301
22302 /**
22303  * @class Roo.form.HtmlEditor
22304  * @extends Roo.form.Field
22305  * Provides a lightweight HTML Editor component.
22306  *
22307  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22308  * 
22309  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22310  * supported by this editor.</b><br/><br/>
22311  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22312  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22313  */
22314 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22315     /**
22316      * @cfg {Boolean} clearUp
22317      */
22318     clearUp : true,
22319       /**
22320      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22321      */
22322     toolbars : false,
22323    
22324      /**
22325      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22326      *                        Roo.resizable.
22327      */
22328     resizable : false,
22329      /**
22330      * @cfg {Number} height (in pixels)
22331      */   
22332     height: 300,
22333    /**
22334      * @cfg {Number} width (in pixels)
22335      */   
22336     width: 500,
22337     
22338     /**
22339      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22340      * 
22341      */
22342     stylesheets: false,
22343     
22344     
22345      /**
22346      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22347      * 
22348      */
22349     cblack: false,
22350     /**
22351      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22352      * 
22353      */
22354     cwhite: false,
22355     
22356      /**
22357      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22358      * 
22359      */
22360     black: false,
22361     /**
22362      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22363      * 
22364      */
22365     white: false,
22366     /**
22367      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22368      */
22369     allowComments: false,
22370     
22371     // id of frame..
22372     frameId: false,
22373     
22374     // private properties
22375     validationEvent : false,
22376     deferHeight: true,
22377     initialized : false,
22378     activated : false,
22379     
22380     onFocus : Roo.emptyFn,
22381     iframePad:3,
22382     hideMode:'offsets',
22383     
22384     actionMode : 'container', // defaults to hiding it...
22385     
22386     defaultAutoCreate : { // modified by initCompnoent..
22387         tag: "textarea",
22388         style:"width:500px;height:300px;",
22389         autocomplete: "new-password"
22390     },
22391
22392     // private
22393     initComponent : function(){
22394         this.addEvents({
22395             /**
22396              * @event initialize
22397              * Fires when the editor is fully initialized (including the iframe)
22398              * @param {HtmlEditor} this
22399              */
22400             initialize: true,
22401             /**
22402              * @event activate
22403              * Fires when the editor is first receives the focus. Any insertion must wait
22404              * until after this event.
22405              * @param {HtmlEditor} this
22406              */
22407             activate: true,
22408              /**
22409              * @event beforesync
22410              * Fires before the textarea is updated with content from the editor iframe. Return false
22411              * to cancel the sync.
22412              * @param {HtmlEditor} this
22413              * @param {String} html
22414              */
22415             beforesync: true,
22416              /**
22417              * @event beforepush
22418              * Fires before the iframe editor is updated with content from the textarea. Return false
22419              * to cancel the push.
22420              * @param {HtmlEditor} this
22421              * @param {String} html
22422              */
22423             beforepush: true,
22424              /**
22425              * @event sync
22426              * Fires when the textarea is updated with content from the editor iframe.
22427              * @param {HtmlEditor} this
22428              * @param {String} html
22429              */
22430             sync: true,
22431              /**
22432              * @event push
22433              * Fires when the iframe editor is updated with content from the textarea.
22434              * @param {HtmlEditor} this
22435              * @param {String} html
22436              */
22437             push: true,
22438              /**
22439              * @event editmodechange
22440              * Fires when the editor switches edit modes
22441              * @param {HtmlEditor} this
22442              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22443              */
22444             editmodechange: true,
22445             /**
22446              * @event editorevent
22447              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22448              * @param {HtmlEditor} this
22449              */
22450             editorevent: true,
22451             /**
22452              * @event firstfocus
22453              * Fires when on first focus - needed by toolbars..
22454              * @param {HtmlEditor} this
22455              */
22456             firstfocus: true,
22457             /**
22458              * @event autosave
22459              * Auto save the htmlEditor value as a file into Events
22460              * @param {HtmlEditor} this
22461              */
22462             autosave: true,
22463             /**
22464              * @event savedpreview
22465              * preview the saved version of htmlEditor
22466              * @param {HtmlEditor} this
22467              */
22468             savedpreview: true,
22469             
22470             /**
22471             * @event stylesheetsclick
22472             * Fires when press the Sytlesheets button
22473             * @param {Roo.HtmlEditorCore} this
22474             */
22475             stylesheetsclick: true
22476         });
22477         this.defaultAutoCreate =  {
22478             tag: "textarea",
22479             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22480             autocomplete: "new-password"
22481         };
22482     },
22483
22484     /**
22485      * Protected method that will not generally be called directly. It
22486      * is called when the editor creates its toolbar. Override this method if you need to
22487      * add custom toolbar buttons.
22488      * @param {HtmlEditor} editor
22489      */
22490     createToolbar : function(editor){
22491         Roo.log("create toolbars");
22492         if (!editor.toolbars || !editor.toolbars.length) {
22493             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22494         }
22495         
22496         for (var i =0 ; i < editor.toolbars.length;i++) {
22497             editor.toolbars[i] = Roo.factory(
22498                     typeof(editor.toolbars[i]) == 'string' ?
22499                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22500                 Roo.form.HtmlEditor);
22501             editor.toolbars[i].init(editor);
22502         }
22503          
22504         
22505     },
22506
22507      
22508     // private
22509     onRender : function(ct, position)
22510     {
22511         var _t = this;
22512         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22513         
22514         this.wrap = this.el.wrap({
22515             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22516         });
22517         
22518         this.editorcore.onRender(ct, position);
22519          
22520         if (this.resizable) {
22521             this.resizeEl = new Roo.Resizable(this.wrap, {
22522                 pinned : true,
22523                 wrap: true,
22524                 dynamic : true,
22525                 minHeight : this.height,
22526                 height: this.height,
22527                 handles : this.resizable,
22528                 width: this.width,
22529                 listeners : {
22530                     resize : function(r, w, h) {
22531                         _t.onResize(w,h); // -something
22532                     }
22533                 }
22534             });
22535             
22536         }
22537         this.createToolbar(this);
22538        
22539         
22540         if(!this.width){
22541             this.setSize(this.wrap.getSize());
22542         }
22543         if (this.resizeEl) {
22544             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22545             // should trigger onReize..
22546         }
22547         
22548         this.keyNav = new Roo.KeyNav(this.el, {
22549             
22550             "tab" : function(e){
22551                 e.preventDefault();
22552                 
22553                 var value = this.getValue();
22554                 
22555                 var start = this.el.dom.selectionStart;
22556                 var end = this.el.dom.selectionEnd;
22557                 
22558                 if(!e.shiftKey){
22559                     
22560                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22561                     this.el.dom.setSelectionRange(end + 1, end + 1);
22562                     return;
22563                 }
22564                 
22565                 var f = value.substring(0, start).split("\t");
22566                 
22567                 if(f.pop().length != 0){
22568                     return;
22569                 }
22570                 
22571                 this.setValue(f.join("\t") + value.substring(end));
22572                 this.el.dom.setSelectionRange(start - 1, start - 1);
22573                 
22574             },
22575             
22576             "home" : function(e){
22577                 e.preventDefault();
22578                 
22579                 var curr = this.el.dom.selectionStart;
22580                 var lines = this.getValue().split("\n");
22581                 
22582                 if(!lines.length){
22583                     return;
22584                 }
22585                 
22586                 if(e.ctrlKey){
22587                     this.el.dom.setSelectionRange(0, 0);
22588                     return;
22589                 }
22590                 
22591                 var pos = 0;
22592                 
22593                 for (var i = 0; i < lines.length;i++) {
22594                     pos += lines[i].length;
22595                     
22596                     if(i != 0){
22597                         pos += 1;
22598                     }
22599                     
22600                     if(pos < curr){
22601                         continue;
22602                     }
22603                     
22604                     pos -= lines[i].length;
22605                     
22606                     break;
22607                 }
22608                 
22609                 if(!e.shiftKey){
22610                     this.el.dom.setSelectionRange(pos, pos);
22611                     return;
22612                 }
22613                 
22614                 this.el.dom.selectionStart = pos;
22615                 this.el.dom.selectionEnd = curr;
22616             },
22617             
22618             "end" : function(e){
22619                 e.preventDefault();
22620                 
22621                 var curr = this.el.dom.selectionStart;
22622                 var lines = this.getValue().split("\n");
22623                 
22624                 if(!lines.length){
22625                     return;
22626                 }
22627                 
22628                 if(e.ctrlKey){
22629                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22630                     return;
22631                 }
22632                 
22633                 var pos = 0;
22634                 
22635                 for (var i = 0; i < lines.length;i++) {
22636                     
22637                     pos += lines[i].length;
22638                     
22639                     if(i != 0){
22640                         pos += 1;
22641                     }
22642                     
22643                     if(pos < curr){
22644                         continue;
22645                     }
22646                     
22647                     break;
22648                 }
22649                 
22650                 if(!e.shiftKey){
22651                     this.el.dom.setSelectionRange(pos, pos);
22652                     return;
22653                 }
22654                 
22655                 this.el.dom.selectionStart = curr;
22656                 this.el.dom.selectionEnd = pos;
22657             },
22658
22659             scope : this,
22660
22661             doRelay : function(foo, bar, hname){
22662                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22663             },
22664
22665             forceKeyDown: true
22666         });
22667         
22668 //        if(this.autosave && this.w){
22669 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22670 //        }
22671     },
22672
22673     // private
22674     onResize : function(w, h)
22675     {
22676         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22677         var ew = false;
22678         var eh = false;
22679         
22680         if(this.el ){
22681             if(typeof w == 'number'){
22682                 var aw = w - this.wrap.getFrameWidth('lr');
22683                 this.el.setWidth(this.adjustWidth('textarea', aw));
22684                 ew = aw;
22685             }
22686             if(typeof h == 'number'){
22687                 var tbh = 0;
22688                 for (var i =0; i < this.toolbars.length;i++) {
22689                     // fixme - ask toolbars for heights?
22690                     tbh += this.toolbars[i].tb.el.getHeight();
22691                     if (this.toolbars[i].footer) {
22692                         tbh += this.toolbars[i].footer.el.getHeight();
22693                     }
22694                 }
22695                 
22696                 
22697                 
22698                 
22699                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22700                 ah -= 5; // knock a few pixes off for look..
22701 //                Roo.log(ah);
22702                 this.el.setHeight(this.adjustWidth('textarea', ah));
22703                 var eh = ah;
22704             }
22705         }
22706         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22707         this.editorcore.onResize(ew,eh);
22708         
22709     },
22710
22711     /**
22712      * Toggles the editor between standard and source edit mode.
22713      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22714      */
22715     toggleSourceEdit : function(sourceEditMode)
22716     {
22717         this.editorcore.toggleSourceEdit(sourceEditMode);
22718         
22719         if(this.editorcore.sourceEditMode){
22720             Roo.log('editor - showing textarea');
22721             
22722 //            Roo.log('in');
22723 //            Roo.log(this.syncValue());
22724             this.editorcore.syncValue();
22725             this.el.removeClass('x-hidden');
22726             this.el.dom.removeAttribute('tabIndex');
22727             this.el.focus();
22728             
22729             for (var i = 0; i < this.toolbars.length; i++) {
22730                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22731                     this.toolbars[i].tb.hide();
22732                     this.toolbars[i].footer.hide();
22733                 }
22734             }
22735             
22736         }else{
22737             Roo.log('editor - hiding textarea');
22738 //            Roo.log('out')
22739 //            Roo.log(this.pushValue()); 
22740             this.editorcore.pushValue();
22741             
22742             this.el.addClass('x-hidden');
22743             this.el.dom.setAttribute('tabIndex', -1);
22744             
22745             for (var i = 0; i < this.toolbars.length; i++) {
22746                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22747                     this.toolbars[i].tb.show();
22748                     this.toolbars[i].footer.show();
22749                 }
22750             }
22751             
22752             //this.deferFocus();
22753         }
22754         
22755         this.setSize(this.wrap.getSize());
22756         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22757         
22758         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22759     },
22760  
22761     // private (for BoxComponent)
22762     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22763
22764     // private (for BoxComponent)
22765     getResizeEl : function(){
22766         return this.wrap;
22767     },
22768
22769     // private (for BoxComponent)
22770     getPositionEl : function(){
22771         return this.wrap;
22772     },
22773
22774     // private
22775     initEvents : function(){
22776         this.originalValue = this.getValue();
22777     },
22778
22779     /**
22780      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22781      * @method
22782      */
22783     markInvalid : Roo.emptyFn,
22784     /**
22785      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22786      * @method
22787      */
22788     clearInvalid : Roo.emptyFn,
22789
22790     setValue : function(v){
22791         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22792         this.editorcore.pushValue();
22793     },
22794
22795      
22796     // private
22797     deferFocus : function(){
22798         this.focus.defer(10, this);
22799     },
22800
22801     // doc'ed in Field
22802     focus : function(){
22803         this.editorcore.focus();
22804         
22805     },
22806       
22807
22808     // private
22809     onDestroy : function(){
22810         
22811         
22812         
22813         if(this.rendered){
22814             
22815             for (var i =0; i < this.toolbars.length;i++) {
22816                 // fixme - ask toolbars for heights?
22817                 this.toolbars[i].onDestroy();
22818             }
22819             
22820             this.wrap.dom.innerHTML = '';
22821             this.wrap.remove();
22822         }
22823     },
22824
22825     // private
22826     onFirstFocus : function(){
22827         //Roo.log("onFirstFocus");
22828         this.editorcore.onFirstFocus();
22829          for (var i =0; i < this.toolbars.length;i++) {
22830             this.toolbars[i].onFirstFocus();
22831         }
22832         
22833     },
22834     
22835     // private
22836     syncValue : function()
22837     {
22838         this.editorcore.syncValue();
22839     },
22840     
22841     pushValue : function()
22842     {
22843         this.editorcore.pushValue();
22844     },
22845     
22846     setStylesheets : function(stylesheets)
22847     {
22848         this.editorcore.setStylesheets(stylesheets);
22849     },
22850     
22851     removeStylesheets : function()
22852     {
22853         this.editorcore.removeStylesheets();
22854     }
22855      
22856     
22857     // hide stuff that is not compatible
22858     /**
22859      * @event blur
22860      * @hide
22861      */
22862     /**
22863      * @event change
22864      * @hide
22865      */
22866     /**
22867      * @event focus
22868      * @hide
22869      */
22870     /**
22871      * @event specialkey
22872      * @hide
22873      */
22874     /**
22875      * @cfg {String} fieldClass @hide
22876      */
22877     /**
22878      * @cfg {String} focusClass @hide
22879      */
22880     /**
22881      * @cfg {String} autoCreate @hide
22882      */
22883     /**
22884      * @cfg {String} inputType @hide
22885      */
22886     /**
22887      * @cfg {String} invalidClass @hide
22888      */
22889     /**
22890      * @cfg {String} invalidText @hide
22891      */
22892     /**
22893      * @cfg {String} msgFx @hide
22894      */
22895     /**
22896      * @cfg {String} validateOnBlur @hide
22897      */
22898 });
22899  
22900     // <script type="text/javascript">
22901 /*
22902  * Based on
22903  * Ext JS Library 1.1.1
22904  * Copyright(c) 2006-2007, Ext JS, LLC.
22905  *  
22906  
22907  */
22908
22909 /**
22910  * @class Roo.form.HtmlEditorToolbar1
22911  * Basic Toolbar
22912  * 
22913  * Usage:
22914  *
22915  new Roo.form.HtmlEditor({
22916     ....
22917     toolbars : [
22918         new Roo.form.HtmlEditorToolbar1({
22919             disable : { fonts: 1 , format: 1, ..., ... , ...],
22920             btns : [ .... ]
22921         })
22922     }
22923      
22924  * 
22925  * @cfg {Object} disable List of elements to disable..
22926  * @cfg {Array} btns List of additional buttons.
22927  * 
22928  * 
22929  * NEEDS Extra CSS? 
22930  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22931  */
22932  
22933 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22934 {
22935     
22936     Roo.apply(this, config);
22937     
22938     // default disabled, based on 'good practice'..
22939     this.disable = this.disable || {};
22940     Roo.applyIf(this.disable, {
22941         fontSize : true,
22942         colors : true,
22943         specialElements : true
22944     });
22945     
22946     
22947     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22948     // dont call parent... till later.
22949 }
22950
22951 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22952     
22953     tb: false,
22954     
22955     rendered: false,
22956     
22957     editor : false,
22958     editorcore : false,
22959     /**
22960      * @cfg {Object} disable  List of toolbar elements to disable
22961          
22962      */
22963     disable : false,
22964     
22965     
22966      /**
22967      * @cfg {String} createLinkText The default text for the create link prompt
22968      */
22969     createLinkText : 'Please enter the URL for the link:',
22970     /**
22971      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22972      */
22973     defaultLinkValue : 'http:/'+'/',
22974    
22975     
22976       /**
22977      * @cfg {Array} fontFamilies An array of available font families
22978      */
22979     fontFamilies : [
22980         'Arial',
22981         'Courier New',
22982         'Tahoma',
22983         'Times New Roman',
22984         'Verdana'
22985     ],
22986     
22987     specialChars : [
22988            "&#169;",
22989           "&#174;",     
22990           "&#8482;",    
22991           "&#163;" ,    
22992          // "&#8212;",    
22993           "&#8230;",    
22994           "&#247;" ,    
22995         //  "&#225;" ,     ?? a acute?
22996            "&#8364;"    , //Euro
22997        //   "&#8220;"    ,
22998         //  "&#8221;"    ,
22999         //  "&#8226;"    ,
23000           "&#176;"  //   , // degrees
23001
23002          // "&#233;"     , // e ecute
23003          // "&#250;"     , // u ecute?
23004     ],
23005     
23006     specialElements : [
23007         {
23008             text: "Insert Table",
23009             xtype: 'MenuItem',
23010             xns : Roo.Menu,
23011             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23012                 
23013         },
23014         {    
23015             text: "Insert Image",
23016             xtype: 'MenuItem',
23017             xns : Roo.Menu,
23018             ihtml : '<img src="about:blank"/>'
23019             
23020         }
23021         
23022          
23023     ],
23024     
23025     
23026     inputElements : [ 
23027             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23028             "input:submit", "input:button", "select", "textarea", "label" ],
23029     formats : [
23030         ["p"] ,  
23031         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23032         ["pre"],[ "code"], 
23033         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23034         ['div'],['span'],
23035         ['sup'],['sub']
23036     ],
23037     
23038     cleanStyles : [
23039         "font-size"
23040     ],
23041      /**
23042      * @cfg {String} defaultFont default font to use.
23043      */
23044     defaultFont: 'tahoma',
23045    
23046     fontSelect : false,
23047     
23048     
23049     formatCombo : false,
23050     
23051     init : function(editor)
23052     {
23053         this.editor = editor;
23054         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23055         var editorcore = this.editorcore;
23056         
23057         var _t = this;
23058         
23059         var fid = editorcore.frameId;
23060         var etb = this;
23061         function btn(id, toggle, handler){
23062             var xid = fid + '-'+ id ;
23063             return {
23064                 id : xid,
23065                 cmd : id,
23066                 cls : 'x-btn-icon x-edit-'+id,
23067                 enableToggle:toggle !== false,
23068                 scope: _t, // was editor...
23069                 handler:handler||_t.relayBtnCmd,
23070                 clickEvent:'mousedown',
23071                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23072                 tabIndex:-1
23073             };
23074         }
23075         
23076         
23077         
23078         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23079         this.tb = tb;
23080          // stop form submits
23081         tb.el.on('click', function(e){
23082             e.preventDefault(); // what does this do?
23083         });
23084
23085         if(!this.disable.font) { // && !Roo.isSafari){
23086             /* why no safari for fonts 
23087             editor.fontSelect = tb.el.createChild({
23088                 tag:'select',
23089                 tabIndex: -1,
23090                 cls:'x-font-select',
23091                 html: this.createFontOptions()
23092             });
23093             
23094             editor.fontSelect.on('change', function(){
23095                 var font = editor.fontSelect.dom.value;
23096                 editor.relayCmd('fontname', font);
23097                 editor.deferFocus();
23098             }, editor);
23099             
23100             tb.add(
23101                 editor.fontSelect.dom,
23102                 '-'
23103             );
23104             */
23105             
23106         };
23107         if(!this.disable.formats){
23108             this.formatCombo = new Roo.form.ComboBox({
23109                 store: new Roo.data.SimpleStore({
23110                     id : 'tag',
23111                     fields: ['tag'],
23112                     data : this.formats // from states.js
23113                 }),
23114                 blockFocus : true,
23115                 name : '',
23116                 //autoCreate : {tag: "div",  size: "20"},
23117                 displayField:'tag',
23118                 typeAhead: false,
23119                 mode: 'local',
23120                 editable : false,
23121                 triggerAction: 'all',
23122                 emptyText:'Add tag',
23123                 selectOnFocus:true,
23124                 width:135,
23125                 listeners : {
23126                     'select': function(c, r, i) {
23127                         editorcore.insertTag(r.get('tag'));
23128                         editor.focus();
23129                     }
23130                 }
23131
23132             });
23133             tb.addField(this.formatCombo);
23134             
23135         }
23136         
23137         if(!this.disable.format){
23138             tb.add(
23139                 btn('bold'),
23140                 btn('italic'),
23141                 btn('underline'),
23142                 btn('strikethrough')
23143             );
23144         };
23145         if(!this.disable.fontSize){
23146             tb.add(
23147                 '-',
23148                 
23149                 
23150                 btn('increasefontsize', false, editorcore.adjustFont),
23151                 btn('decreasefontsize', false, editorcore.adjustFont)
23152             );
23153         };
23154         
23155         
23156         if(!this.disable.colors){
23157             tb.add(
23158                 '-', {
23159                     id:editorcore.frameId +'-forecolor',
23160                     cls:'x-btn-icon x-edit-forecolor',
23161                     clickEvent:'mousedown',
23162                     tooltip: this.buttonTips['forecolor'] || undefined,
23163                     tabIndex:-1,
23164                     menu : new Roo.menu.ColorMenu({
23165                         allowReselect: true,
23166                         focus: Roo.emptyFn,
23167                         value:'000000',
23168                         plain:true,
23169                         selectHandler: function(cp, color){
23170                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23171                             editor.deferFocus();
23172                         },
23173                         scope: editorcore,
23174                         clickEvent:'mousedown'
23175                     })
23176                 }, {
23177                     id:editorcore.frameId +'backcolor',
23178                     cls:'x-btn-icon x-edit-backcolor',
23179                     clickEvent:'mousedown',
23180                     tooltip: this.buttonTips['backcolor'] || undefined,
23181                     tabIndex:-1,
23182                     menu : new Roo.menu.ColorMenu({
23183                         focus: Roo.emptyFn,
23184                         value:'FFFFFF',
23185                         plain:true,
23186                         allowReselect: true,
23187                         selectHandler: function(cp, color){
23188                             if(Roo.isGecko){
23189                                 editorcore.execCmd('useCSS', false);
23190                                 editorcore.execCmd('hilitecolor', color);
23191                                 editorcore.execCmd('useCSS', true);
23192                                 editor.deferFocus();
23193                             }else{
23194                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23195                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23196                                 editor.deferFocus();
23197                             }
23198                         },
23199                         scope:editorcore,
23200                         clickEvent:'mousedown'
23201                     })
23202                 }
23203             );
23204         };
23205         // now add all the items...
23206         
23207
23208         if(!this.disable.alignments){
23209             tb.add(
23210                 '-',
23211                 btn('justifyleft'),
23212                 btn('justifycenter'),
23213                 btn('justifyright')
23214             );
23215         };
23216
23217         //if(!Roo.isSafari){
23218             if(!this.disable.links){
23219                 tb.add(
23220                     '-',
23221                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23222                 );
23223             };
23224
23225             if(!this.disable.lists){
23226                 tb.add(
23227                     '-',
23228                     btn('insertorderedlist'),
23229                     btn('insertunorderedlist')
23230                 );
23231             }
23232             if(!this.disable.sourceEdit){
23233                 tb.add(
23234                     '-',
23235                     btn('sourceedit', true, function(btn){
23236                         this.toggleSourceEdit(btn.pressed);
23237                     })
23238                 );
23239             }
23240         //}
23241         
23242         var smenu = { };
23243         // special menu.. - needs to be tidied up..
23244         if (!this.disable.special) {
23245             smenu = {
23246                 text: "&#169;",
23247                 cls: 'x-edit-none',
23248                 
23249                 menu : {
23250                     items : []
23251                 }
23252             };
23253             for (var i =0; i < this.specialChars.length; i++) {
23254                 smenu.menu.items.push({
23255                     
23256                     html: this.specialChars[i],
23257                     handler: function(a,b) {
23258                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23259                         //editor.insertAtCursor(a.html);
23260                         
23261                     },
23262                     tabIndex:-1
23263                 });
23264             }
23265             
23266             
23267             tb.add(smenu);
23268             
23269             
23270         }
23271         
23272         var cmenu = { };
23273         if (!this.disable.cleanStyles) {
23274             cmenu = {
23275                 cls: 'x-btn-icon x-btn-clear',
23276                 
23277                 menu : {
23278                     items : []
23279                 }
23280             };
23281             for (var i =0; i < this.cleanStyles.length; i++) {
23282                 cmenu.menu.items.push({
23283                     actiontype : this.cleanStyles[i],
23284                     html: 'Remove ' + this.cleanStyles[i],
23285                     handler: function(a,b) {
23286 //                        Roo.log(a);
23287 //                        Roo.log(b);
23288                         var c = Roo.get(editorcore.doc.body);
23289                         c.select('[style]').each(function(s) {
23290                             s.dom.style.removeProperty(a.actiontype);
23291                         });
23292                         editorcore.syncValue();
23293                     },
23294                     tabIndex:-1
23295                 });
23296             }
23297              cmenu.menu.items.push({
23298                 actiontype : 'tablewidths',
23299                 html: 'Remove Table Widths',
23300                 handler: function(a,b) {
23301                     editorcore.cleanTableWidths();
23302                     editorcore.syncValue();
23303                 },
23304                 tabIndex:-1
23305             });
23306             cmenu.menu.items.push({
23307                 actiontype : 'word',
23308                 html: 'Remove MS Word Formating',
23309                 handler: function(a,b) {
23310                     editorcore.cleanWord();
23311                     editorcore.syncValue();
23312                 },
23313                 tabIndex:-1
23314             });
23315             
23316             cmenu.menu.items.push({
23317                 actiontype : 'all',
23318                 html: 'Remove All Styles',
23319                 handler: function(a,b) {
23320                     
23321                     var c = Roo.get(editorcore.doc.body);
23322                     c.select('[style]').each(function(s) {
23323                         s.dom.removeAttribute('style');
23324                     });
23325                     editorcore.syncValue();
23326                 },
23327                 tabIndex:-1
23328             });
23329             
23330             cmenu.menu.items.push({
23331                 actiontype : 'all',
23332                 html: 'Remove All CSS Classes',
23333                 handler: function(a,b) {
23334                     
23335                     var c = Roo.get(editorcore.doc.body);
23336                     c.select('[class]').each(function(s) {
23337                         s.dom.removeAttribute('class');
23338                     });
23339                     editorcore.cleanWord();
23340                     editorcore.syncValue();
23341                 },
23342                 tabIndex:-1
23343             });
23344             
23345              cmenu.menu.items.push({
23346                 actiontype : 'tidy',
23347                 html: 'Tidy HTML Source',
23348                 handler: function(a,b) {
23349                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23350                     editorcore.syncValue();
23351                 },
23352                 tabIndex:-1
23353             });
23354             
23355             
23356             tb.add(cmenu);
23357         }
23358          
23359         if (!this.disable.specialElements) {
23360             var semenu = {
23361                 text: "Other;",
23362                 cls: 'x-edit-none',
23363                 menu : {
23364                     items : []
23365                 }
23366             };
23367             for (var i =0; i < this.specialElements.length; i++) {
23368                 semenu.menu.items.push(
23369                     Roo.apply({ 
23370                         handler: function(a,b) {
23371                             editor.insertAtCursor(this.ihtml);
23372                         }
23373                     }, this.specialElements[i])
23374                 );
23375                     
23376             }
23377             
23378             tb.add(semenu);
23379             
23380             
23381         }
23382          
23383         
23384         if (this.btns) {
23385             for(var i =0; i< this.btns.length;i++) {
23386                 var b = Roo.factory(this.btns[i],Roo.form);
23387                 b.cls =  'x-edit-none';
23388                 
23389                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23390                     b.cls += ' x-init-enable';
23391                 }
23392                 
23393                 b.scope = editorcore;
23394                 tb.add(b);
23395             }
23396         
23397         }
23398         
23399         
23400         
23401         // disable everything...
23402         
23403         this.tb.items.each(function(item){
23404             
23405            if(
23406                 item.id != editorcore.frameId+ '-sourceedit' && 
23407                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23408             ){
23409                 
23410                 item.disable();
23411             }
23412         });
23413         this.rendered = true;
23414         
23415         // the all the btns;
23416         editor.on('editorevent', this.updateToolbar, this);
23417         // other toolbars need to implement this..
23418         //editor.on('editmodechange', this.updateToolbar, this);
23419     },
23420     
23421     
23422     relayBtnCmd : function(btn) {
23423         this.editorcore.relayCmd(btn.cmd);
23424     },
23425     // private used internally
23426     createLink : function(){
23427         Roo.log("create link?");
23428         var url = prompt(this.createLinkText, this.defaultLinkValue);
23429         if(url && url != 'http:/'+'/'){
23430             this.editorcore.relayCmd('createlink', url);
23431         }
23432     },
23433
23434     
23435     /**
23436      * Protected method that will not generally be called directly. It triggers
23437      * a toolbar update by reading the markup state of the current selection in the editor.
23438      */
23439     updateToolbar: function(){
23440
23441         if(!this.editorcore.activated){
23442             this.editor.onFirstFocus();
23443             return;
23444         }
23445
23446         var btns = this.tb.items.map, 
23447             doc = this.editorcore.doc,
23448             frameId = this.editorcore.frameId;
23449
23450         if(!this.disable.font && !Roo.isSafari){
23451             /*
23452             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23453             if(name != this.fontSelect.dom.value){
23454                 this.fontSelect.dom.value = name;
23455             }
23456             */
23457         }
23458         if(!this.disable.format){
23459             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23460             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23461             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23462             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23463         }
23464         if(!this.disable.alignments){
23465             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23466             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23467             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23468         }
23469         if(!Roo.isSafari && !this.disable.lists){
23470             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23471             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23472         }
23473         
23474         var ans = this.editorcore.getAllAncestors();
23475         if (this.formatCombo) {
23476             
23477             
23478             var store = this.formatCombo.store;
23479             this.formatCombo.setValue("");
23480             for (var i =0; i < ans.length;i++) {
23481                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23482                     // select it..
23483                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23484                     break;
23485                 }
23486             }
23487         }
23488         
23489         
23490         
23491         // hides menus... - so this cant be on a menu...
23492         Roo.menu.MenuMgr.hideAll();
23493
23494         //this.editorsyncValue();
23495     },
23496    
23497     
23498     createFontOptions : function(){
23499         var buf = [], fs = this.fontFamilies, ff, lc;
23500         
23501         
23502         
23503         for(var i = 0, len = fs.length; i< len; i++){
23504             ff = fs[i];
23505             lc = ff.toLowerCase();
23506             buf.push(
23507                 '<option value="',lc,'" style="font-family:',ff,';"',
23508                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23509                     ff,
23510                 '</option>'
23511             );
23512         }
23513         return buf.join('');
23514     },
23515     
23516     toggleSourceEdit : function(sourceEditMode){
23517         
23518         Roo.log("toolbar toogle");
23519         if(sourceEditMode === undefined){
23520             sourceEditMode = !this.sourceEditMode;
23521         }
23522         this.sourceEditMode = sourceEditMode === true;
23523         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23524         // just toggle the button?
23525         if(btn.pressed !== this.sourceEditMode){
23526             btn.toggle(this.sourceEditMode);
23527             return;
23528         }
23529         
23530         if(sourceEditMode){
23531             Roo.log("disabling buttons");
23532             this.tb.items.each(function(item){
23533                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23534                     item.disable();
23535                 }
23536             });
23537           
23538         }else{
23539             Roo.log("enabling buttons");
23540             if(this.editorcore.initialized){
23541                 this.tb.items.each(function(item){
23542                     item.enable();
23543                 });
23544             }
23545             
23546         }
23547         Roo.log("calling toggole on editor");
23548         // tell the editor that it's been pressed..
23549         this.editor.toggleSourceEdit(sourceEditMode);
23550        
23551     },
23552      /**
23553      * Object collection of toolbar tooltips for the buttons in the editor. The key
23554      * is the command id associated with that button and the value is a valid QuickTips object.
23555      * For example:
23556 <pre><code>
23557 {
23558     bold : {
23559         title: 'Bold (Ctrl+B)',
23560         text: 'Make the selected text bold.',
23561         cls: 'x-html-editor-tip'
23562     },
23563     italic : {
23564         title: 'Italic (Ctrl+I)',
23565         text: 'Make the selected text italic.',
23566         cls: 'x-html-editor-tip'
23567     },
23568     ...
23569 </code></pre>
23570     * @type Object
23571      */
23572     buttonTips : {
23573         bold : {
23574             title: 'Bold (Ctrl+B)',
23575             text: 'Make the selected text bold.',
23576             cls: 'x-html-editor-tip'
23577         },
23578         italic : {
23579             title: 'Italic (Ctrl+I)',
23580             text: 'Make the selected text italic.',
23581             cls: 'x-html-editor-tip'
23582         },
23583         underline : {
23584             title: 'Underline (Ctrl+U)',
23585             text: 'Underline the selected text.',
23586             cls: 'x-html-editor-tip'
23587         },
23588         strikethrough : {
23589             title: 'Strikethrough',
23590             text: 'Strikethrough the selected text.',
23591             cls: 'x-html-editor-tip'
23592         },
23593         increasefontsize : {
23594             title: 'Grow Text',
23595             text: 'Increase the font size.',
23596             cls: 'x-html-editor-tip'
23597         },
23598         decreasefontsize : {
23599             title: 'Shrink Text',
23600             text: 'Decrease the font size.',
23601             cls: 'x-html-editor-tip'
23602         },
23603         backcolor : {
23604             title: 'Text Highlight Color',
23605             text: 'Change the background color of the selected text.',
23606             cls: 'x-html-editor-tip'
23607         },
23608         forecolor : {
23609             title: 'Font Color',
23610             text: 'Change the color of the selected text.',
23611             cls: 'x-html-editor-tip'
23612         },
23613         justifyleft : {
23614             title: 'Align Text Left',
23615             text: 'Align text to the left.',
23616             cls: 'x-html-editor-tip'
23617         },
23618         justifycenter : {
23619             title: 'Center Text',
23620             text: 'Center text in the editor.',
23621             cls: 'x-html-editor-tip'
23622         },
23623         justifyright : {
23624             title: 'Align Text Right',
23625             text: 'Align text to the right.',
23626             cls: 'x-html-editor-tip'
23627         },
23628         insertunorderedlist : {
23629             title: 'Bullet List',
23630             text: 'Start a bulleted list.',
23631             cls: 'x-html-editor-tip'
23632         },
23633         insertorderedlist : {
23634             title: 'Numbered List',
23635             text: 'Start a numbered list.',
23636             cls: 'x-html-editor-tip'
23637         },
23638         createlink : {
23639             title: 'Hyperlink',
23640             text: 'Make the selected text a hyperlink.',
23641             cls: 'x-html-editor-tip'
23642         },
23643         sourceedit : {
23644             title: 'Source Edit',
23645             text: 'Switch to source editing mode.',
23646             cls: 'x-html-editor-tip'
23647         }
23648     },
23649     // private
23650     onDestroy : function(){
23651         if(this.rendered){
23652             
23653             this.tb.items.each(function(item){
23654                 if(item.menu){
23655                     item.menu.removeAll();
23656                     if(item.menu.el){
23657                         item.menu.el.destroy();
23658                     }
23659                 }
23660                 item.destroy();
23661             });
23662              
23663         }
23664     },
23665     onFirstFocus: function() {
23666         this.tb.items.each(function(item){
23667            item.enable();
23668         });
23669     }
23670 });
23671
23672
23673
23674
23675 // <script type="text/javascript">
23676 /*
23677  * Based on
23678  * Ext JS Library 1.1.1
23679  * Copyright(c) 2006-2007, Ext JS, LLC.
23680  *  
23681  
23682  */
23683
23684  
23685 /**
23686  * @class Roo.form.HtmlEditor.ToolbarContext
23687  * Context Toolbar
23688  * 
23689  * Usage:
23690  *
23691  new Roo.form.HtmlEditor({
23692     ....
23693     toolbars : [
23694         { xtype: 'ToolbarStandard', styles : {} }
23695         { xtype: 'ToolbarContext', disable : {} }
23696     ]
23697 })
23698
23699      
23700  * 
23701  * @config : {Object} disable List of elements to disable.. (not done yet.)
23702  * @config : {Object} styles  Map of styles available.
23703  * 
23704  */
23705
23706 Roo.form.HtmlEditor.ToolbarContext = function(config)
23707 {
23708     
23709     Roo.apply(this, config);
23710     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23711     // dont call parent... till later.
23712     this.styles = this.styles || {};
23713 }
23714
23715  
23716
23717 Roo.form.HtmlEditor.ToolbarContext.types = {
23718     'IMG' : {
23719         width : {
23720             title: "Width",
23721             width: 40
23722         },
23723         height:  {
23724             title: "Height",
23725             width: 40
23726         },
23727         align: {
23728             title: "Align",
23729             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23730             width : 80
23731             
23732         },
23733         border: {
23734             title: "Border",
23735             width: 40
23736         },
23737         alt: {
23738             title: "Alt",
23739             width: 120
23740         },
23741         src : {
23742             title: "Src",
23743             width: 220
23744         }
23745         
23746     },
23747     'A' : {
23748         name : {
23749             title: "Name",
23750             width: 50
23751         },
23752         target:  {
23753             title: "Target",
23754             width: 120
23755         },
23756         href:  {
23757             title: "Href",
23758             width: 220
23759         } // border?
23760         
23761     },
23762     'TABLE' : {
23763         rows : {
23764             title: "Rows",
23765             width: 20
23766         },
23767         cols : {
23768             title: "Cols",
23769             width: 20
23770         },
23771         width : {
23772             title: "Width",
23773             width: 40
23774         },
23775         height : {
23776             title: "Height",
23777             width: 40
23778         },
23779         border : {
23780             title: "Border",
23781             width: 20
23782         }
23783     },
23784     'TD' : {
23785         width : {
23786             title: "Width",
23787             width: 40
23788         },
23789         height : {
23790             title: "Height",
23791             width: 40
23792         },   
23793         align: {
23794             title: "Align",
23795             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23796             width: 80
23797         },
23798         valign: {
23799             title: "Valign",
23800             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23801             width: 80
23802         },
23803         colspan: {
23804             title: "Colspan",
23805             width: 20
23806             
23807         },
23808          'font-family'  : {
23809             title : "Font",
23810             style : 'fontFamily',
23811             displayField: 'display',
23812             optname : 'font-family',
23813             width: 140
23814         }
23815     },
23816     'INPUT' : {
23817         name : {
23818             title: "name",
23819             width: 120
23820         },
23821         value : {
23822             title: "Value",
23823             width: 120
23824         },
23825         width : {
23826             title: "Width",
23827             width: 40
23828         }
23829     },
23830     'LABEL' : {
23831         'for' : {
23832             title: "For",
23833             width: 120
23834         }
23835     },
23836     'TEXTAREA' : {
23837           name : {
23838             title: "name",
23839             width: 120
23840         },
23841         rows : {
23842             title: "Rows",
23843             width: 20
23844         },
23845         cols : {
23846             title: "Cols",
23847             width: 20
23848         }
23849     },
23850     'SELECT' : {
23851         name : {
23852             title: "name",
23853             width: 120
23854         },
23855         selectoptions : {
23856             title: "Options",
23857             width: 200
23858         }
23859     },
23860     
23861     // should we really allow this??
23862     // should this just be 
23863     'BODY' : {
23864         title : {
23865             title: "Title",
23866             width: 200,
23867             disabled : true
23868         }
23869     },
23870     'SPAN' : {
23871         'font-family'  : {
23872             title : "Font",
23873             style : 'fontFamily',
23874             displayField: 'display',
23875             optname : 'font-family',
23876             width: 140
23877         }
23878     },
23879     'DIV' : {
23880         'font-family'  : {
23881             title : "Font",
23882             style : 'fontFamily',
23883             displayField: 'display',
23884             optname : 'font-family',
23885             width: 140
23886         }
23887     },
23888      'P' : {
23889         'font-family'  : {
23890             title : "Font",
23891             style : 'fontFamily',
23892             displayField: 'display',
23893             optname : 'font-family',
23894             width: 140
23895         }
23896     },
23897     
23898     '*' : {
23899         // empty..
23900     }
23901
23902 };
23903
23904 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23905 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23906
23907 Roo.form.HtmlEditor.ToolbarContext.options = {
23908         'font-family'  : [ 
23909                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23910                 [ 'Courier New', 'Courier New'],
23911                 [ 'Tahoma', 'Tahoma'],
23912                 [ 'Times New Roman,serif', 'Times'],
23913                 [ 'Verdana','Verdana' ]
23914         ]
23915 };
23916
23917 // fixme - these need to be configurable..
23918  
23919
23920 //Roo.form.HtmlEditor.ToolbarContext.types
23921
23922
23923 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23924     
23925     tb: false,
23926     
23927     rendered: false,
23928     
23929     editor : false,
23930     editorcore : false,
23931     /**
23932      * @cfg {Object} disable  List of toolbar elements to disable
23933          
23934      */
23935     disable : false,
23936     /**
23937      * @cfg {Object} styles List of styles 
23938      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23939      *
23940      * These must be defined in the page, so they get rendered correctly..
23941      * .headline { }
23942      * TD.underline { }
23943      * 
23944      */
23945     styles : false,
23946     
23947     options: false,
23948     
23949     toolbars : false,
23950     
23951     init : function(editor)
23952     {
23953         this.editor = editor;
23954         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23955         var editorcore = this.editorcore;
23956         
23957         var fid = editorcore.frameId;
23958         var etb = this;
23959         function btn(id, toggle, handler){
23960             var xid = fid + '-'+ id ;
23961             return {
23962                 id : xid,
23963                 cmd : id,
23964                 cls : 'x-btn-icon x-edit-'+id,
23965                 enableToggle:toggle !== false,
23966                 scope: editorcore, // was editor...
23967                 handler:handler||editorcore.relayBtnCmd,
23968                 clickEvent:'mousedown',
23969                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23970                 tabIndex:-1
23971             };
23972         }
23973         // create a new element.
23974         var wdiv = editor.wrap.createChild({
23975                 tag: 'div'
23976             }, editor.wrap.dom.firstChild.nextSibling, true);
23977         
23978         // can we do this more than once??
23979         
23980          // stop form submits
23981       
23982  
23983         // disable everything...
23984         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23985         this.toolbars = {};
23986            
23987         for (var i in  ty) {
23988           
23989             this.toolbars[i] = this.buildToolbar(ty[i],i);
23990         }
23991         this.tb = this.toolbars.BODY;
23992         this.tb.el.show();
23993         this.buildFooter();
23994         this.footer.show();
23995         editor.on('hide', function( ) { this.footer.hide() }, this);
23996         editor.on('show', function( ) { this.footer.show() }, this);
23997         
23998          
23999         this.rendered = true;
24000         
24001         // the all the btns;
24002         editor.on('editorevent', this.updateToolbar, this);
24003         // other toolbars need to implement this..
24004         //editor.on('editmodechange', this.updateToolbar, this);
24005     },
24006     
24007     
24008     
24009     /**
24010      * Protected method that will not generally be called directly. It triggers
24011      * a toolbar update by reading the markup state of the current selection in the editor.
24012      *
24013      * Note you can force an update by calling on('editorevent', scope, false)
24014      */
24015     updateToolbar: function(editor,ev,sel){
24016
24017         //Roo.log(ev);
24018         // capture mouse up - this is handy for selecting images..
24019         // perhaps should go somewhere else...
24020         if(!this.editorcore.activated){
24021              this.editor.onFirstFocus();
24022             return;
24023         }
24024         
24025         
24026         
24027         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24028         // selectNode - might want to handle IE?
24029         if (ev &&
24030             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24031             ev.target && ev.target.tagName == 'IMG') {
24032             // they have click on an image...
24033             // let's see if we can change the selection...
24034             sel = ev.target;
24035          
24036               var nodeRange = sel.ownerDocument.createRange();
24037             try {
24038                 nodeRange.selectNode(sel);
24039             } catch (e) {
24040                 nodeRange.selectNodeContents(sel);
24041             }
24042             //nodeRange.collapse(true);
24043             var s = this.editorcore.win.getSelection();
24044             s.removeAllRanges();
24045             s.addRange(nodeRange);
24046         }  
24047         
24048       
24049         var updateFooter = sel ? false : true;
24050         
24051         
24052         var ans = this.editorcore.getAllAncestors();
24053         
24054         // pick
24055         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24056         
24057         if (!sel) { 
24058             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24059             sel = sel ? sel : this.editorcore.doc.body;
24060             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24061             
24062         }
24063         // pick a menu that exists..
24064         var tn = sel.tagName.toUpperCase();
24065         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24066         
24067         tn = sel.tagName.toUpperCase();
24068         
24069         var lastSel = this.tb.selectedNode;
24070         
24071         this.tb.selectedNode = sel;
24072         
24073         // if current menu does not match..
24074         
24075         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24076                 
24077             this.tb.el.hide();
24078             ///console.log("show: " + tn);
24079             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24080             this.tb.el.show();
24081             // update name
24082             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24083             
24084             
24085             // update attributes
24086             if (this.tb.fields) {
24087                 this.tb.fields.each(function(e) {
24088                     if (e.stylename) {
24089                         e.setValue(sel.style[e.stylename]);
24090                         return;
24091                     } 
24092                    e.setValue(sel.getAttribute(e.attrname));
24093                 });
24094             }
24095             
24096             var hasStyles = false;
24097             for(var i in this.styles) {
24098                 hasStyles = true;
24099                 break;
24100             }
24101             
24102             // update styles
24103             if (hasStyles) { 
24104                 var st = this.tb.fields.item(0);
24105                 
24106                 st.store.removeAll();
24107                
24108                 
24109                 var cn = sel.className.split(/\s+/);
24110                 
24111                 var avs = [];
24112                 if (this.styles['*']) {
24113                     
24114                     Roo.each(this.styles['*'], function(v) {
24115                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24116                     });
24117                 }
24118                 if (this.styles[tn]) { 
24119                     Roo.each(this.styles[tn], function(v) {
24120                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24121                     });
24122                 }
24123                 
24124                 st.store.loadData(avs);
24125                 st.collapse();
24126                 st.setValue(cn);
24127             }
24128             // flag our selected Node.
24129             this.tb.selectedNode = sel;
24130            
24131            
24132             Roo.menu.MenuMgr.hideAll();
24133
24134         }
24135         
24136         if (!updateFooter) {
24137             //this.footDisp.dom.innerHTML = ''; 
24138             return;
24139         }
24140         // update the footer
24141         //
24142         var html = '';
24143         
24144         this.footerEls = ans.reverse();
24145         Roo.each(this.footerEls, function(a,i) {
24146             if (!a) { return; }
24147             html += html.length ? ' &gt; '  :  '';
24148             
24149             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24150             
24151         });
24152        
24153         // 
24154         var sz = this.footDisp.up('td').getSize();
24155         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24156         this.footDisp.dom.style.marginLeft = '5px';
24157         
24158         this.footDisp.dom.style.overflow = 'hidden';
24159         
24160         this.footDisp.dom.innerHTML = html;
24161             
24162         //this.editorsyncValue();
24163     },
24164      
24165     
24166    
24167        
24168     // private
24169     onDestroy : function(){
24170         if(this.rendered){
24171             
24172             this.tb.items.each(function(item){
24173                 if(item.menu){
24174                     item.menu.removeAll();
24175                     if(item.menu.el){
24176                         item.menu.el.destroy();
24177                     }
24178                 }
24179                 item.destroy();
24180             });
24181              
24182         }
24183     },
24184     onFirstFocus: function() {
24185         // need to do this for all the toolbars..
24186         this.tb.items.each(function(item){
24187            item.enable();
24188         });
24189     },
24190     buildToolbar: function(tlist, nm)
24191     {
24192         var editor = this.editor;
24193         var editorcore = this.editorcore;
24194          // create a new element.
24195         var wdiv = editor.wrap.createChild({
24196                 tag: 'div'
24197             }, editor.wrap.dom.firstChild.nextSibling, true);
24198         
24199        
24200         var tb = new Roo.Toolbar(wdiv);
24201         // add the name..
24202         
24203         tb.add(nm+ ":&nbsp;");
24204         
24205         var styles = [];
24206         for(var i in this.styles) {
24207             styles.push(i);
24208         }
24209         
24210         // styles...
24211         if (styles && styles.length) {
24212             
24213             // this needs a multi-select checkbox...
24214             tb.addField( new Roo.form.ComboBox({
24215                 store: new Roo.data.SimpleStore({
24216                     id : 'val',
24217                     fields: ['val', 'selected'],
24218                     data : [] 
24219                 }),
24220                 name : '-roo-edit-className',
24221                 attrname : 'className',
24222                 displayField: 'val',
24223                 typeAhead: false,
24224                 mode: 'local',
24225                 editable : false,
24226                 triggerAction: 'all',
24227                 emptyText:'Select Style',
24228                 selectOnFocus:true,
24229                 width: 130,
24230                 listeners : {
24231                     'select': function(c, r, i) {
24232                         // initial support only for on class per el..
24233                         tb.selectedNode.className =  r ? r.get('val') : '';
24234                         editorcore.syncValue();
24235                     }
24236                 }
24237     
24238             }));
24239         }
24240         
24241         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24242         var tbops = tbc.options;
24243         
24244         for (var i in tlist) {
24245             
24246             var item = tlist[i];
24247             tb.add(item.title + ":&nbsp;");
24248             
24249             
24250             //optname == used so you can configure the options available..
24251             var opts = item.opts ? item.opts : false;
24252             if (item.optname) {
24253                 opts = tbops[item.optname];
24254            
24255             }
24256             
24257             if (opts) {
24258                 // opts == pulldown..
24259                 tb.addField( new Roo.form.ComboBox({
24260                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24261                         id : 'val',
24262                         fields: ['val', 'display'],
24263                         data : opts  
24264                     }),
24265                     name : '-roo-edit-' + i,
24266                     attrname : i,
24267                     stylename : item.style ? item.style : false,
24268                     displayField: item.displayField ? item.displayField : 'val',
24269                     valueField :  'val',
24270                     typeAhead: false,
24271                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24272                     editable : false,
24273                     triggerAction: 'all',
24274                     emptyText:'Select',
24275                     selectOnFocus:true,
24276                     width: item.width ? item.width  : 130,
24277                     listeners : {
24278                         'select': function(c, r, i) {
24279                             if (c.stylename) {
24280                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24281                                 return;
24282                             }
24283                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24284                         }
24285                     }
24286
24287                 }));
24288                 continue;
24289                     
24290                  
24291                 
24292                 tb.addField( new Roo.form.TextField({
24293                     name: i,
24294                     width: 100,
24295                     //allowBlank:false,
24296                     value: ''
24297                 }));
24298                 continue;
24299             }
24300             tb.addField( new Roo.form.TextField({
24301                 name: '-roo-edit-' + i,
24302                 attrname : i,
24303                 
24304                 width: item.width,
24305                 //allowBlank:true,
24306                 value: '',
24307                 listeners: {
24308                     'change' : function(f, nv, ov) {
24309                         tb.selectedNode.setAttribute(f.attrname, nv);
24310                         editorcore.syncValue();
24311                     }
24312                 }
24313             }));
24314              
24315         }
24316         
24317         var _this = this;
24318         
24319         if(nm == 'BODY'){
24320             tb.addSeparator();
24321         
24322             tb.addButton( {
24323                 text: 'Stylesheets',
24324
24325                 listeners : {
24326                     click : function ()
24327                     {
24328                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24329                     }
24330                 }
24331             });
24332         }
24333         
24334         tb.addFill();
24335         tb.addButton( {
24336             text: 'Remove Tag',
24337     
24338             listeners : {
24339                 click : function ()
24340                 {
24341                     // remove
24342                     // undo does not work.
24343                      
24344                     var sn = tb.selectedNode;
24345                     
24346                     var pn = sn.parentNode;
24347                     
24348                     var stn =  sn.childNodes[0];
24349                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24350                     while (sn.childNodes.length) {
24351                         var node = sn.childNodes[0];
24352                         sn.removeChild(node);
24353                         //Roo.log(node);
24354                         pn.insertBefore(node, sn);
24355                         
24356                     }
24357                     pn.removeChild(sn);
24358                     var range = editorcore.createRange();
24359         
24360                     range.setStart(stn,0);
24361                     range.setEnd(en,0); //????
24362                     //range.selectNode(sel);
24363                     
24364                     
24365                     var selection = editorcore.getSelection();
24366                     selection.removeAllRanges();
24367                     selection.addRange(range);
24368                     
24369                     
24370                     
24371                     //_this.updateToolbar(null, null, pn);
24372                     _this.updateToolbar(null, null, null);
24373                     _this.footDisp.dom.innerHTML = ''; 
24374                 }
24375             }
24376             
24377                     
24378                 
24379             
24380         });
24381         
24382         
24383         tb.el.on('click', function(e){
24384             e.preventDefault(); // what does this do?
24385         });
24386         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24387         tb.el.hide();
24388         tb.name = nm;
24389         // dont need to disable them... as they will get hidden
24390         return tb;
24391          
24392         
24393     },
24394     buildFooter : function()
24395     {
24396         
24397         var fel = this.editor.wrap.createChild();
24398         this.footer = new Roo.Toolbar(fel);
24399         // toolbar has scrolly on left / right?
24400         var footDisp= new Roo.Toolbar.Fill();
24401         var _t = this;
24402         this.footer.add(
24403             {
24404                 text : '&lt;',
24405                 xtype: 'Button',
24406                 handler : function() {
24407                     _t.footDisp.scrollTo('left',0,true)
24408                 }
24409             }
24410         );
24411         this.footer.add( footDisp );
24412         this.footer.add( 
24413             {
24414                 text : '&gt;',
24415                 xtype: 'Button',
24416                 handler : function() {
24417                     // no animation..
24418                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24419                 }
24420             }
24421         );
24422         var fel = Roo.get(footDisp.el);
24423         fel.addClass('x-editor-context');
24424         this.footDispWrap = fel; 
24425         this.footDispWrap.overflow  = 'hidden';
24426         
24427         this.footDisp = fel.createChild();
24428         this.footDispWrap.on('click', this.onContextClick, this)
24429         
24430         
24431     },
24432     onContextClick : function (ev,dom)
24433     {
24434         ev.preventDefault();
24435         var  cn = dom.className;
24436         //Roo.log(cn);
24437         if (!cn.match(/x-ed-loc-/)) {
24438             return;
24439         }
24440         var n = cn.split('-').pop();
24441         var ans = this.footerEls;
24442         var sel = ans[n];
24443         
24444          // pick
24445         var range = this.editorcore.createRange();
24446         
24447         range.selectNodeContents(sel);
24448         //range.selectNode(sel);
24449         
24450         
24451         var selection = this.editorcore.getSelection();
24452         selection.removeAllRanges();
24453         selection.addRange(range);
24454         
24455         
24456         
24457         this.updateToolbar(null, null, sel);
24458         
24459         
24460     }
24461     
24462     
24463     
24464     
24465     
24466 });
24467
24468
24469
24470
24471
24472 /*
24473  * Based on:
24474  * Ext JS Library 1.1.1
24475  * Copyright(c) 2006-2007, Ext JS, LLC.
24476  *
24477  * Originally Released Under LGPL - original licence link has changed is not relivant.
24478  *
24479  * Fork - LGPL
24480  * <script type="text/javascript">
24481  */
24482  
24483 /**
24484  * @class Roo.form.BasicForm
24485  * @extends Roo.util.Observable
24486  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24487  * @constructor
24488  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24489  * @param {Object} config Configuration options
24490  */
24491 Roo.form.BasicForm = function(el, config){
24492     this.allItems = [];
24493     this.childForms = [];
24494     Roo.apply(this, config);
24495     /*
24496      * The Roo.form.Field items in this form.
24497      * @type MixedCollection
24498      */
24499      
24500      
24501     this.items = new Roo.util.MixedCollection(false, function(o){
24502         return o.id || (o.id = Roo.id());
24503     });
24504     this.addEvents({
24505         /**
24506          * @event beforeaction
24507          * Fires before any action is performed. Return false to cancel the action.
24508          * @param {Form} this
24509          * @param {Action} action The action to be performed
24510          */
24511         beforeaction: true,
24512         /**
24513          * @event actionfailed
24514          * Fires when an action fails.
24515          * @param {Form} this
24516          * @param {Action} action The action that failed
24517          */
24518         actionfailed : true,
24519         /**
24520          * @event actioncomplete
24521          * Fires when an action is completed.
24522          * @param {Form} this
24523          * @param {Action} action The action that completed
24524          */
24525         actioncomplete : true
24526     });
24527     if(el){
24528         this.initEl(el);
24529     }
24530     Roo.form.BasicForm.superclass.constructor.call(this);
24531     
24532     Roo.form.BasicForm.popover.apply();
24533 };
24534
24535 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24536     /**
24537      * @cfg {String} method
24538      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24539      */
24540     /**
24541      * @cfg {DataReader} reader
24542      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24543      * This is optional as there is built-in support for processing JSON.
24544      */
24545     /**
24546      * @cfg {DataReader} errorReader
24547      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24548      * This is completely optional as there is built-in support for processing JSON.
24549      */
24550     /**
24551      * @cfg {String} url
24552      * The URL to use for form actions if one isn't supplied in the action options.
24553      */
24554     /**
24555      * @cfg {Boolean} fileUpload
24556      * Set to true if this form is a file upload.
24557      */
24558      
24559     /**
24560      * @cfg {Object} baseParams
24561      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24562      */
24563      /**
24564      
24565     /**
24566      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24567      */
24568     timeout: 30,
24569
24570     // private
24571     activeAction : null,
24572
24573     /**
24574      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24575      * or setValues() data instead of when the form was first created.
24576      */
24577     trackResetOnLoad : false,
24578     
24579     
24580     /**
24581      * childForms - used for multi-tab forms
24582      * @type {Array}
24583      */
24584     childForms : false,
24585     
24586     /**
24587      * allItems - full list of fields.
24588      * @type {Array}
24589      */
24590     allItems : false,
24591     
24592     /**
24593      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24594      * element by passing it or its id or mask the form itself by passing in true.
24595      * @type Mixed
24596      */
24597     waitMsgTarget : false,
24598     
24599     /**
24600      * @type Boolean
24601      */
24602     disableMask : false,
24603     
24604     /**
24605      * @cfg {Boolean} errorMask (true|false) default false
24606      */
24607     errorMask : false,
24608     
24609     /**
24610      * @cfg {Number} maskOffset Default 100
24611      */
24612     maskOffset : 100,
24613
24614     // private
24615     initEl : function(el){
24616         this.el = Roo.get(el);
24617         this.id = this.el.id || Roo.id();
24618         this.el.on('submit', this.onSubmit, this);
24619         this.el.addClass('x-form');
24620     },
24621
24622     // private
24623     onSubmit : function(e){
24624         e.stopEvent();
24625     },
24626
24627     /**
24628      * Returns true if client-side validation on the form is successful.
24629      * @return Boolean
24630      */
24631     isValid : function(){
24632         var valid = true;
24633         var target = false;
24634         this.items.each(function(f){
24635             if(f.validate()){
24636                 return;
24637             }
24638             
24639             valid = false;
24640                 
24641             if(!target && f.el.isVisible(true)){
24642                 target = f;
24643             }
24644         });
24645         
24646         if(this.errorMask && !valid){
24647             Roo.form.BasicForm.popover.mask(this, target);
24648         }
24649         
24650         return valid;
24651     },
24652     /**
24653      * Returns array of invalid form fields.
24654      * @return Array
24655      */
24656     
24657     invalidFields : function()
24658     {
24659         var ret = [];
24660         this.items.each(function(f){
24661             if(f.validate()){
24662                 return;
24663             }
24664             ret.push(f);
24665             
24666         });
24667         
24668         return ret;
24669     },
24670     
24671     
24672     /**
24673      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24674      * @return Boolean
24675      */
24676     isDirty : function(){
24677         var dirty = false;
24678         this.items.each(function(f){
24679            if(f.isDirty()){
24680                dirty = true;
24681                return false;
24682            }
24683         });
24684         return dirty;
24685     },
24686     
24687     /**
24688      * Returns true if any fields in this form have changed since their original load. (New version)
24689      * @return Boolean
24690      */
24691     
24692     hasChanged : function()
24693     {
24694         var dirty = false;
24695         this.items.each(function(f){
24696            if(f.hasChanged()){
24697                dirty = true;
24698                return false;
24699            }
24700         });
24701         return dirty;
24702         
24703     },
24704     /**
24705      * Resets all hasChanged to 'false' -
24706      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24707      * So hasChanged storage is only to be used for this purpose
24708      * @return Boolean
24709      */
24710     resetHasChanged : function()
24711     {
24712         this.items.each(function(f){
24713            f.resetHasChanged();
24714         });
24715         
24716     },
24717     
24718     
24719     /**
24720      * Performs a predefined action (submit or load) or custom actions you define on this form.
24721      * @param {String} actionName The name of the action type
24722      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24723      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24724      * accept other config options):
24725      * <pre>
24726 Property          Type             Description
24727 ----------------  ---------------  ----------------------------------------------------------------------------------
24728 url               String           The url for the action (defaults to the form's url)
24729 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24730 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24731 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24732                                    validate the form on the client (defaults to false)
24733      * </pre>
24734      * @return {BasicForm} this
24735      */
24736     doAction : function(action, options){
24737         if(typeof action == 'string'){
24738             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24739         }
24740         if(this.fireEvent('beforeaction', this, action) !== false){
24741             this.beforeAction(action);
24742             action.run.defer(100, action);
24743         }
24744         return this;
24745     },
24746
24747     /**
24748      * Shortcut to do a submit action.
24749      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24750      * @return {BasicForm} this
24751      */
24752     submit : function(options){
24753         this.doAction('submit', options);
24754         return this;
24755     },
24756
24757     /**
24758      * Shortcut to do a load action.
24759      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24760      * @return {BasicForm} this
24761      */
24762     load : function(options){
24763         this.doAction('load', options);
24764         return this;
24765     },
24766
24767     /**
24768      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24769      * @param {Record} record The record to edit
24770      * @return {BasicForm} this
24771      */
24772     updateRecord : function(record){
24773         record.beginEdit();
24774         var fs = record.fields;
24775         fs.each(function(f){
24776             var field = this.findField(f.name);
24777             if(field){
24778                 record.set(f.name, field.getValue());
24779             }
24780         }, this);
24781         record.endEdit();
24782         return this;
24783     },
24784
24785     /**
24786      * Loads an Roo.data.Record into this form.
24787      * @param {Record} record The record to load
24788      * @return {BasicForm} this
24789      */
24790     loadRecord : function(record){
24791         this.setValues(record.data);
24792         return this;
24793     },
24794
24795     // private
24796     beforeAction : function(action){
24797         var o = action.options;
24798         
24799         if(!this.disableMask) {
24800             if(this.waitMsgTarget === true){
24801                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24802             }else if(this.waitMsgTarget){
24803                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24804                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24805             }else {
24806                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24807             }
24808         }
24809         
24810          
24811     },
24812
24813     // private
24814     afterAction : function(action, success){
24815         this.activeAction = null;
24816         var o = action.options;
24817         
24818         if(!this.disableMask) {
24819             if(this.waitMsgTarget === true){
24820                 this.el.unmask();
24821             }else if(this.waitMsgTarget){
24822                 this.waitMsgTarget.unmask();
24823             }else{
24824                 Roo.MessageBox.updateProgress(1);
24825                 Roo.MessageBox.hide();
24826             }
24827         }
24828         
24829         if(success){
24830             if(o.reset){
24831                 this.reset();
24832             }
24833             Roo.callback(o.success, o.scope, [this, action]);
24834             this.fireEvent('actioncomplete', this, action);
24835             
24836         }else{
24837             
24838             // failure condition..
24839             // we have a scenario where updates need confirming.
24840             // eg. if a locking scenario exists..
24841             // we look for { errors : { needs_confirm : true }} in the response.
24842             if (
24843                 (typeof(action.result) != 'undefined')  &&
24844                 (typeof(action.result.errors) != 'undefined')  &&
24845                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24846            ){
24847                 var _t = this;
24848                 Roo.MessageBox.confirm(
24849                     "Change requires confirmation",
24850                     action.result.errorMsg,
24851                     function(r) {
24852                         if (r != 'yes') {
24853                             return;
24854                         }
24855                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24856                     }
24857                     
24858                 );
24859                 
24860                 
24861                 
24862                 return;
24863             }
24864             
24865             Roo.callback(o.failure, o.scope, [this, action]);
24866             // show an error message if no failed handler is set..
24867             if (!this.hasListener('actionfailed')) {
24868                 Roo.MessageBox.alert("Error",
24869                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24870                         action.result.errorMsg :
24871                         "Saving Failed, please check your entries or try again"
24872                 );
24873             }
24874             
24875             this.fireEvent('actionfailed', this, action);
24876         }
24877         
24878     },
24879
24880     /**
24881      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24882      * @param {String} id The value to search for
24883      * @return Field
24884      */
24885     findField : function(id){
24886         var field = this.items.get(id);
24887         if(!field){
24888             this.items.each(function(f){
24889                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24890                     field = f;
24891                     return false;
24892                 }
24893             });
24894         }
24895         return field || null;
24896     },
24897
24898     /**
24899      * Add a secondary form to this one, 
24900      * Used to provide tabbed forms. One form is primary, with hidden values 
24901      * which mirror the elements from the other forms.
24902      * 
24903      * @param {Roo.form.Form} form to add.
24904      * 
24905      */
24906     addForm : function(form)
24907     {
24908        
24909         if (this.childForms.indexOf(form) > -1) {
24910             // already added..
24911             return;
24912         }
24913         this.childForms.push(form);
24914         var n = '';
24915         Roo.each(form.allItems, function (fe) {
24916             
24917             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24918             if (this.findField(n)) { // already added..
24919                 return;
24920             }
24921             var add = new Roo.form.Hidden({
24922                 name : n
24923             });
24924             add.render(this.el);
24925             
24926             this.add( add );
24927         }, this);
24928         
24929     },
24930     /**
24931      * Mark fields in this form invalid in bulk.
24932      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24933      * @return {BasicForm} this
24934      */
24935     markInvalid : function(errors){
24936         if(errors instanceof Array){
24937             for(var i = 0, len = errors.length; i < len; i++){
24938                 var fieldError = errors[i];
24939                 var f = this.findField(fieldError.id);
24940                 if(f){
24941                     f.markInvalid(fieldError.msg);
24942                 }
24943             }
24944         }else{
24945             var field, id;
24946             for(id in errors){
24947                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24948                     field.markInvalid(errors[id]);
24949                 }
24950             }
24951         }
24952         Roo.each(this.childForms || [], function (f) {
24953             f.markInvalid(errors);
24954         });
24955         
24956         return this;
24957     },
24958
24959     /**
24960      * Set values for fields in this form in bulk.
24961      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24962      * @return {BasicForm} this
24963      */
24964     setValues : function(values){
24965         if(values instanceof Array){ // array of objects
24966             for(var i = 0, len = values.length; i < len; i++){
24967                 var v = values[i];
24968                 var f = this.findField(v.id);
24969                 if(f){
24970                     f.setValue(v.value);
24971                     if(this.trackResetOnLoad){
24972                         f.originalValue = f.getValue();
24973                     }
24974                 }
24975             }
24976         }else{ // object hash
24977             var field, id;
24978             for(id in values){
24979                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24980                     
24981                     if (field.setFromData && 
24982                         field.valueField && 
24983                         field.displayField &&
24984                         // combos' with local stores can 
24985                         // be queried via setValue()
24986                         // to set their value..
24987                         (field.store && !field.store.isLocal)
24988                         ) {
24989                         // it's a combo
24990                         var sd = { };
24991                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24992                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24993                         field.setFromData(sd);
24994                         
24995                     } else {
24996                         field.setValue(values[id]);
24997                     }
24998                     
24999                     
25000                     if(this.trackResetOnLoad){
25001                         field.originalValue = field.getValue();
25002                     }
25003                 }
25004             }
25005         }
25006         this.resetHasChanged();
25007         
25008         
25009         Roo.each(this.childForms || [], function (f) {
25010             f.setValues(values);
25011             f.resetHasChanged();
25012         });
25013                 
25014         return this;
25015     },
25016  
25017     /**
25018      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25019      * they are returned as an array.
25020      * @param {Boolean} asString
25021      * @return {Object}
25022      */
25023     getValues : function(asString){
25024         if (this.childForms) {
25025             // copy values from the child forms
25026             Roo.each(this.childForms, function (f) {
25027                 this.setValues(f.getValues());
25028             }, this);
25029         }
25030         
25031         // use formdata
25032         if (typeof(FormData) != 'undefined' && asString !== true) {
25033             // this relies on a 'recent' version of chrome apparently...
25034             try {
25035                 var fd = (new FormData(this.el.dom)).entries();
25036                 var ret = {};
25037                 var ent = fd.next();
25038                 while (!ent.done) {
25039                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25040                     ent = fd.next();
25041                 };
25042                 return ret;
25043             } catch(e) {
25044                 
25045             }
25046             
25047         }
25048         
25049         
25050         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25051         if(asString === true){
25052             return fs;
25053         }
25054         return Roo.urlDecode(fs);
25055     },
25056     
25057     /**
25058      * Returns the fields in this form as an object with key/value pairs. 
25059      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25060      * @return {Object}
25061      */
25062     getFieldValues : function(with_hidden)
25063     {
25064         if (this.childForms) {
25065             // copy values from the child forms
25066             // should this call getFieldValues - probably not as we do not currently copy
25067             // hidden fields when we generate..
25068             Roo.each(this.childForms, function (f) {
25069                 this.setValues(f.getValues());
25070             }, this);
25071         }
25072         
25073         var ret = {};
25074         this.items.each(function(f){
25075             if (!f.getName()) {
25076                 return;
25077             }
25078             var v = f.getValue();
25079             if (f.inputType =='radio') {
25080                 if (typeof(ret[f.getName()]) == 'undefined') {
25081                     ret[f.getName()] = ''; // empty..
25082                 }
25083                 
25084                 if (!f.el.dom.checked) {
25085                     return;
25086                     
25087                 }
25088                 v = f.el.dom.value;
25089                 
25090             }
25091             
25092             // not sure if this supported any more..
25093             if ((typeof(v) == 'object') && f.getRawValue) {
25094                 v = f.getRawValue() ; // dates..
25095             }
25096             // combo boxes where name != hiddenName...
25097             if (f.name != f.getName()) {
25098                 ret[f.name] = f.getRawValue();
25099             }
25100             ret[f.getName()] = v;
25101         });
25102         
25103         return ret;
25104     },
25105
25106     /**
25107      * Clears all invalid messages in this form.
25108      * @return {BasicForm} this
25109      */
25110     clearInvalid : function(){
25111         this.items.each(function(f){
25112            f.clearInvalid();
25113         });
25114         
25115         Roo.each(this.childForms || [], function (f) {
25116             f.clearInvalid();
25117         });
25118         
25119         
25120         return this;
25121     },
25122
25123     /**
25124      * Resets this form.
25125      * @return {BasicForm} this
25126      */
25127     reset : function(){
25128         this.items.each(function(f){
25129             f.reset();
25130         });
25131         
25132         Roo.each(this.childForms || [], function (f) {
25133             f.reset();
25134         });
25135         this.resetHasChanged();
25136         
25137         return this;
25138     },
25139
25140     /**
25141      * Add Roo.form components to this form.
25142      * @param {Field} field1
25143      * @param {Field} field2 (optional)
25144      * @param {Field} etc (optional)
25145      * @return {BasicForm} this
25146      */
25147     add : function(){
25148         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25149         return this;
25150     },
25151
25152
25153     /**
25154      * Removes a field from the items collection (does NOT remove its markup).
25155      * @param {Field} field
25156      * @return {BasicForm} this
25157      */
25158     remove : function(field){
25159         this.items.remove(field);
25160         return this;
25161     },
25162
25163     /**
25164      * Looks at the fields in this form, checks them for an id attribute,
25165      * and calls applyTo on the existing dom element with that id.
25166      * @return {BasicForm} this
25167      */
25168     render : function(){
25169         this.items.each(function(f){
25170             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25171                 f.applyTo(f.id);
25172             }
25173         });
25174         return this;
25175     },
25176
25177     /**
25178      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25179      * @param {Object} values
25180      * @return {BasicForm} this
25181      */
25182     applyToFields : function(o){
25183         this.items.each(function(f){
25184            Roo.apply(f, o);
25185         });
25186         return this;
25187     },
25188
25189     /**
25190      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25191      * @param {Object} values
25192      * @return {BasicForm} this
25193      */
25194     applyIfToFields : function(o){
25195         this.items.each(function(f){
25196            Roo.applyIf(f, o);
25197         });
25198         return this;
25199     }
25200 });
25201
25202 // back compat
25203 Roo.BasicForm = Roo.form.BasicForm;
25204
25205 Roo.apply(Roo.form.BasicForm, {
25206     
25207     popover : {
25208         
25209         padding : 5,
25210         
25211         isApplied : false,
25212         
25213         isMasked : false,
25214         
25215         form : false,
25216         
25217         target : false,
25218         
25219         intervalID : false,
25220         
25221         maskEl : false,
25222         
25223         apply : function()
25224         {
25225             if(this.isApplied){
25226                 return;
25227             }
25228             
25229             this.maskEl = {
25230                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25231                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25232                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25233                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25234             };
25235             
25236             this.maskEl.top.enableDisplayMode("block");
25237             this.maskEl.left.enableDisplayMode("block");
25238             this.maskEl.bottom.enableDisplayMode("block");
25239             this.maskEl.right.enableDisplayMode("block");
25240             
25241             Roo.get(document.body).on('click', function(){
25242                 this.unmask();
25243             }, this);
25244             
25245             Roo.get(document.body).on('touchstart', function(){
25246                 this.unmask();
25247             }, this);
25248             
25249             this.isApplied = true
25250         },
25251         
25252         mask : function(form, target)
25253         {
25254             this.form = form;
25255             
25256             this.target = target;
25257             
25258             if(!this.form.errorMask || !target.el){
25259                 return;
25260             }
25261             
25262             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25263             
25264             var ot = this.target.el.calcOffsetsTo(scrollable);
25265             
25266             var scrollTo = ot[1] - this.form.maskOffset;
25267             
25268             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25269             
25270             scrollable.scrollTo('top', scrollTo);
25271             
25272             var el = this.target.wrap || this.target.el;
25273             
25274             var box = el.getBox();
25275             
25276             this.maskEl.top.setStyle('position', 'absolute');
25277             this.maskEl.top.setStyle('z-index', 10000);
25278             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25279             this.maskEl.top.setLeft(0);
25280             this.maskEl.top.setTop(0);
25281             this.maskEl.top.show();
25282             
25283             this.maskEl.left.setStyle('position', 'absolute');
25284             this.maskEl.left.setStyle('z-index', 10000);
25285             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25286             this.maskEl.left.setLeft(0);
25287             this.maskEl.left.setTop(box.y - this.padding);
25288             this.maskEl.left.show();
25289
25290             this.maskEl.bottom.setStyle('position', 'absolute');
25291             this.maskEl.bottom.setStyle('z-index', 10000);
25292             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25293             this.maskEl.bottom.setLeft(0);
25294             this.maskEl.bottom.setTop(box.bottom + this.padding);
25295             this.maskEl.bottom.show();
25296
25297             this.maskEl.right.setStyle('position', 'absolute');
25298             this.maskEl.right.setStyle('z-index', 10000);
25299             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25300             this.maskEl.right.setLeft(box.right + this.padding);
25301             this.maskEl.right.setTop(box.y - this.padding);
25302             this.maskEl.right.show();
25303
25304             this.intervalID = window.setInterval(function() {
25305                 Roo.form.BasicForm.popover.unmask();
25306             }, 10000);
25307
25308             window.onwheel = function(){ return false;};
25309             
25310             (function(){ this.isMasked = true; }).defer(500, this);
25311             
25312         },
25313         
25314         unmask : function()
25315         {
25316             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25317                 return;
25318             }
25319             
25320             this.maskEl.top.setStyle('position', 'absolute');
25321             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25322             this.maskEl.top.hide();
25323
25324             this.maskEl.left.setStyle('position', 'absolute');
25325             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25326             this.maskEl.left.hide();
25327
25328             this.maskEl.bottom.setStyle('position', 'absolute');
25329             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25330             this.maskEl.bottom.hide();
25331
25332             this.maskEl.right.setStyle('position', 'absolute');
25333             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25334             this.maskEl.right.hide();
25335             
25336             window.onwheel = function(){ return true;};
25337             
25338             if(this.intervalID){
25339                 window.clearInterval(this.intervalID);
25340                 this.intervalID = false;
25341             }
25342             
25343             this.isMasked = false;
25344             
25345         }
25346         
25347     }
25348     
25349 });/*
25350  * Based on:
25351  * Ext JS Library 1.1.1
25352  * Copyright(c) 2006-2007, Ext JS, LLC.
25353  *
25354  * Originally Released Under LGPL - original licence link has changed is not relivant.
25355  *
25356  * Fork - LGPL
25357  * <script type="text/javascript">
25358  */
25359
25360 /**
25361  * @class Roo.form.Form
25362  * @extends Roo.form.BasicForm
25363  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25364  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25365  * @constructor
25366  * @param {Object} config Configuration options
25367  */
25368 Roo.form.Form = function(config){
25369     var xitems =  [];
25370     if (config.items) {
25371         xitems = config.items;
25372         delete config.items;
25373     }
25374    
25375     
25376     Roo.form.Form.superclass.constructor.call(this, null, config);
25377     this.url = this.url || this.action;
25378     if(!this.root){
25379         this.root = new Roo.form.Layout(Roo.applyIf({
25380             id: Roo.id()
25381         }, config));
25382     }
25383     this.active = this.root;
25384     /**
25385      * Array of all the buttons that have been added to this form via {@link addButton}
25386      * @type Array
25387      */
25388     this.buttons = [];
25389     this.allItems = [];
25390     this.addEvents({
25391         /**
25392          * @event clientvalidation
25393          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25394          * @param {Form} this
25395          * @param {Boolean} valid true if the form has passed client-side validation
25396          */
25397         clientvalidation: true,
25398         /**
25399          * @event rendered
25400          * Fires when the form is rendered
25401          * @param {Roo.form.Form} form
25402          */
25403         rendered : true
25404     });
25405     
25406     if (this.progressUrl) {
25407             // push a hidden field onto the list of fields..
25408             this.addxtype( {
25409                     xns: Roo.form, 
25410                     xtype : 'Hidden', 
25411                     name : 'UPLOAD_IDENTIFIER' 
25412             });
25413         }
25414         
25415     
25416     Roo.each(xitems, this.addxtype, this);
25417     
25418 };
25419
25420 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25421      /**
25422      * @cfg {Roo.Button} buttons[] buttons at bottom of form
25423      */
25424     
25425     /**
25426      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25427      */
25428     /**
25429      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25430      */
25431     /**
25432      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25433      */
25434     buttonAlign:'center',
25435
25436     /**
25437      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25438      */
25439     minButtonWidth:75,
25440
25441     /**
25442      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25443      * This property cascades to child containers if not set.
25444      */
25445     labelAlign:'left',
25446
25447     /**
25448      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25449      * fires a looping event with that state. This is required to bind buttons to the valid
25450      * state using the config value formBind:true on the button.
25451      */
25452     monitorValid : false,
25453
25454     /**
25455      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25456      */
25457     monitorPoll : 200,
25458     
25459     /**
25460      * @cfg {String} progressUrl - Url to return progress data 
25461      */
25462     
25463     progressUrl : false,
25464     /**
25465      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25466      * sending a formdata with extra parameters - eg uploaded elements.
25467      */
25468     
25469     formData : false,
25470     
25471     /**
25472      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25473      * fields are added and the column is closed. If no fields are passed the column remains open
25474      * until end() is called.
25475      * @param {Object} config The config to pass to the column
25476      * @param {Field} field1 (optional)
25477      * @param {Field} field2 (optional)
25478      * @param {Field} etc (optional)
25479      * @return Column The column container object
25480      */
25481     column : function(c){
25482         var col = new Roo.form.Column(c);
25483         this.start(col);
25484         if(arguments.length > 1){ // duplicate code required because of Opera
25485             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25486             this.end();
25487         }
25488         return col;
25489     },
25490
25491     /**
25492      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25493      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25494      * until end() is called.
25495      * @param {Object} config The config to pass to the fieldset
25496      * @param {Field} field1 (optional)
25497      * @param {Field} field2 (optional)
25498      * @param {Field} etc (optional)
25499      * @return FieldSet The fieldset container object
25500      */
25501     fieldset : function(c){
25502         var fs = new Roo.form.FieldSet(c);
25503         this.start(fs);
25504         if(arguments.length > 1){ // duplicate code required because of Opera
25505             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25506             this.end();
25507         }
25508         return fs;
25509     },
25510
25511     /**
25512      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25513      * fields are added and the container is closed. If no fields are passed the container remains open
25514      * until end() is called.
25515      * @param {Object} config The config to pass to the Layout
25516      * @param {Field} field1 (optional)
25517      * @param {Field} field2 (optional)
25518      * @param {Field} etc (optional)
25519      * @return Layout The container object
25520      */
25521     container : function(c){
25522         var l = new Roo.form.Layout(c);
25523         this.start(l);
25524         if(arguments.length > 1){ // duplicate code required because of Opera
25525             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25526             this.end();
25527         }
25528         return l;
25529     },
25530
25531     /**
25532      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25533      * @param {Object} container A Roo.form.Layout or subclass of Layout
25534      * @return {Form} this
25535      */
25536     start : function(c){
25537         // cascade label info
25538         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25539         this.active.stack.push(c);
25540         c.ownerCt = this.active;
25541         this.active = c;
25542         return this;
25543     },
25544
25545     /**
25546      * Closes the current open container
25547      * @return {Form} this
25548      */
25549     end : function(){
25550         if(this.active == this.root){
25551             return this;
25552         }
25553         this.active = this.active.ownerCt;
25554         return this;
25555     },
25556
25557     /**
25558      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25559      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25560      * as the label of the field.
25561      * @param {Field} field1
25562      * @param {Field} field2 (optional)
25563      * @param {Field} etc. (optional)
25564      * @return {Form} this
25565      */
25566     add : function(){
25567         this.active.stack.push.apply(this.active.stack, arguments);
25568         this.allItems.push.apply(this.allItems,arguments);
25569         var r = [];
25570         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25571             if(a[i].isFormField){
25572                 r.push(a[i]);
25573             }
25574         }
25575         if(r.length > 0){
25576             Roo.form.Form.superclass.add.apply(this, r);
25577         }
25578         return this;
25579     },
25580     
25581
25582     
25583     
25584     
25585      /**
25586      * Find any element that has been added to a form, using it's ID or name
25587      * This can include framesets, columns etc. along with regular fields..
25588      * @param {String} id - id or name to find.
25589      
25590      * @return {Element} e - or false if nothing found.
25591      */
25592     findbyId : function(id)
25593     {
25594         var ret = false;
25595         if (!id) {
25596             return ret;
25597         }
25598         Roo.each(this.allItems, function(f){
25599             if (f.id == id || f.name == id ){
25600                 ret = f;
25601                 return false;
25602             }
25603         });
25604         return ret;
25605     },
25606
25607     
25608     
25609     /**
25610      * Render this form into the passed container. This should only be called once!
25611      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25612      * @return {Form} this
25613      */
25614     render : function(ct)
25615     {
25616         
25617         
25618         
25619         ct = Roo.get(ct);
25620         var o = this.autoCreate || {
25621             tag: 'form',
25622             method : this.method || 'POST',
25623             id : this.id || Roo.id()
25624         };
25625         this.initEl(ct.createChild(o));
25626
25627         this.root.render(this.el);
25628         
25629        
25630              
25631         this.items.each(function(f){
25632             f.render('x-form-el-'+f.id);
25633         });
25634
25635         if(this.buttons.length > 0){
25636             // tables are required to maintain order and for correct IE layout
25637             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25638                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25639                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25640             }}, null, true);
25641             var tr = tb.getElementsByTagName('tr')[0];
25642             for(var i = 0, len = this.buttons.length; i < len; i++) {
25643                 var b = this.buttons[i];
25644                 var td = document.createElement('td');
25645                 td.className = 'x-form-btn-td';
25646                 b.render(tr.appendChild(td));
25647             }
25648         }
25649         if(this.monitorValid){ // initialize after render
25650             this.startMonitoring();
25651         }
25652         this.fireEvent('rendered', this);
25653         return this;
25654     },
25655
25656     /**
25657      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25658      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25659      * object or a valid Roo.DomHelper element config
25660      * @param {Function} handler The function called when the button is clicked
25661      * @param {Object} scope (optional) The scope of the handler function
25662      * @return {Roo.Button}
25663      */
25664     addButton : function(config, handler, scope){
25665         var bc = {
25666             handler: handler,
25667             scope: scope,
25668             minWidth: this.minButtonWidth,
25669             hideParent:true
25670         };
25671         if(typeof config == "string"){
25672             bc.text = config;
25673         }else{
25674             Roo.apply(bc, config);
25675         }
25676         var btn = new Roo.Button(null, bc);
25677         this.buttons.push(btn);
25678         return btn;
25679     },
25680
25681      /**
25682      * Adds a series of form elements (using the xtype property as the factory method.
25683      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25684      * @param {Object} config 
25685      */
25686     
25687     addxtype : function()
25688     {
25689         var ar = Array.prototype.slice.call(arguments, 0);
25690         var ret = false;
25691         for(var i = 0; i < ar.length; i++) {
25692             if (!ar[i]) {
25693                 continue; // skip -- if this happends something invalid got sent, we 
25694                 // should ignore it, as basically that interface element will not show up
25695                 // and that should be pretty obvious!!
25696             }
25697             
25698             if (Roo.form[ar[i].xtype]) {
25699                 ar[i].form = this;
25700                 var fe = Roo.factory(ar[i], Roo.form);
25701                 if (!ret) {
25702                     ret = fe;
25703                 }
25704                 fe.form = this;
25705                 if (fe.store) {
25706                     fe.store.form = this;
25707                 }
25708                 if (fe.isLayout) {  
25709                          
25710                     this.start(fe);
25711                     this.allItems.push(fe);
25712                     if (fe.items && fe.addxtype) {
25713                         fe.addxtype.apply(fe, fe.items);
25714                         delete fe.items;
25715                     }
25716                      this.end();
25717                     continue;
25718                 }
25719                 
25720                 
25721                  
25722                 this.add(fe);
25723               //  console.log('adding ' + ar[i].xtype);
25724             }
25725             if (ar[i].xtype == 'Button') {  
25726                 //console.log('adding button');
25727                 //console.log(ar[i]);
25728                 this.addButton(ar[i]);
25729                 this.allItems.push(fe);
25730                 continue;
25731             }
25732             
25733             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25734                 alert('end is not supported on xtype any more, use items');
25735             //    this.end();
25736             //    //console.log('adding end');
25737             }
25738             
25739         }
25740         return ret;
25741     },
25742     
25743     /**
25744      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25745      * option "monitorValid"
25746      */
25747     startMonitoring : function(){
25748         if(!this.bound){
25749             this.bound = true;
25750             Roo.TaskMgr.start({
25751                 run : this.bindHandler,
25752                 interval : this.monitorPoll || 200,
25753                 scope: this
25754             });
25755         }
25756     },
25757
25758     /**
25759      * Stops monitoring of the valid state of this form
25760      */
25761     stopMonitoring : function(){
25762         this.bound = false;
25763     },
25764
25765     // private
25766     bindHandler : function(){
25767         if(!this.bound){
25768             return false; // stops binding
25769         }
25770         var valid = true;
25771         this.items.each(function(f){
25772             if(!f.isValid(true)){
25773                 valid = false;
25774                 return false;
25775             }
25776         });
25777         for(var i = 0, len = this.buttons.length; i < len; i++){
25778             var btn = this.buttons[i];
25779             if(btn.formBind === true && btn.disabled === valid){
25780                 btn.setDisabled(!valid);
25781             }
25782         }
25783         this.fireEvent('clientvalidation', this, valid);
25784     }
25785     
25786     
25787     
25788     
25789     
25790     
25791     
25792     
25793 });
25794
25795
25796 // back compat
25797 Roo.Form = Roo.form.Form;
25798 /*
25799  * Based on:
25800  * Ext JS Library 1.1.1
25801  * Copyright(c) 2006-2007, Ext JS, LLC.
25802  *
25803  * Originally Released Under LGPL - original licence link has changed is not relivant.
25804  *
25805  * Fork - LGPL
25806  * <script type="text/javascript">
25807  */
25808
25809 // as we use this in bootstrap.
25810 Roo.namespace('Roo.form');
25811  /**
25812  * @class Roo.form.Action
25813  * Internal Class used to handle form actions
25814  * @constructor
25815  * @param {Roo.form.BasicForm} el The form element or its id
25816  * @param {Object} config Configuration options
25817  */
25818
25819  
25820  
25821 // define the action interface
25822 Roo.form.Action = function(form, options){
25823     this.form = form;
25824     this.options = options || {};
25825 };
25826 /**
25827  * Client Validation Failed
25828  * @const 
25829  */
25830 Roo.form.Action.CLIENT_INVALID = 'client';
25831 /**
25832  * Server Validation Failed
25833  * @const 
25834  */
25835 Roo.form.Action.SERVER_INVALID = 'server';
25836  /**
25837  * Connect to Server Failed
25838  * @const 
25839  */
25840 Roo.form.Action.CONNECT_FAILURE = 'connect';
25841 /**
25842  * Reading Data from Server Failed
25843  * @const 
25844  */
25845 Roo.form.Action.LOAD_FAILURE = 'load';
25846
25847 Roo.form.Action.prototype = {
25848     type : 'default',
25849     failureType : undefined,
25850     response : undefined,
25851     result : undefined,
25852
25853     // interface method
25854     run : function(options){
25855
25856     },
25857
25858     // interface method
25859     success : function(response){
25860
25861     },
25862
25863     // interface method
25864     handleResponse : function(response){
25865
25866     },
25867
25868     // default connection failure
25869     failure : function(response){
25870         
25871         this.response = response;
25872         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25873         this.form.afterAction(this, false);
25874     },
25875
25876     processResponse : function(response){
25877         this.response = response;
25878         if(!response.responseText){
25879             return true;
25880         }
25881         this.result = this.handleResponse(response);
25882         return this.result;
25883     },
25884
25885     // utility functions used internally
25886     getUrl : function(appendParams){
25887         var url = this.options.url || this.form.url || this.form.el.dom.action;
25888         if(appendParams){
25889             var p = this.getParams();
25890             if(p){
25891                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25892             }
25893         }
25894         return url;
25895     },
25896
25897     getMethod : function(){
25898         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25899     },
25900
25901     getParams : function(){
25902         var bp = this.form.baseParams;
25903         var p = this.options.params;
25904         if(p){
25905             if(typeof p == "object"){
25906                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25907             }else if(typeof p == 'string' && bp){
25908                 p += '&' + Roo.urlEncode(bp);
25909             }
25910         }else if(bp){
25911             p = Roo.urlEncode(bp);
25912         }
25913         return p;
25914     },
25915
25916     createCallback : function(){
25917         return {
25918             success: this.success,
25919             failure: this.failure,
25920             scope: this,
25921             timeout: (this.form.timeout*1000),
25922             upload: this.form.fileUpload ? this.success : undefined
25923         };
25924     }
25925 };
25926
25927 Roo.form.Action.Submit = function(form, options){
25928     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25929 };
25930
25931 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25932     type : 'submit',
25933
25934     haveProgress : false,
25935     uploadComplete : false,
25936     
25937     // uploadProgress indicator.
25938     uploadProgress : function()
25939     {
25940         if (!this.form.progressUrl) {
25941             return;
25942         }
25943         
25944         if (!this.haveProgress) {
25945             Roo.MessageBox.progress("Uploading", "Uploading");
25946         }
25947         if (this.uploadComplete) {
25948            Roo.MessageBox.hide();
25949            return;
25950         }
25951         
25952         this.haveProgress = true;
25953    
25954         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25955         
25956         var c = new Roo.data.Connection();
25957         c.request({
25958             url : this.form.progressUrl,
25959             params: {
25960                 id : uid
25961             },
25962             method: 'GET',
25963             success : function(req){
25964                //console.log(data);
25965                 var rdata = false;
25966                 var edata;
25967                 try  {
25968                    rdata = Roo.decode(req.responseText)
25969                 } catch (e) {
25970                     Roo.log("Invalid data from server..");
25971                     Roo.log(edata);
25972                     return;
25973                 }
25974                 if (!rdata || !rdata.success) {
25975                     Roo.log(rdata);
25976                     Roo.MessageBox.alert(Roo.encode(rdata));
25977                     return;
25978                 }
25979                 var data = rdata.data;
25980                 
25981                 if (this.uploadComplete) {
25982                    Roo.MessageBox.hide();
25983                    return;
25984                 }
25985                    
25986                 if (data){
25987                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25988                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25989                     );
25990                 }
25991                 this.uploadProgress.defer(2000,this);
25992             },
25993        
25994             failure: function(data) {
25995                 Roo.log('progress url failed ');
25996                 Roo.log(data);
25997             },
25998             scope : this
25999         });
26000            
26001     },
26002     
26003     
26004     run : function()
26005     {
26006         // run get Values on the form, so it syncs any secondary forms.
26007         this.form.getValues();
26008         
26009         var o = this.options;
26010         var method = this.getMethod();
26011         var isPost = method == 'POST';
26012         if(o.clientValidation === false || this.form.isValid()){
26013             
26014             if (this.form.progressUrl) {
26015                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26016                     (new Date() * 1) + '' + Math.random());
26017                     
26018             } 
26019             
26020             
26021             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26022                 form:this.form.el.dom,
26023                 url:this.getUrl(!isPost),
26024                 method: method,
26025                 params:isPost ? this.getParams() : null,
26026                 isUpload: this.form.fileUpload,
26027                 formData : this.form.formData
26028             }));
26029             
26030             this.uploadProgress();
26031
26032         }else if (o.clientValidation !== false){ // client validation failed
26033             this.failureType = Roo.form.Action.CLIENT_INVALID;
26034             this.form.afterAction(this, false);
26035         }
26036     },
26037
26038     success : function(response)
26039     {
26040         this.uploadComplete= true;
26041         if (this.haveProgress) {
26042             Roo.MessageBox.hide();
26043         }
26044         
26045         
26046         var result = this.processResponse(response);
26047         if(result === true || result.success){
26048             this.form.afterAction(this, true);
26049             return;
26050         }
26051         if(result.errors){
26052             this.form.markInvalid(result.errors);
26053             this.failureType = Roo.form.Action.SERVER_INVALID;
26054         }
26055         this.form.afterAction(this, false);
26056     },
26057     failure : function(response)
26058     {
26059         this.uploadComplete= true;
26060         if (this.haveProgress) {
26061             Roo.MessageBox.hide();
26062         }
26063         
26064         this.response = response;
26065         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26066         this.form.afterAction(this, false);
26067     },
26068     
26069     handleResponse : function(response){
26070         if(this.form.errorReader){
26071             var rs = this.form.errorReader.read(response);
26072             var errors = [];
26073             if(rs.records){
26074                 for(var i = 0, len = rs.records.length; i < len; i++) {
26075                     var r = rs.records[i];
26076                     errors[i] = r.data;
26077                 }
26078             }
26079             if(errors.length < 1){
26080                 errors = null;
26081             }
26082             return {
26083                 success : rs.success,
26084                 errors : errors
26085             };
26086         }
26087         var ret = false;
26088         try {
26089             ret = Roo.decode(response.responseText);
26090         } catch (e) {
26091             ret = {
26092                 success: false,
26093                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26094                 errors : []
26095             };
26096         }
26097         return ret;
26098         
26099     }
26100 });
26101
26102
26103 Roo.form.Action.Load = function(form, options){
26104     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26105     this.reader = this.form.reader;
26106 };
26107
26108 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26109     type : 'load',
26110
26111     run : function(){
26112         
26113         Roo.Ajax.request(Roo.apply(
26114                 this.createCallback(), {
26115                     method:this.getMethod(),
26116                     url:this.getUrl(false),
26117                     params:this.getParams()
26118         }));
26119     },
26120
26121     success : function(response){
26122         
26123         var result = this.processResponse(response);
26124         if(result === true || !result.success || !result.data){
26125             this.failureType = Roo.form.Action.LOAD_FAILURE;
26126             this.form.afterAction(this, false);
26127             return;
26128         }
26129         this.form.clearInvalid();
26130         this.form.setValues(result.data);
26131         this.form.afterAction(this, true);
26132     },
26133
26134     handleResponse : function(response){
26135         if(this.form.reader){
26136             var rs = this.form.reader.read(response);
26137             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26138             return {
26139                 success : rs.success,
26140                 data : data
26141             };
26142         }
26143         return Roo.decode(response.responseText);
26144     }
26145 });
26146
26147 Roo.form.Action.ACTION_TYPES = {
26148     'load' : Roo.form.Action.Load,
26149     'submit' : Roo.form.Action.Submit
26150 };/*
26151  * Based on:
26152  * Ext JS Library 1.1.1
26153  * Copyright(c) 2006-2007, Ext JS, LLC.
26154  *
26155  * Originally Released Under LGPL - original licence link has changed is not relivant.
26156  *
26157  * Fork - LGPL
26158  * <script type="text/javascript">
26159  */
26160  
26161 /**
26162  * @class Roo.form.Layout
26163  * @extends Roo.Component
26164  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26165  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26166  * @constructor
26167  * @param {Object} config Configuration options
26168  */
26169 Roo.form.Layout = function(config){
26170     var xitems = [];
26171     if (config.items) {
26172         xitems = config.items;
26173         delete config.items;
26174     }
26175     Roo.form.Layout.superclass.constructor.call(this, config);
26176     this.stack = [];
26177     Roo.each(xitems, this.addxtype, this);
26178      
26179 };
26180
26181 Roo.extend(Roo.form.Layout, Roo.Component, {
26182     /**
26183      * @cfg {String/Object} autoCreate
26184      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26185      */
26186     /**
26187      * @cfg {String/Object/Function} style
26188      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26189      * a function which returns such a specification.
26190      */
26191     /**
26192      * @cfg {String} labelAlign
26193      * Valid values are "left," "top" and "right" (defaults to "left")
26194      */
26195     /**
26196      * @cfg {Number} labelWidth
26197      * Fixed width in pixels of all field labels (defaults to undefined)
26198      */
26199     /**
26200      * @cfg {Boolean} clear
26201      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26202      */
26203     clear : true,
26204     /**
26205      * @cfg {String} labelSeparator
26206      * The separator to use after field labels (defaults to ':')
26207      */
26208     labelSeparator : ':',
26209     /**
26210      * @cfg {Boolean} hideLabels
26211      * True to suppress the display of field labels in this layout (defaults to false)
26212      */
26213     hideLabels : false,
26214
26215     // private
26216     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26217     
26218     isLayout : true,
26219     
26220     // private
26221     onRender : function(ct, position){
26222         if(this.el){ // from markup
26223             this.el = Roo.get(this.el);
26224         }else {  // generate
26225             var cfg = this.getAutoCreate();
26226             this.el = ct.createChild(cfg, position);
26227         }
26228         if(this.style){
26229             this.el.applyStyles(this.style);
26230         }
26231         if(this.labelAlign){
26232             this.el.addClass('x-form-label-'+this.labelAlign);
26233         }
26234         if(this.hideLabels){
26235             this.labelStyle = "display:none";
26236             this.elementStyle = "padding-left:0;";
26237         }else{
26238             if(typeof this.labelWidth == 'number'){
26239                 this.labelStyle = "width:"+this.labelWidth+"px;";
26240                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26241             }
26242             if(this.labelAlign == 'top'){
26243                 this.labelStyle = "width:auto;";
26244                 this.elementStyle = "padding-left:0;";
26245             }
26246         }
26247         var stack = this.stack;
26248         var slen = stack.length;
26249         if(slen > 0){
26250             if(!this.fieldTpl){
26251                 var t = new Roo.Template(
26252                     '<div class="x-form-item {5}">',
26253                         '<label for="{0}" style="{2}">{1}{4}</label>',
26254                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26255                         '</div>',
26256                     '</div><div class="x-form-clear-left"></div>'
26257                 );
26258                 t.disableFormats = true;
26259                 t.compile();
26260                 Roo.form.Layout.prototype.fieldTpl = t;
26261             }
26262             for(var i = 0; i < slen; i++) {
26263                 if(stack[i].isFormField){
26264                     this.renderField(stack[i]);
26265                 }else{
26266                     this.renderComponent(stack[i]);
26267                 }
26268             }
26269         }
26270         if(this.clear){
26271             this.el.createChild({cls:'x-form-clear'});
26272         }
26273     },
26274
26275     // private
26276     renderField : function(f){
26277         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26278                f.id, //0
26279                f.fieldLabel, //1
26280                f.labelStyle||this.labelStyle||'', //2
26281                this.elementStyle||'', //3
26282                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26283                f.itemCls||this.itemCls||''  //5
26284        ], true).getPrevSibling());
26285     },
26286
26287     // private
26288     renderComponent : function(c){
26289         c.render(c.isLayout ? this.el : this.el.createChild());    
26290     },
26291     /**
26292      * Adds a object form elements (using the xtype property as the factory method.)
26293      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26294      * @param {Object} config 
26295      */
26296     addxtype : function(o)
26297     {
26298         // create the lement.
26299         o.form = this.form;
26300         var fe = Roo.factory(o, Roo.form);
26301         this.form.allItems.push(fe);
26302         this.stack.push(fe);
26303         
26304         if (fe.isFormField) {
26305             this.form.items.add(fe);
26306         }
26307          
26308         return fe;
26309     }
26310 });
26311
26312 /**
26313  * @class Roo.form.Column
26314  * @extends Roo.form.Layout
26315  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26316  * @constructor
26317  * @param {Object} config Configuration options
26318  */
26319 Roo.form.Column = function(config){
26320     Roo.form.Column.superclass.constructor.call(this, config);
26321 };
26322
26323 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26324     /**
26325      * @cfg {Number/String} width
26326      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26327      */
26328     /**
26329      * @cfg {String/Object} autoCreate
26330      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26331      */
26332
26333     // private
26334     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26335
26336     // private
26337     onRender : function(ct, position){
26338         Roo.form.Column.superclass.onRender.call(this, ct, position);
26339         if(this.width){
26340             this.el.setWidth(this.width);
26341         }
26342     }
26343 });
26344
26345
26346 /**
26347  * @class Roo.form.Row
26348  * @extends Roo.form.Layout
26349  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26350  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26351  * @constructor
26352  * @param {Object} config Configuration options
26353  */
26354
26355  
26356 Roo.form.Row = function(config){
26357     Roo.form.Row.superclass.constructor.call(this, config);
26358 };
26359  
26360 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26361       /**
26362      * @cfg {Number/String} width
26363      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26364      */
26365     /**
26366      * @cfg {Number/String} height
26367      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26368      */
26369     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26370     
26371     padWidth : 20,
26372     // private
26373     onRender : function(ct, position){
26374         //console.log('row render');
26375         if(!this.rowTpl){
26376             var t = new Roo.Template(
26377                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26378                     '<label for="{0}" style="{2}">{1}{4}</label>',
26379                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26380                     '</div>',
26381                 '</div>'
26382             );
26383             t.disableFormats = true;
26384             t.compile();
26385             Roo.form.Layout.prototype.rowTpl = t;
26386         }
26387         this.fieldTpl = this.rowTpl;
26388         
26389         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26390         var labelWidth = 100;
26391         
26392         if ((this.labelAlign != 'top')) {
26393             if (typeof this.labelWidth == 'number') {
26394                 labelWidth = this.labelWidth
26395             }
26396             this.padWidth =  20 + labelWidth;
26397             
26398         }
26399         
26400         Roo.form.Column.superclass.onRender.call(this, ct, position);
26401         if(this.width){
26402             this.el.setWidth(this.width);
26403         }
26404         if(this.height){
26405             this.el.setHeight(this.height);
26406         }
26407     },
26408     
26409     // private
26410     renderField : function(f){
26411         f.fieldEl = this.fieldTpl.append(this.el, [
26412                f.id, f.fieldLabel,
26413                f.labelStyle||this.labelStyle||'',
26414                this.elementStyle||'',
26415                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26416                f.itemCls||this.itemCls||'',
26417                f.width ? f.width + this.padWidth : 160 + this.padWidth
26418        ],true);
26419     }
26420 });
26421  
26422
26423 /**
26424  * @class Roo.form.FieldSet
26425  * @extends Roo.form.Layout
26426  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26427  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26428  * @constructor
26429  * @param {Object} config Configuration options
26430  */
26431 Roo.form.FieldSet = function(config){
26432     Roo.form.FieldSet.superclass.constructor.call(this, config);
26433 };
26434
26435 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26436     /**
26437      * @cfg {String} legend
26438      * The text to display as the legend for the FieldSet (defaults to '')
26439      */
26440     /**
26441      * @cfg {String/Object} autoCreate
26442      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26443      */
26444
26445     // private
26446     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26447
26448     // private
26449     onRender : function(ct, position){
26450         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26451         if(this.legend){
26452             this.setLegend(this.legend);
26453         }
26454     },
26455
26456     // private
26457     setLegend : function(text){
26458         if(this.rendered){
26459             this.el.child('legend').update(text);
26460         }
26461     }
26462 });/*
26463  * Based on:
26464  * Ext JS Library 1.1.1
26465  * Copyright(c) 2006-2007, Ext JS, LLC.
26466  *
26467  * Originally Released Under LGPL - original licence link has changed is not relivant.
26468  *
26469  * Fork - LGPL
26470  * <script type="text/javascript">
26471  */
26472 /**
26473  * @class Roo.form.VTypes
26474  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26475  * @singleton
26476  */
26477 Roo.form.VTypes = function(){
26478     // closure these in so they are only created once.
26479     var alpha = /^[a-zA-Z_]+$/;
26480     var alphanum = /^[a-zA-Z0-9_]+$/;
26481     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26482     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26483
26484     // All these messages and functions are configurable
26485     return {
26486         /**
26487          * The function used to validate email addresses
26488          * @param {String} value The email address
26489          */
26490         'email' : function(v){
26491             return email.test(v);
26492         },
26493         /**
26494          * The error text to display when the email validation function returns false
26495          * @type String
26496          */
26497         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26498         /**
26499          * The keystroke filter mask to be applied on email input
26500          * @type RegExp
26501          */
26502         'emailMask' : /[a-z0-9_\.\-@]/i,
26503
26504         /**
26505          * The function used to validate URLs
26506          * @param {String} value The URL
26507          */
26508         'url' : function(v){
26509             return url.test(v);
26510         },
26511         /**
26512          * The error text to display when the url validation function returns false
26513          * @type String
26514          */
26515         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26516         
26517         /**
26518          * The function used to validate alpha values
26519          * @param {String} value The value
26520          */
26521         'alpha' : function(v){
26522             return alpha.test(v);
26523         },
26524         /**
26525          * The error text to display when the alpha validation function returns false
26526          * @type String
26527          */
26528         'alphaText' : 'This field should only contain letters and _',
26529         /**
26530          * The keystroke filter mask to be applied on alpha input
26531          * @type RegExp
26532          */
26533         'alphaMask' : /[a-z_]/i,
26534
26535         /**
26536          * The function used to validate alphanumeric values
26537          * @param {String} value The value
26538          */
26539         'alphanum' : function(v){
26540             return alphanum.test(v);
26541         },
26542         /**
26543          * The error text to display when the alphanumeric validation function returns false
26544          * @type String
26545          */
26546         'alphanumText' : 'This field should only contain letters, numbers and _',
26547         /**
26548          * The keystroke filter mask to be applied on alphanumeric input
26549          * @type RegExp
26550          */
26551         'alphanumMask' : /[a-z0-9_]/i
26552     };
26553 }();//<script type="text/javascript">
26554
26555 /**
26556  * @class Roo.form.FCKeditor
26557  * @extends Roo.form.TextArea
26558  * Wrapper around the FCKEditor http://www.fckeditor.net
26559  * @constructor
26560  * Creates a new FCKeditor
26561  * @param {Object} config Configuration options
26562  */
26563 Roo.form.FCKeditor = function(config){
26564     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26565     this.addEvents({
26566          /**
26567          * @event editorinit
26568          * Fired when the editor is initialized - you can add extra handlers here..
26569          * @param {FCKeditor} this
26570          * @param {Object} the FCK object.
26571          */
26572         editorinit : true
26573     });
26574     
26575     
26576 };
26577 Roo.form.FCKeditor.editors = { };
26578 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26579 {
26580     //defaultAutoCreate : {
26581     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26582     //},
26583     // private
26584     /**
26585      * @cfg {Object} fck options - see fck manual for details.
26586      */
26587     fckconfig : false,
26588     
26589     /**
26590      * @cfg {Object} fck toolbar set (Basic or Default)
26591      */
26592     toolbarSet : 'Basic',
26593     /**
26594      * @cfg {Object} fck BasePath
26595      */ 
26596     basePath : '/fckeditor/',
26597     
26598     
26599     frame : false,
26600     
26601     value : '',
26602     
26603    
26604     onRender : function(ct, position)
26605     {
26606         if(!this.el){
26607             this.defaultAutoCreate = {
26608                 tag: "textarea",
26609                 style:"width:300px;height:60px;",
26610                 autocomplete: "new-password"
26611             };
26612         }
26613         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26614         /*
26615         if(this.grow){
26616             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26617             if(this.preventScrollbars){
26618                 this.el.setStyle("overflow", "hidden");
26619             }
26620             this.el.setHeight(this.growMin);
26621         }
26622         */
26623         //console.log('onrender' + this.getId() );
26624         Roo.form.FCKeditor.editors[this.getId()] = this;
26625          
26626
26627         this.replaceTextarea() ;
26628         
26629     },
26630     
26631     getEditor : function() {
26632         return this.fckEditor;
26633     },
26634     /**
26635      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26636      * @param {Mixed} value The value to set
26637      */
26638     
26639     
26640     setValue : function(value)
26641     {
26642         //console.log('setValue: ' + value);
26643         
26644         if(typeof(value) == 'undefined') { // not sure why this is happending...
26645             return;
26646         }
26647         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26648         
26649         //if(!this.el || !this.getEditor()) {
26650         //    this.value = value;
26651             //this.setValue.defer(100,this,[value]);    
26652         //    return;
26653         //} 
26654         
26655         if(!this.getEditor()) {
26656             return;
26657         }
26658         
26659         this.getEditor().SetData(value);
26660         
26661         //
26662
26663     },
26664
26665     /**
26666      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26667      * @return {Mixed} value The field value
26668      */
26669     getValue : function()
26670     {
26671         
26672         if (this.frame && this.frame.dom.style.display == 'none') {
26673             return Roo.form.FCKeditor.superclass.getValue.call(this);
26674         }
26675         
26676         if(!this.el || !this.getEditor()) {
26677            
26678            // this.getValue.defer(100,this); 
26679             return this.value;
26680         }
26681        
26682         
26683         var value=this.getEditor().GetData();
26684         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26685         return Roo.form.FCKeditor.superclass.getValue.call(this);
26686         
26687
26688     },
26689
26690     /**
26691      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26692      * @return {Mixed} value The field value
26693      */
26694     getRawValue : function()
26695     {
26696         if (this.frame && this.frame.dom.style.display == 'none') {
26697             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26698         }
26699         
26700         if(!this.el || !this.getEditor()) {
26701             //this.getRawValue.defer(100,this); 
26702             return this.value;
26703             return;
26704         }
26705         
26706         
26707         
26708         var value=this.getEditor().GetData();
26709         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26710         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26711          
26712     },
26713     
26714     setSize : function(w,h) {
26715         
26716         
26717         
26718         //if (this.frame && this.frame.dom.style.display == 'none') {
26719         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26720         //    return;
26721         //}
26722         //if(!this.el || !this.getEditor()) {
26723         //    this.setSize.defer(100,this, [w,h]); 
26724         //    return;
26725         //}
26726         
26727         
26728         
26729         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26730         
26731         this.frame.dom.setAttribute('width', w);
26732         this.frame.dom.setAttribute('height', h);
26733         this.frame.setSize(w,h);
26734         
26735     },
26736     
26737     toggleSourceEdit : function(value) {
26738         
26739       
26740          
26741         this.el.dom.style.display = value ? '' : 'none';
26742         this.frame.dom.style.display = value ?  'none' : '';
26743         
26744     },
26745     
26746     
26747     focus: function(tag)
26748     {
26749         if (this.frame.dom.style.display == 'none') {
26750             return Roo.form.FCKeditor.superclass.focus.call(this);
26751         }
26752         if(!this.el || !this.getEditor()) {
26753             this.focus.defer(100,this, [tag]); 
26754             return;
26755         }
26756         
26757         
26758         
26759         
26760         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26761         this.getEditor().Focus();
26762         if (tgs.length) {
26763             if (!this.getEditor().Selection.GetSelection()) {
26764                 this.focus.defer(100,this, [tag]); 
26765                 return;
26766             }
26767             
26768             
26769             var r = this.getEditor().EditorDocument.createRange();
26770             r.setStart(tgs[0],0);
26771             r.setEnd(tgs[0],0);
26772             this.getEditor().Selection.GetSelection().removeAllRanges();
26773             this.getEditor().Selection.GetSelection().addRange(r);
26774             this.getEditor().Focus();
26775         }
26776         
26777     },
26778     
26779     
26780     
26781     replaceTextarea : function()
26782     {
26783         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26784             return ;
26785         }
26786         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26787         //{
26788             // We must check the elements firstly using the Id and then the name.
26789         var oTextarea = document.getElementById( this.getId() );
26790         
26791         var colElementsByName = document.getElementsByName( this.getId() ) ;
26792          
26793         oTextarea.style.display = 'none' ;
26794
26795         if ( oTextarea.tabIndex ) {            
26796             this.TabIndex = oTextarea.tabIndex ;
26797         }
26798         
26799         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26800         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26801         this.frame = Roo.get(this.getId() + '___Frame')
26802     },
26803     
26804     _getConfigHtml : function()
26805     {
26806         var sConfig = '' ;
26807
26808         for ( var o in this.fckconfig ) {
26809             sConfig += sConfig.length > 0  ? '&amp;' : '';
26810             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26811         }
26812
26813         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26814     },
26815     
26816     
26817     _getIFrameHtml : function()
26818     {
26819         var sFile = 'fckeditor.html' ;
26820         /* no idea what this is about..
26821         try
26822         {
26823             if ( (/fcksource=true/i).test( window.top.location.search ) )
26824                 sFile = 'fckeditor.original.html' ;
26825         }
26826         catch (e) { 
26827         */
26828
26829         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26830         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26831         
26832         
26833         var html = '<iframe id="' + this.getId() +
26834             '___Frame" src="' + sLink +
26835             '" width="' + this.width +
26836             '" height="' + this.height + '"' +
26837             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26838             ' frameborder="0" scrolling="no"></iframe>' ;
26839
26840         return html ;
26841     },
26842     
26843     _insertHtmlBefore : function( html, element )
26844     {
26845         if ( element.insertAdjacentHTML )       {
26846             // IE
26847             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26848         } else { // Gecko
26849             var oRange = document.createRange() ;
26850             oRange.setStartBefore( element ) ;
26851             var oFragment = oRange.createContextualFragment( html );
26852             element.parentNode.insertBefore( oFragment, element ) ;
26853         }
26854     }
26855     
26856     
26857   
26858     
26859     
26860     
26861     
26862
26863 });
26864
26865 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26866
26867 function FCKeditor_OnComplete(editorInstance){
26868     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26869     f.fckEditor = editorInstance;
26870     //console.log("loaded");
26871     f.fireEvent('editorinit', f, editorInstance);
26872
26873   
26874
26875  
26876
26877
26878
26879
26880
26881
26882
26883
26884
26885
26886
26887
26888
26889
26890
26891 //<script type="text/javascript">
26892 /**
26893  * @class Roo.form.GridField
26894  * @extends Roo.form.Field
26895  * Embed a grid (or editable grid into a form)
26896  * STATUS ALPHA
26897  * 
26898  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26899  * it needs 
26900  * xgrid.store = Roo.data.Store
26901  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26902  * xgrid.store.reader = Roo.data.JsonReader 
26903  * 
26904  * 
26905  * @constructor
26906  * Creates a new GridField
26907  * @param {Object} config Configuration options
26908  */
26909 Roo.form.GridField = function(config){
26910     Roo.form.GridField.superclass.constructor.call(this, config);
26911      
26912 };
26913
26914 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26915     /**
26916      * @cfg {Number} width  - used to restrict width of grid..
26917      */
26918     width : 100,
26919     /**
26920      * @cfg {Number} height - used to restrict height of grid..
26921      */
26922     height : 50,
26923      /**
26924      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26925          * 
26926          *}
26927      */
26928     xgrid : false, 
26929     /**
26930      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26931      * {tag: "input", type: "checkbox", autocomplete: "off"})
26932      */
26933    // defaultAutoCreate : { tag: 'div' },
26934     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26935     /**
26936      * @cfg {String} addTitle Text to include for adding a title.
26937      */
26938     addTitle : false,
26939     //
26940     onResize : function(){
26941         Roo.form.Field.superclass.onResize.apply(this, arguments);
26942     },
26943
26944     initEvents : function(){
26945         // Roo.form.Checkbox.superclass.initEvents.call(this);
26946         // has no events...
26947        
26948     },
26949
26950
26951     getResizeEl : function(){
26952         return this.wrap;
26953     },
26954
26955     getPositionEl : function(){
26956         return this.wrap;
26957     },
26958
26959     // private
26960     onRender : function(ct, position){
26961         
26962         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26963         var style = this.style;
26964         delete this.style;
26965         
26966         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26967         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26968         this.viewEl = this.wrap.createChild({ tag: 'div' });
26969         if (style) {
26970             this.viewEl.applyStyles(style);
26971         }
26972         if (this.width) {
26973             this.viewEl.setWidth(this.width);
26974         }
26975         if (this.height) {
26976             this.viewEl.setHeight(this.height);
26977         }
26978         //if(this.inputValue !== undefined){
26979         //this.setValue(this.value);
26980         
26981         
26982         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26983         
26984         
26985         this.grid.render();
26986         this.grid.getDataSource().on('remove', this.refreshValue, this);
26987         this.grid.getDataSource().on('update', this.refreshValue, this);
26988         this.grid.on('afteredit', this.refreshValue, this);
26989  
26990     },
26991      
26992     
26993     /**
26994      * Sets the value of the item. 
26995      * @param {String} either an object  or a string..
26996      */
26997     setValue : function(v){
26998         //this.value = v;
26999         v = v || []; // empty set..
27000         // this does not seem smart - it really only affects memoryproxy grids..
27001         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27002             var ds = this.grid.getDataSource();
27003             // assumes a json reader..
27004             var data = {}
27005             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27006             ds.loadData( data);
27007         }
27008         // clear selection so it does not get stale.
27009         if (this.grid.sm) { 
27010             this.grid.sm.clearSelections();
27011         }
27012         
27013         Roo.form.GridField.superclass.setValue.call(this, v);
27014         this.refreshValue();
27015         // should load data in the grid really....
27016     },
27017     
27018     // private
27019     refreshValue: function() {
27020          var val = [];
27021         this.grid.getDataSource().each(function(r) {
27022             val.push(r.data);
27023         });
27024         this.el.dom.value = Roo.encode(val);
27025     }
27026     
27027      
27028     
27029     
27030 });/*
27031  * Based on:
27032  * Ext JS Library 1.1.1
27033  * Copyright(c) 2006-2007, Ext JS, LLC.
27034  *
27035  * Originally Released Under LGPL - original licence link has changed is not relivant.
27036  *
27037  * Fork - LGPL
27038  * <script type="text/javascript">
27039  */
27040 /**
27041  * @class Roo.form.DisplayField
27042  * @extends Roo.form.Field
27043  * A generic Field to display non-editable data.
27044  * @cfg {Boolean} closable (true|false) default false
27045  * @constructor
27046  * Creates a new Display Field item.
27047  * @param {Object} config Configuration options
27048  */
27049 Roo.form.DisplayField = function(config){
27050     Roo.form.DisplayField.superclass.constructor.call(this, config);
27051     
27052     this.addEvents({
27053         /**
27054          * @event close
27055          * Fires after the click the close btn
27056              * @param {Roo.form.DisplayField} this
27057              */
27058         close : true
27059     });
27060 };
27061
27062 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27063     inputType:      'hidden',
27064     allowBlank:     true,
27065     readOnly:         true,
27066     
27067  
27068     /**
27069      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27070      */
27071     focusClass : undefined,
27072     /**
27073      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27074      */
27075     fieldClass: 'x-form-field',
27076     
27077      /**
27078      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27079      */
27080     valueRenderer: undefined,
27081     
27082     width: 100,
27083     /**
27084      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27085      * {tag: "input", type: "checkbox", autocomplete: "off"})
27086      */
27087      
27088  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27089  
27090     closable : false,
27091     
27092     onResize : function(){
27093         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27094         
27095     },
27096
27097     initEvents : function(){
27098         // Roo.form.Checkbox.superclass.initEvents.call(this);
27099         // has no events...
27100         
27101         if(this.closable){
27102             this.closeEl.on('click', this.onClose, this);
27103         }
27104        
27105     },
27106
27107
27108     getResizeEl : function(){
27109         return this.wrap;
27110     },
27111
27112     getPositionEl : function(){
27113         return this.wrap;
27114     },
27115
27116     // private
27117     onRender : function(ct, position){
27118         
27119         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27120         //if(this.inputValue !== undefined){
27121         this.wrap = this.el.wrap();
27122         
27123         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27124         
27125         if(this.closable){
27126             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27127         }
27128         
27129         if (this.bodyStyle) {
27130             this.viewEl.applyStyles(this.bodyStyle);
27131         }
27132         //this.viewEl.setStyle('padding', '2px');
27133         
27134         this.setValue(this.value);
27135         
27136     },
27137 /*
27138     // private
27139     initValue : Roo.emptyFn,
27140
27141   */
27142
27143         // private
27144     onClick : function(){
27145         
27146     },
27147
27148     /**
27149      * Sets the checked state of the checkbox.
27150      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27151      */
27152     setValue : function(v){
27153         this.value = v;
27154         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27155         // this might be called before we have a dom element..
27156         if (!this.viewEl) {
27157             return;
27158         }
27159         this.viewEl.dom.innerHTML = html;
27160         Roo.form.DisplayField.superclass.setValue.call(this, v);
27161
27162     },
27163     
27164     onClose : function(e)
27165     {
27166         e.preventDefault();
27167         
27168         this.fireEvent('close', this);
27169     }
27170 });/*
27171  * 
27172  * Licence- LGPL
27173  * 
27174  */
27175
27176 /**
27177  * @class Roo.form.DayPicker
27178  * @extends Roo.form.Field
27179  * A Day picker show [M] [T] [W] ....
27180  * @constructor
27181  * Creates a new Day Picker
27182  * @param {Object} config Configuration options
27183  */
27184 Roo.form.DayPicker= function(config){
27185     Roo.form.DayPicker.superclass.constructor.call(this, config);
27186      
27187 };
27188
27189 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27190     /**
27191      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27192      */
27193     focusClass : undefined,
27194     /**
27195      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27196      */
27197     fieldClass: "x-form-field",
27198    
27199     /**
27200      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27201      * {tag: "input", type: "checkbox", autocomplete: "off"})
27202      */
27203     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27204     
27205    
27206     actionMode : 'viewEl', 
27207     //
27208     // private
27209  
27210     inputType : 'hidden',
27211     
27212      
27213     inputElement: false, // real input element?
27214     basedOn: false, // ????
27215     
27216     isFormField: true, // not sure where this is needed!!!!
27217
27218     onResize : function(){
27219         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27220         if(!this.boxLabel){
27221             this.el.alignTo(this.wrap, 'c-c');
27222         }
27223     },
27224
27225     initEvents : function(){
27226         Roo.form.Checkbox.superclass.initEvents.call(this);
27227         this.el.on("click", this.onClick,  this);
27228         this.el.on("change", this.onClick,  this);
27229     },
27230
27231
27232     getResizeEl : function(){
27233         return this.wrap;
27234     },
27235
27236     getPositionEl : function(){
27237         return this.wrap;
27238     },
27239
27240     
27241     // private
27242     onRender : function(ct, position){
27243         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27244        
27245         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27246         
27247         var r1 = '<table><tr>';
27248         var r2 = '<tr class="x-form-daypick-icons">';
27249         for (var i=0; i < 7; i++) {
27250             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27251             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27252         }
27253         
27254         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27255         viewEl.select('img').on('click', this.onClick, this);
27256         this.viewEl = viewEl;   
27257         
27258         
27259         // this will not work on Chrome!!!
27260         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27261         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27262         
27263         
27264           
27265
27266     },
27267
27268     // private
27269     initValue : Roo.emptyFn,
27270
27271     /**
27272      * Returns the checked state of the checkbox.
27273      * @return {Boolean} True if checked, else false
27274      */
27275     getValue : function(){
27276         return this.el.dom.value;
27277         
27278     },
27279
27280         // private
27281     onClick : function(e){ 
27282         //this.setChecked(!this.checked);
27283         Roo.get(e.target).toggleClass('x-menu-item-checked');
27284         this.refreshValue();
27285         //if(this.el.dom.checked != this.checked){
27286         //    this.setValue(this.el.dom.checked);
27287        // }
27288     },
27289     
27290     // private
27291     refreshValue : function()
27292     {
27293         var val = '';
27294         this.viewEl.select('img',true).each(function(e,i,n)  {
27295             val += e.is(".x-menu-item-checked") ? String(n) : '';
27296         });
27297         this.setValue(val, true);
27298     },
27299
27300     /**
27301      * Sets the checked state of the checkbox.
27302      * On is always based on a string comparison between inputValue and the param.
27303      * @param {Boolean/String} value - the value to set 
27304      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27305      */
27306     setValue : function(v,suppressEvent){
27307         if (!this.el.dom) {
27308             return;
27309         }
27310         var old = this.el.dom.value ;
27311         this.el.dom.value = v;
27312         if (suppressEvent) {
27313             return ;
27314         }
27315          
27316         // update display..
27317         this.viewEl.select('img',true).each(function(e,i,n)  {
27318             
27319             var on = e.is(".x-menu-item-checked");
27320             var newv = v.indexOf(String(n)) > -1;
27321             if (on != newv) {
27322                 e.toggleClass('x-menu-item-checked');
27323             }
27324             
27325         });
27326         
27327         
27328         this.fireEvent('change', this, v, old);
27329         
27330         
27331     },
27332    
27333     // handle setting of hidden value by some other method!!?!?
27334     setFromHidden: function()
27335     {
27336         if(!this.el){
27337             return;
27338         }
27339         //console.log("SET FROM HIDDEN");
27340         //alert('setFrom hidden');
27341         this.setValue(this.el.dom.value);
27342     },
27343     
27344     onDestroy : function()
27345     {
27346         if(this.viewEl){
27347             Roo.get(this.viewEl).remove();
27348         }
27349          
27350         Roo.form.DayPicker.superclass.onDestroy.call(this);
27351     }
27352
27353 });/*
27354  * RooJS Library 1.1.1
27355  * Copyright(c) 2008-2011  Alan Knowles
27356  *
27357  * License - LGPL
27358  */
27359  
27360
27361 /**
27362  * @class Roo.form.ComboCheck
27363  * @extends Roo.form.ComboBox
27364  * A combobox for multiple select items.
27365  *
27366  * FIXME - could do with a reset button..
27367  * 
27368  * @constructor
27369  * Create a new ComboCheck
27370  * @param {Object} config Configuration options
27371  */
27372 Roo.form.ComboCheck = function(config){
27373     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27374     // should verify some data...
27375     // like
27376     // hiddenName = required..
27377     // displayField = required
27378     // valudField == required
27379     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27380     var _t = this;
27381     Roo.each(req, function(e) {
27382         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27383             throw "Roo.form.ComboCheck : missing value for: " + e;
27384         }
27385     });
27386     
27387     
27388 };
27389
27390 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27391      
27392      
27393     editable : false,
27394      
27395     selectedClass: 'x-menu-item-checked', 
27396     
27397     // private
27398     onRender : function(ct, position){
27399         var _t = this;
27400         
27401         
27402         
27403         if(!this.tpl){
27404             var cls = 'x-combo-list';
27405
27406             
27407             this.tpl =  new Roo.Template({
27408                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27409                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27410                    '<span>{' + this.displayField + '}</span>' +
27411                     '</div>' 
27412                 
27413             });
27414         }
27415  
27416         
27417         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27418         this.view.singleSelect = false;
27419         this.view.multiSelect = true;
27420         this.view.toggleSelect = true;
27421         this.pageTb.add(new Roo.Toolbar.Fill(), {
27422             
27423             text: 'Done',
27424             handler: function()
27425             {
27426                 _t.collapse();
27427             }
27428         });
27429     },
27430     
27431     onViewOver : function(e, t){
27432         // do nothing...
27433         return;
27434         
27435     },
27436     
27437     onViewClick : function(doFocus,index){
27438         return;
27439         
27440     },
27441     select: function () {
27442         //Roo.log("SELECT CALLED");
27443     },
27444      
27445     selectByValue : function(xv, scrollIntoView){
27446         var ar = this.getValueArray();
27447         var sels = [];
27448         
27449         Roo.each(ar, function(v) {
27450             if(v === undefined || v === null){
27451                 return;
27452             }
27453             var r = this.findRecord(this.valueField, v);
27454             if(r){
27455                 sels.push(this.store.indexOf(r))
27456                 
27457             }
27458         },this);
27459         this.view.select(sels);
27460         return false;
27461     },
27462     
27463     
27464     
27465     onSelect : function(record, index){
27466        // Roo.log("onselect Called");
27467        // this is only called by the clear button now..
27468         this.view.clearSelections();
27469         this.setValue('[]');
27470         if (this.value != this.valueBefore) {
27471             this.fireEvent('change', this, this.value, this.valueBefore);
27472             this.valueBefore = this.value;
27473         }
27474     },
27475     getValueArray : function()
27476     {
27477         var ar = [] ;
27478         
27479         try {
27480             //Roo.log(this.value);
27481             if (typeof(this.value) == 'undefined') {
27482                 return [];
27483             }
27484             var ar = Roo.decode(this.value);
27485             return  ar instanceof Array ? ar : []; //?? valid?
27486             
27487         } catch(e) {
27488             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27489             return [];
27490         }
27491          
27492     },
27493     expand : function ()
27494     {
27495         
27496         Roo.form.ComboCheck.superclass.expand.call(this);
27497         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27498         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27499         
27500
27501     },
27502     
27503     collapse : function(){
27504         Roo.form.ComboCheck.superclass.collapse.call(this);
27505         var sl = this.view.getSelectedIndexes();
27506         var st = this.store;
27507         var nv = [];
27508         var tv = [];
27509         var r;
27510         Roo.each(sl, function(i) {
27511             r = st.getAt(i);
27512             nv.push(r.get(this.valueField));
27513         },this);
27514         this.setValue(Roo.encode(nv));
27515         if (this.value != this.valueBefore) {
27516
27517             this.fireEvent('change', this, this.value, this.valueBefore);
27518             this.valueBefore = this.value;
27519         }
27520         
27521     },
27522     
27523     setValue : function(v){
27524         // Roo.log(v);
27525         this.value = v;
27526         
27527         var vals = this.getValueArray();
27528         var tv = [];
27529         Roo.each(vals, function(k) {
27530             var r = this.findRecord(this.valueField, k);
27531             if(r){
27532                 tv.push(r.data[this.displayField]);
27533             }else if(this.valueNotFoundText !== undefined){
27534                 tv.push( this.valueNotFoundText );
27535             }
27536         },this);
27537        // Roo.log(tv);
27538         
27539         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27540         this.hiddenField.value = v;
27541         this.value = v;
27542     }
27543     
27544 });/*
27545  * Based on:
27546  * Ext JS Library 1.1.1
27547  * Copyright(c) 2006-2007, Ext JS, LLC.
27548  *
27549  * Originally Released Under LGPL - original licence link has changed is not relivant.
27550  *
27551  * Fork - LGPL
27552  * <script type="text/javascript">
27553  */
27554  
27555 /**
27556  * @class Roo.form.Signature
27557  * @extends Roo.form.Field
27558  * Signature field.  
27559  * @constructor
27560  * 
27561  * @param {Object} config Configuration options
27562  */
27563
27564 Roo.form.Signature = function(config){
27565     Roo.form.Signature.superclass.constructor.call(this, config);
27566     
27567     this.addEvents({// not in used??
27568          /**
27569          * @event confirm
27570          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27571              * @param {Roo.form.Signature} combo This combo box
27572              */
27573         'confirm' : true,
27574         /**
27575          * @event reset
27576          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27577              * @param {Roo.form.ComboBox} combo This combo box
27578              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27579              */
27580         'reset' : true
27581     });
27582 };
27583
27584 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27585     /**
27586      * @cfg {Object} labels Label to use when rendering a form.
27587      * defaults to 
27588      * labels : { 
27589      *      clear : "Clear",
27590      *      confirm : "Confirm"
27591      *  }
27592      */
27593     labels : { 
27594         clear : "Clear",
27595         confirm : "Confirm"
27596     },
27597     /**
27598      * @cfg {Number} width The signature panel width (defaults to 300)
27599      */
27600     width: 300,
27601     /**
27602      * @cfg {Number} height The signature panel height (defaults to 100)
27603      */
27604     height : 100,
27605     /**
27606      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27607      */
27608     allowBlank : false,
27609     
27610     //private
27611     // {Object} signPanel The signature SVG panel element (defaults to {})
27612     signPanel : {},
27613     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27614     isMouseDown : false,
27615     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27616     isConfirmed : false,
27617     // {String} signatureTmp SVG mapping string (defaults to empty string)
27618     signatureTmp : '',
27619     
27620     
27621     defaultAutoCreate : { // modified by initCompnoent..
27622         tag: "input",
27623         type:"hidden"
27624     },
27625
27626     // private
27627     onRender : function(ct, position){
27628         
27629         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27630         
27631         this.wrap = this.el.wrap({
27632             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27633         });
27634         
27635         this.createToolbar(this);
27636         this.signPanel = this.wrap.createChild({
27637                 tag: 'div',
27638                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27639             }, this.el
27640         );
27641             
27642         this.svgID = Roo.id();
27643         this.svgEl = this.signPanel.createChild({
27644               xmlns : 'http://www.w3.org/2000/svg',
27645               tag : 'svg',
27646               id : this.svgID + "-svg",
27647               width: this.width,
27648               height: this.height,
27649               viewBox: '0 0 '+this.width+' '+this.height,
27650               cn : [
27651                 {
27652                     tag: "rect",
27653                     id: this.svgID + "-svg-r",
27654                     width: this.width,
27655                     height: this.height,
27656                     fill: "#ffa"
27657                 },
27658                 {
27659                     tag: "line",
27660                     id: this.svgID + "-svg-l",
27661                     x1: "0", // start
27662                     y1: (this.height*0.8), // start set the line in 80% of height
27663                     x2: this.width, // end
27664                     y2: (this.height*0.8), // end set the line in 80% of height
27665                     'stroke': "#666",
27666                     'stroke-width': "1",
27667                     'stroke-dasharray': "3",
27668                     'shape-rendering': "crispEdges",
27669                     'pointer-events': "none"
27670                 },
27671                 {
27672                     tag: "path",
27673                     id: this.svgID + "-svg-p",
27674                     'stroke': "navy",
27675                     'stroke-width': "3",
27676                     'fill': "none",
27677                     'pointer-events': 'none'
27678                 }
27679               ]
27680         });
27681         this.createSVG();
27682         this.svgBox = this.svgEl.dom.getScreenCTM();
27683     },
27684     createSVG : function(){ 
27685         var svg = this.signPanel;
27686         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27687         var t = this;
27688
27689         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27690         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27691         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27692         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27693         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27694         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27695         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27696         
27697     },
27698     isTouchEvent : function(e){
27699         return e.type.match(/^touch/);
27700     },
27701     getCoords : function (e) {
27702         var pt    = this.svgEl.dom.createSVGPoint();
27703         pt.x = e.clientX; 
27704         pt.y = e.clientY;
27705         if (this.isTouchEvent(e)) {
27706             pt.x =  e.targetTouches[0].clientX;
27707             pt.y = e.targetTouches[0].clientY;
27708         }
27709         var a = this.svgEl.dom.getScreenCTM();
27710         var b = a.inverse();
27711         var mx = pt.matrixTransform(b);
27712         return mx.x + ',' + mx.y;
27713     },
27714     //mouse event headler 
27715     down : function (e) {
27716         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27717         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27718         
27719         this.isMouseDown = true;
27720         
27721         e.preventDefault();
27722     },
27723     move : function (e) {
27724         if (this.isMouseDown) {
27725             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27726             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27727         }
27728         
27729         e.preventDefault();
27730     },
27731     up : function (e) {
27732         this.isMouseDown = false;
27733         var sp = this.signatureTmp.split(' ');
27734         
27735         if(sp.length > 1){
27736             if(!sp[sp.length-2].match(/^L/)){
27737                 sp.pop();
27738                 sp.pop();
27739                 sp.push("");
27740                 this.signatureTmp = sp.join(" ");
27741             }
27742         }
27743         if(this.getValue() != this.signatureTmp){
27744             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27745             this.isConfirmed = false;
27746         }
27747         e.preventDefault();
27748     },
27749     
27750     /**
27751      * Protected method that will not generally be called directly. It
27752      * is called when the editor creates its toolbar. Override this method if you need to
27753      * add custom toolbar buttons.
27754      * @param {HtmlEditor} editor
27755      */
27756     createToolbar : function(editor){
27757          function btn(id, toggle, handler){
27758             var xid = fid + '-'+ id ;
27759             return {
27760                 id : xid,
27761                 cmd : id,
27762                 cls : 'x-btn-icon x-edit-'+id,
27763                 enableToggle:toggle !== false,
27764                 scope: editor, // was editor...
27765                 handler:handler||editor.relayBtnCmd,
27766                 clickEvent:'mousedown',
27767                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27768                 tabIndex:-1
27769             };
27770         }
27771         
27772         
27773         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27774         this.tb = tb;
27775         this.tb.add(
27776            {
27777                 cls : ' x-signature-btn x-signature-'+id,
27778                 scope: editor, // was editor...
27779                 handler: this.reset,
27780                 clickEvent:'mousedown',
27781                 text: this.labels.clear
27782             },
27783             {
27784                  xtype : 'Fill',
27785                  xns: Roo.Toolbar
27786             }, 
27787             {
27788                 cls : '  x-signature-btn x-signature-'+id,
27789                 scope: editor, // was editor...
27790                 handler: this.confirmHandler,
27791                 clickEvent:'mousedown',
27792                 text: this.labels.confirm
27793             }
27794         );
27795     
27796     },
27797     //public
27798     /**
27799      * when user is clicked confirm then show this image.....
27800      * 
27801      * @return {String} Image Data URI
27802      */
27803     getImageDataURI : function(){
27804         var svg = this.svgEl.dom.parentNode.innerHTML;
27805         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27806         return src; 
27807     },
27808     /**
27809      * 
27810      * @return {Boolean} this.isConfirmed
27811      */
27812     getConfirmed : function(){
27813         return this.isConfirmed;
27814     },
27815     /**
27816      * 
27817      * @return {Number} this.width
27818      */
27819     getWidth : function(){
27820         return this.width;
27821     },
27822     /**
27823      * 
27824      * @return {Number} this.height
27825      */
27826     getHeight : function(){
27827         return this.height;
27828     },
27829     // private
27830     getSignature : function(){
27831         return this.signatureTmp;
27832     },
27833     // private
27834     reset : function(){
27835         this.signatureTmp = '';
27836         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27837         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27838         this.isConfirmed = false;
27839         Roo.form.Signature.superclass.reset.call(this);
27840     },
27841     setSignature : function(s){
27842         this.signatureTmp = s;
27843         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27844         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27845         this.setValue(s);
27846         this.isConfirmed = false;
27847         Roo.form.Signature.superclass.reset.call(this);
27848     }, 
27849     test : function(){
27850 //        Roo.log(this.signPanel.dom.contentWindow.up())
27851     },
27852     //private
27853     setConfirmed : function(){
27854         
27855         
27856         
27857 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27858     },
27859     // private
27860     confirmHandler : function(){
27861         if(!this.getSignature()){
27862             return;
27863         }
27864         
27865         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27866         this.setValue(this.getSignature());
27867         this.isConfirmed = true;
27868         
27869         this.fireEvent('confirm', this);
27870     },
27871     // private
27872     // Subclasses should provide the validation implementation by overriding this
27873     validateValue : function(value){
27874         if(this.allowBlank){
27875             return true;
27876         }
27877         
27878         if(this.isConfirmed){
27879             return true;
27880         }
27881         return false;
27882     }
27883 });/*
27884  * Based on:
27885  * Ext JS Library 1.1.1
27886  * Copyright(c) 2006-2007, Ext JS, LLC.
27887  *
27888  * Originally Released Under LGPL - original licence link has changed is not relivant.
27889  *
27890  * Fork - LGPL
27891  * <script type="text/javascript">
27892  */
27893  
27894
27895 /**
27896  * @class Roo.form.ComboBox
27897  * @extends Roo.form.TriggerField
27898  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27899  * @constructor
27900  * Create a new ComboBox.
27901  * @param {Object} config Configuration options
27902  */
27903 Roo.form.Select = function(config){
27904     Roo.form.Select.superclass.constructor.call(this, config);
27905      
27906 };
27907
27908 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27909     /**
27910      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27911      */
27912     /**
27913      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27914      * rendering into an Roo.Editor, defaults to false)
27915      */
27916     /**
27917      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27918      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27919      */
27920     /**
27921      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27922      */
27923     /**
27924      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27925      * the dropdown list (defaults to undefined, with no header element)
27926      */
27927
27928      /**
27929      * @cfg {String/Roo.Template} tpl The template to use to render the output
27930      */
27931      
27932     // private
27933     defaultAutoCreate : {tag: "select"  },
27934     /**
27935      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27936      */
27937     listWidth: undefined,
27938     /**
27939      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27940      * mode = 'remote' or 'text' if mode = 'local')
27941      */
27942     displayField: undefined,
27943     /**
27944      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27945      * mode = 'remote' or 'value' if mode = 'local'). 
27946      * Note: use of a valueField requires the user make a selection
27947      * in order for a value to be mapped.
27948      */
27949     valueField: undefined,
27950     
27951     
27952     /**
27953      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27954      * field's data value (defaults to the underlying DOM element's name)
27955      */
27956     hiddenName: undefined,
27957     /**
27958      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27959      */
27960     listClass: '',
27961     /**
27962      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27963      */
27964     selectedClass: 'x-combo-selected',
27965     /**
27966      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27967      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27968      * which displays a downward arrow icon).
27969      */
27970     triggerClass : 'x-form-arrow-trigger',
27971     /**
27972      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27973      */
27974     shadow:'sides',
27975     /**
27976      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27977      * anchor positions (defaults to 'tl-bl')
27978      */
27979     listAlign: 'tl-bl?',
27980     /**
27981      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27982      */
27983     maxHeight: 300,
27984     /**
27985      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27986      * query specified by the allQuery config option (defaults to 'query')
27987      */
27988     triggerAction: 'query',
27989     /**
27990      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27991      * (defaults to 4, does not apply if editable = false)
27992      */
27993     minChars : 4,
27994     /**
27995      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27996      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27997      */
27998     typeAhead: false,
27999     /**
28000      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28001      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28002      */
28003     queryDelay: 500,
28004     /**
28005      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28006      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28007      */
28008     pageSize: 0,
28009     /**
28010      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28011      * when editable = true (defaults to false)
28012      */
28013     selectOnFocus:false,
28014     /**
28015      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28016      */
28017     queryParam: 'query',
28018     /**
28019      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28020      * when mode = 'remote' (defaults to 'Loading...')
28021      */
28022     loadingText: 'Loading...',
28023     /**
28024      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28025      */
28026     resizable: false,
28027     /**
28028      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28029      */
28030     handleHeight : 8,
28031     /**
28032      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28033      * traditional select (defaults to true)
28034      */
28035     editable: true,
28036     /**
28037      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28038      */
28039     allQuery: '',
28040     /**
28041      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28042      */
28043     mode: 'remote',
28044     /**
28045      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28046      * listWidth has a higher value)
28047      */
28048     minListWidth : 70,
28049     /**
28050      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28051      * allow the user to set arbitrary text into the field (defaults to false)
28052      */
28053     forceSelection:false,
28054     /**
28055      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28056      * if typeAhead = true (defaults to 250)
28057      */
28058     typeAheadDelay : 250,
28059     /**
28060      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28061      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28062      */
28063     valueNotFoundText : undefined,
28064     
28065     /**
28066      * @cfg {String} defaultValue The value displayed after loading the store.
28067      */
28068     defaultValue: '',
28069     
28070     /**
28071      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28072      */
28073     blockFocus : false,
28074     
28075     /**
28076      * @cfg {Boolean} disableClear Disable showing of clear button.
28077      */
28078     disableClear : false,
28079     /**
28080      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28081      */
28082     alwaysQuery : false,
28083     
28084     //private
28085     addicon : false,
28086     editicon: false,
28087     
28088     // element that contains real text value.. (when hidden is used..)
28089      
28090     // private
28091     onRender : function(ct, position){
28092         Roo.form.Field.prototype.onRender.call(this, ct, position);
28093         
28094         if(this.store){
28095             this.store.on('beforeload', this.onBeforeLoad, this);
28096             this.store.on('load', this.onLoad, this);
28097             this.store.on('loadexception', this.onLoadException, this);
28098             this.store.load({});
28099         }
28100         
28101         
28102         
28103     },
28104
28105     // private
28106     initEvents : function(){
28107         //Roo.form.ComboBox.superclass.initEvents.call(this);
28108  
28109     },
28110
28111     onDestroy : function(){
28112        
28113         if(this.store){
28114             this.store.un('beforeload', this.onBeforeLoad, this);
28115             this.store.un('load', this.onLoad, this);
28116             this.store.un('loadexception', this.onLoadException, this);
28117         }
28118         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28119     },
28120
28121     // private
28122     fireKey : function(e){
28123         if(e.isNavKeyPress() && !this.list.isVisible()){
28124             this.fireEvent("specialkey", this, e);
28125         }
28126     },
28127
28128     // private
28129     onResize: function(w, h){
28130         
28131         return; 
28132     
28133         
28134     },
28135
28136     /**
28137      * Allow or prevent the user from directly editing the field text.  If false is passed,
28138      * the user will only be able to select from the items defined in the dropdown list.  This method
28139      * is the runtime equivalent of setting the 'editable' config option at config time.
28140      * @param {Boolean} value True to allow the user to directly edit the field text
28141      */
28142     setEditable : function(value){
28143          
28144     },
28145
28146     // private
28147     onBeforeLoad : function(){
28148         
28149         Roo.log("Select before load");
28150         return;
28151     
28152         this.innerList.update(this.loadingText ?
28153                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28154         //this.restrictHeight();
28155         this.selectedIndex = -1;
28156     },
28157
28158     // private
28159     onLoad : function(){
28160
28161     
28162         var dom = this.el.dom;
28163         dom.innerHTML = '';
28164          var od = dom.ownerDocument;
28165          
28166         if (this.emptyText) {
28167             var op = od.createElement('option');
28168             op.setAttribute('value', '');
28169             op.innerHTML = String.format('{0}', this.emptyText);
28170             dom.appendChild(op);
28171         }
28172         if(this.store.getCount() > 0){
28173            
28174             var vf = this.valueField;
28175             var df = this.displayField;
28176             this.store.data.each(function(r) {
28177                 // which colmsn to use... testing - cdoe / title..
28178                 var op = od.createElement('option');
28179                 op.setAttribute('value', r.data[vf]);
28180                 op.innerHTML = String.format('{0}', r.data[df]);
28181                 dom.appendChild(op);
28182             });
28183             if (typeof(this.defaultValue != 'undefined')) {
28184                 this.setValue(this.defaultValue);
28185             }
28186             
28187              
28188         }else{
28189             //this.onEmptyResults();
28190         }
28191         //this.el.focus();
28192     },
28193     // private
28194     onLoadException : function()
28195     {
28196         dom.innerHTML = '';
28197             
28198         Roo.log("Select on load exception");
28199         return;
28200     
28201         this.collapse();
28202         Roo.log(this.store.reader.jsonData);
28203         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28204             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28205         }
28206         
28207         
28208     },
28209     // private
28210     onTypeAhead : function(){
28211          
28212     },
28213
28214     // private
28215     onSelect : function(record, index){
28216         Roo.log('on select?');
28217         return;
28218         if(this.fireEvent('beforeselect', this, record, index) !== false){
28219             this.setFromData(index > -1 ? record.data : false);
28220             this.collapse();
28221             this.fireEvent('select', this, record, index);
28222         }
28223     },
28224
28225     /**
28226      * Returns the currently selected field value or empty string if no value is set.
28227      * @return {String} value The selected value
28228      */
28229     getValue : function(){
28230         var dom = this.el.dom;
28231         this.value = dom.options[dom.selectedIndex].value;
28232         return this.value;
28233         
28234     },
28235
28236     /**
28237      * Clears any text/value currently set in the field
28238      */
28239     clearValue : function(){
28240         this.value = '';
28241         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28242         
28243     },
28244
28245     /**
28246      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28247      * will be displayed in the field.  If the value does not match the data value of an existing item,
28248      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28249      * Otherwise the field will be blank (although the value will still be set).
28250      * @param {String} value The value to match
28251      */
28252     setValue : function(v){
28253         var d = this.el.dom;
28254         for (var i =0; i < d.options.length;i++) {
28255             if (v == d.options[i].value) {
28256                 d.selectedIndex = i;
28257                 this.value = v;
28258                 return;
28259             }
28260         }
28261         this.clearValue();
28262     },
28263     /**
28264      * @property {Object} the last set data for the element
28265      */
28266     
28267     lastData : false,
28268     /**
28269      * Sets the value of the field based on a object which is related to the record format for the store.
28270      * @param {Object} value the value to set as. or false on reset?
28271      */
28272     setFromData : function(o){
28273         Roo.log('setfrom data?');
28274          
28275         
28276         
28277     },
28278     // private
28279     reset : function(){
28280         this.clearValue();
28281     },
28282     // private
28283     findRecord : function(prop, value){
28284         
28285         return false;
28286     
28287         var record;
28288         if(this.store.getCount() > 0){
28289             this.store.each(function(r){
28290                 if(r.data[prop] == value){
28291                     record = r;
28292                     return false;
28293                 }
28294                 return true;
28295             });
28296         }
28297         return record;
28298     },
28299     
28300     getName: function()
28301     {
28302         // returns hidden if it's set..
28303         if (!this.rendered) {return ''};
28304         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28305         
28306     },
28307      
28308
28309     
28310
28311     // private
28312     onEmptyResults : function(){
28313         Roo.log('empty results');
28314         //this.collapse();
28315     },
28316
28317     /**
28318      * Returns true if the dropdown list is expanded, else false.
28319      */
28320     isExpanded : function(){
28321         return false;
28322     },
28323
28324     /**
28325      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28326      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28327      * @param {String} value The data value of the item to select
28328      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28329      * selected item if it is not currently in view (defaults to true)
28330      * @return {Boolean} True if the value matched an item in the list, else false
28331      */
28332     selectByValue : function(v, scrollIntoView){
28333         Roo.log('select By Value');
28334         return false;
28335     
28336         if(v !== undefined && v !== null){
28337             var r = this.findRecord(this.valueField || this.displayField, v);
28338             if(r){
28339                 this.select(this.store.indexOf(r), scrollIntoView);
28340                 return true;
28341             }
28342         }
28343         return false;
28344     },
28345
28346     /**
28347      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28348      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28349      * @param {Number} index The zero-based index of the list item to select
28350      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28351      * selected item if it is not currently in view (defaults to true)
28352      */
28353     select : function(index, scrollIntoView){
28354         Roo.log('select ');
28355         return  ;
28356         
28357         this.selectedIndex = index;
28358         this.view.select(index);
28359         if(scrollIntoView !== false){
28360             var el = this.view.getNode(index);
28361             if(el){
28362                 this.innerList.scrollChildIntoView(el, false);
28363             }
28364         }
28365     },
28366
28367       
28368
28369     // private
28370     validateBlur : function(){
28371         
28372         return;
28373         
28374     },
28375
28376     // private
28377     initQuery : function(){
28378         this.doQuery(this.getRawValue());
28379     },
28380
28381     // private
28382     doForce : function(){
28383         if(this.el.dom.value.length > 0){
28384             this.el.dom.value =
28385                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28386              
28387         }
28388     },
28389
28390     /**
28391      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28392      * query allowing the query action to be canceled if needed.
28393      * @param {String} query The SQL query to execute
28394      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28395      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28396      * saved in the current store (defaults to false)
28397      */
28398     doQuery : function(q, forceAll){
28399         
28400         Roo.log('doQuery?');
28401         if(q === undefined || q === null){
28402             q = '';
28403         }
28404         var qe = {
28405             query: q,
28406             forceAll: forceAll,
28407             combo: this,
28408             cancel:false
28409         };
28410         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28411             return false;
28412         }
28413         q = qe.query;
28414         forceAll = qe.forceAll;
28415         if(forceAll === true || (q.length >= this.minChars)){
28416             if(this.lastQuery != q || this.alwaysQuery){
28417                 this.lastQuery = q;
28418                 if(this.mode == 'local'){
28419                     this.selectedIndex = -1;
28420                     if(forceAll){
28421                         this.store.clearFilter();
28422                     }else{
28423                         this.store.filter(this.displayField, q);
28424                     }
28425                     this.onLoad();
28426                 }else{
28427                     this.store.baseParams[this.queryParam] = q;
28428                     this.store.load({
28429                         params: this.getParams(q)
28430                     });
28431                     this.expand();
28432                 }
28433             }else{
28434                 this.selectedIndex = -1;
28435                 this.onLoad();   
28436             }
28437         }
28438     },
28439
28440     // private
28441     getParams : function(q){
28442         var p = {};
28443         //p[this.queryParam] = q;
28444         if(this.pageSize){
28445             p.start = 0;
28446             p.limit = this.pageSize;
28447         }
28448         return p;
28449     },
28450
28451     /**
28452      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28453      */
28454     collapse : function(){
28455         
28456     },
28457
28458     // private
28459     collapseIf : function(e){
28460         
28461     },
28462
28463     /**
28464      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28465      */
28466     expand : function(){
28467         
28468     } ,
28469
28470     // private
28471      
28472
28473     /** 
28474     * @cfg {Boolean} grow 
28475     * @hide 
28476     */
28477     /** 
28478     * @cfg {Number} growMin 
28479     * @hide 
28480     */
28481     /** 
28482     * @cfg {Number} growMax 
28483     * @hide 
28484     */
28485     /**
28486      * @hide
28487      * @method autoSize
28488      */
28489     
28490     setWidth : function()
28491     {
28492         
28493     },
28494     getResizeEl : function(){
28495         return this.el;
28496     }
28497 });//<script type="text/javasscript">
28498  
28499
28500 /**
28501  * @class Roo.DDView
28502  * A DnD enabled version of Roo.View.
28503  * @param {Element/String} container The Element in which to create the View.
28504  * @param {String} tpl The template string used to create the markup for each element of the View
28505  * @param {Object} config The configuration properties. These include all the config options of
28506  * {@link Roo.View} plus some specific to this class.<br>
28507  * <p>
28508  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28509  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28510  * <p>
28511  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28512 .x-view-drag-insert-above {
28513         border-top:1px dotted #3366cc;
28514 }
28515 .x-view-drag-insert-below {
28516         border-bottom:1px dotted #3366cc;
28517 }
28518 </code></pre>
28519  * 
28520  */
28521  
28522 Roo.DDView = function(container, tpl, config) {
28523     Roo.DDView.superclass.constructor.apply(this, arguments);
28524     this.getEl().setStyle("outline", "0px none");
28525     this.getEl().unselectable();
28526     if (this.dragGroup) {
28527         this.setDraggable(this.dragGroup.split(","));
28528     }
28529     if (this.dropGroup) {
28530         this.setDroppable(this.dropGroup.split(","));
28531     }
28532     if (this.deletable) {
28533         this.setDeletable();
28534     }
28535     this.isDirtyFlag = false;
28536         this.addEvents({
28537                 "drop" : true
28538         });
28539 };
28540
28541 Roo.extend(Roo.DDView, Roo.View, {
28542 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28543 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28544 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28545 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28546
28547         isFormField: true,
28548
28549         reset: Roo.emptyFn,
28550         
28551         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28552
28553         validate: function() {
28554                 return true;
28555         },
28556         
28557         destroy: function() {
28558                 this.purgeListeners();
28559                 this.getEl.removeAllListeners();
28560                 this.getEl().remove();
28561                 if (this.dragZone) {
28562                         if (this.dragZone.destroy) {
28563                                 this.dragZone.destroy();
28564                         }
28565                 }
28566                 if (this.dropZone) {
28567                         if (this.dropZone.destroy) {
28568                                 this.dropZone.destroy();
28569                         }
28570                 }
28571         },
28572
28573 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28574         getName: function() {
28575                 return this.name;
28576         },
28577
28578 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28579         setValue: function(v) {
28580                 if (!this.store) {
28581                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28582                 }
28583                 var data = {};
28584                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28585                 this.store.proxy = new Roo.data.MemoryProxy(data);
28586                 this.store.load();
28587         },
28588
28589 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28590         getValue: function() {
28591                 var result = '(';
28592                 this.store.each(function(rec) {
28593                         result += rec.id + ',';
28594                 });
28595                 return result.substr(0, result.length - 1) + ')';
28596         },
28597         
28598         getIds: function() {
28599                 var i = 0, result = new Array(this.store.getCount());
28600                 this.store.each(function(rec) {
28601                         result[i++] = rec.id;
28602                 });
28603                 return result;
28604         },
28605         
28606         isDirty: function() {
28607                 return this.isDirtyFlag;
28608         },
28609
28610 /**
28611  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28612  *      whole Element becomes the target, and this causes the drop gesture to append.
28613  */
28614     getTargetFromEvent : function(e) {
28615                 var target = e.getTarget();
28616                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28617                 target = target.parentNode;
28618                 }
28619                 if (!target) {
28620                         target = this.el.dom.lastChild || this.el.dom;
28621                 }
28622                 return target;
28623     },
28624
28625 /**
28626  *      Create the drag data which consists of an object which has the property "ddel" as
28627  *      the drag proxy element. 
28628  */
28629     getDragData : function(e) {
28630         var target = this.findItemFromChild(e.getTarget());
28631                 if(target) {
28632                         this.handleSelection(e);
28633                         var selNodes = this.getSelectedNodes();
28634             var dragData = {
28635                 source: this,
28636                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28637                 nodes: selNodes,
28638                 records: []
28639                         };
28640                         var selectedIndices = this.getSelectedIndexes();
28641                         for (var i = 0; i < selectedIndices.length; i++) {
28642                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28643                         }
28644                         if (selNodes.length == 1) {
28645                                 dragData.ddel = target.cloneNode(true); // the div element
28646                         } else {
28647                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28648                                 div.className = 'multi-proxy';
28649                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28650                                         div.appendChild(selNodes[i].cloneNode(true));
28651                                 }
28652                                 dragData.ddel = div;
28653                         }
28654             //console.log(dragData)
28655             //console.log(dragData.ddel.innerHTML)
28656                         return dragData;
28657                 }
28658         //console.log('nodragData')
28659                 return false;
28660     },
28661     
28662 /**     Specify to which ddGroup items in this DDView may be dragged. */
28663     setDraggable: function(ddGroup) {
28664         if (ddGroup instanceof Array) {
28665                 Roo.each(ddGroup, this.setDraggable, this);
28666                 return;
28667         }
28668         if (this.dragZone) {
28669                 this.dragZone.addToGroup(ddGroup);
28670         } else {
28671                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28672                                 containerScroll: true,
28673                                 ddGroup: ddGroup 
28674
28675                         });
28676 //                      Draggability implies selection. DragZone's mousedown selects the element.
28677                         if (!this.multiSelect) { this.singleSelect = true; }
28678
28679 //                      Wire the DragZone's handlers up to methods in *this*
28680                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28681                 }
28682     },
28683
28684 /**     Specify from which ddGroup this DDView accepts drops. */
28685     setDroppable: function(ddGroup) {
28686         if (ddGroup instanceof Array) {
28687                 Roo.each(ddGroup, this.setDroppable, this);
28688                 return;
28689         }
28690         if (this.dropZone) {
28691                 this.dropZone.addToGroup(ddGroup);
28692         } else {
28693                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28694                                 containerScroll: true,
28695                                 ddGroup: ddGroup
28696                         });
28697
28698 //                      Wire the DropZone's handlers up to methods in *this*
28699                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28700                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28701                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28702                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28703                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28704                 }
28705     },
28706
28707 /**     Decide whether to drop above or below a View node. */
28708     getDropPoint : function(e, n, dd){
28709         if (n == this.el.dom) { return "above"; }
28710                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28711                 var c = t + (b - t) / 2;
28712                 var y = Roo.lib.Event.getPageY(e);
28713                 if(y <= c) {
28714                         return "above";
28715                 }else{
28716                         return "below";
28717                 }
28718     },
28719
28720     onNodeEnter : function(n, dd, e, data){
28721                 return false;
28722     },
28723     
28724     onNodeOver : function(n, dd, e, data){
28725                 var pt = this.getDropPoint(e, n, dd);
28726                 // set the insert point style on the target node
28727                 var dragElClass = this.dropNotAllowed;
28728                 if (pt) {
28729                         var targetElClass;
28730                         if (pt == "above"){
28731                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28732                                 targetElClass = "x-view-drag-insert-above";
28733                         } else {
28734                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28735                                 targetElClass = "x-view-drag-insert-below";
28736                         }
28737                         if (this.lastInsertClass != targetElClass){
28738                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28739                                 this.lastInsertClass = targetElClass;
28740                         }
28741                 }
28742                 return dragElClass;
28743         },
28744
28745     onNodeOut : function(n, dd, e, data){
28746                 this.removeDropIndicators(n);
28747     },
28748
28749     onNodeDrop : function(n, dd, e, data){
28750         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28751                 return false;
28752         }
28753         var pt = this.getDropPoint(e, n, dd);
28754                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28755                 if (pt == "below") { insertAt++; }
28756                 for (var i = 0; i < data.records.length; i++) {
28757                         var r = data.records[i];
28758                         var dup = this.store.getById(r.id);
28759                         if (dup && (dd != this.dragZone)) {
28760                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28761                         } else {
28762                                 if (data.copy) {
28763                                         this.store.insert(insertAt++, r.copy());
28764                                 } else {
28765                                         data.source.isDirtyFlag = true;
28766                                         r.store.remove(r);
28767                                         this.store.insert(insertAt++, r);
28768                                 }
28769                                 this.isDirtyFlag = true;
28770                         }
28771                 }
28772                 this.dragZone.cachedTarget = null;
28773                 return true;
28774     },
28775
28776     removeDropIndicators : function(n){
28777                 if(n){
28778                         Roo.fly(n).removeClass([
28779                                 "x-view-drag-insert-above",
28780                                 "x-view-drag-insert-below"]);
28781                         this.lastInsertClass = "_noclass";
28782                 }
28783     },
28784
28785 /**
28786  *      Utility method. Add a delete option to the DDView's context menu.
28787  *      @param {String} imageUrl The URL of the "delete" icon image.
28788  */
28789         setDeletable: function(imageUrl) {
28790                 if (!this.singleSelect && !this.multiSelect) {
28791                         this.singleSelect = true;
28792                 }
28793                 var c = this.getContextMenu();
28794                 this.contextMenu.on("itemclick", function(item) {
28795                         switch (item.id) {
28796                                 case "delete":
28797                                         this.remove(this.getSelectedIndexes());
28798                                         break;
28799                         }
28800                 }, this);
28801                 this.contextMenu.add({
28802                         icon: imageUrl,
28803                         id: "delete",
28804                         text: 'Delete'
28805                 });
28806         },
28807         
28808 /**     Return the context menu for this DDView. */
28809         getContextMenu: function() {
28810                 if (!this.contextMenu) {
28811 //                      Create the View's context menu
28812                         this.contextMenu = new Roo.menu.Menu({
28813                                 id: this.id + "-contextmenu"
28814                         });
28815                         this.el.on("contextmenu", this.showContextMenu, this);
28816                 }
28817                 return this.contextMenu;
28818         },
28819         
28820         disableContextMenu: function() {
28821                 if (this.contextMenu) {
28822                         this.el.un("contextmenu", this.showContextMenu, this);
28823                 }
28824         },
28825
28826         showContextMenu: function(e, item) {
28827         item = this.findItemFromChild(e.getTarget());
28828                 if (item) {
28829                         e.stopEvent();
28830                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28831                         this.contextMenu.showAt(e.getXY());
28832             }
28833     },
28834
28835 /**
28836  *      Remove {@link Roo.data.Record}s at the specified indices.
28837  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28838  */
28839     remove: function(selectedIndices) {
28840                 selectedIndices = [].concat(selectedIndices);
28841                 for (var i = 0; i < selectedIndices.length; i++) {
28842                         var rec = this.store.getAt(selectedIndices[i]);
28843                         this.store.remove(rec);
28844                 }
28845     },
28846
28847 /**
28848  *      Double click fires the event, but also, if this is draggable, and there is only one other
28849  *      related DropZone, it transfers the selected node.
28850  */
28851     onDblClick : function(e){
28852         var item = this.findItemFromChild(e.getTarget());
28853         if(item){
28854             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28855                 return false;
28856             }
28857             if (this.dragGroup) {
28858                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28859                     while (targets.indexOf(this.dropZone) > -1) {
28860                             targets.remove(this.dropZone);
28861                                 }
28862                     if (targets.length == 1) {
28863                                         this.dragZone.cachedTarget = null;
28864                         var el = Roo.get(targets[0].getEl());
28865                         var box = el.getBox(true);
28866                         targets[0].onNodeDrop(el.dom, {
28867                                 target: el.dom,
28868                                 xy: [box.x, box.y + box.height - 1]
28869                         }, null, this.getDragData(e));
28870                     }
28871                 }
28872         }
28873     },
28874     
28875     handleSelection: function(e) {
28876                 this.dragZone.cachedTarget = null;
28877         var item = this.findItemFromChild(e.getTarget());
28878         if (!item) {
28879                 this.clearSelections(true);
28880                 return;
28881         }
28882                 if (item && (this.multiSelect || this.singleSelect)){
28883                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28884                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28885                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28886                                 this.unselect(item);
28887                         } else {
28888                                 this.select(item, this.multiSelect && e.ctrlKey);
28889                                 this.lastSelection = item;
28890                         }
28891                 }
28892     },
28893
28894     onItemClick : function(item, index, e){
28895                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28896                         return false;
28897                 }
28898                 return true;
28899     },
28900
28901     unselect : function(nodeInfo, suppressEvent){
28902                 var node = this.getNode(nodeInfo);
28903                 if(node && this.isSelected(node)){
28904                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28905                                 Roo.fly(node).removeClass(this.selectedClass);
28906                                 this.selections.remove(node);
28907                                 if(!suppressEvent){
28908                                         this.fireEvent("selectionchange", this, this.selections);
28909                                 }
28910                         }
28911                 }
28912     }
28913 });
28914 /*
28915  * Based on:
28916  * Ext JS Library 1.1.1
28917  * Copyright(c) 2006-2007, Ext JS, LLC.
28918  *
28919  * Originally Released Under LGPL - original licence link has changed is not relivant.
28920  *
28921  * Fork - LGPL
28922  * <script type="text/javascript">
28923  */
28924  
28925 /**
28926  * @class Roo.LayoutManager
28927  * @extends Roo.util.Observable
28928  * Base class for layout managers.
28929  */
28930 Roo.LayoutManager = function(container, config){
28931     Roo.LayoutManager.superclass.constructor.call(this);
28932     this.el = Roo.get(container);
28933     // ie scrollbar fix
28934     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28935         document.body.scroll = "no";
28936     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28937         this.el.position('relative');
28938     }
28939     this.id = this.el.id;
28940     this.el.addClass("x-layout-container");
28941     /** false to disable window resize monitoring @type Boolean */
28942     this.monitorWindowResize = true;
28943     this.regions = {};
28944     this.addEvents({
28945         /**
28946          * @event layout
28947          * Fires when a layout is performed. 
28948          * @param {Roo.LayoutManager} this
28949          */
28950         "layout" : true,
28951         /**
28952          * @event regionresized
28953          * Fires when the user resizes a region. 
28954          * @param {Roo.LayoutRegion} region The resized region
28955          * @param {Number} newSize The new size (width for east/west, height for north/south)
28956          */
28957         "regionresized" : true,
28958         /**
28959          * @event regioncollapsed
28960          * Fires when a region is collapsed. 
28961          * @param {Roo.LayoutRegion} region The collapsed region
28962          */
28963         "regioncollapsed" : true,
28964         /**
28965          * @event regionexpanded
28966          * Fires when a region is expanded.  
28967          * @param {Roo.LayoutRegion} region The expanded region
28968          */
28969         "regionexpanded" : true
28970     });
28971     this.updating = false;
28972     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28973 };
28974
28975 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28976     /**
28977      * Returns true if this layout is currently being updated
28978      * @return {Boolean}
28979      */
28980     isUpdating : function(){
28981         return this.updating; 
28982     },
28983     
28984     /**
28985      * Suspend the LayoutManager from doing auto-layouts while
28986      * making multiple add or remove calls
28987      */
28988     beginUpdate : function(){
28989         this.updating = true;    
28990     },
28991     
28992     /**
28993      * Restore auto-layouts and optionally disable the manager from performing a layout
28994      * @param {Boolean} noLayout true to disable a layout update 
28995      */
28996     endUpdate : function(noLayout){
28997         this.updating = false;
28998         if(!noLayout){
28999             this.layout();
29000         }    
29001     },
29002     
29003     layout: function(){
29004         
29005     },
29006     
29007     onRegionResized : function(region, newSize){
29008         this.fireEvent("regionresized", region, newSize);
29009         this.layout();
29010     },
29011     
29012     onRegionCollapsed : function(region){
29013         this.fireEvent("regioncollapsed", region);
29014     },
29015     
29016     onRegionExpanded : function(region){
29017         this.fireEvent("regionexpanded", region);
29018     },
29019         
29020     /**
29021      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29022      * performs box-model adjustments.
29023      * @return {Object} The size as an object {width: (the width), height: (the height)}
29024      */
29025     getViewSize : function(){
29026         var size;
29027         if(this.el.dom != document.body){
29028             size = this.el.getSize();
29029         }else{
29030             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29031         }
29032         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29033         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29034         return size;
29035     },
29036     
29037     /**
29038      * Returns the Element this layout is bound to.
29039      * @return {Roo.Element}
29040      */
29041     getEl : function(){
29042         return this.el;
29043     },
29044     
29045     /**
29046      * Returns the specified region.
29047      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29048      * @return {Roo.LayoutRegion}
29049      */
29050     getRegion : function(target){
29051         return this.regions[target.toLowerCase()];
29052     },
29053     
29054     onWindowResize : function(){
29055         if(this.monitorWindowResize){
29056             this.layout();
29057         }
29058     }
29059 });/*
29060  * Based on:
29061  * Ext JS Library 1.1.1
29062  * Copyright(c) 2006-2007, Ext JS, LLC.
29063  *
29064  * Originally Released Under LGPL - original licence link has changed is not relivant.
29065  *
29066  * Fork - LGPL
29067  * <script type="text/javascript">
29068  */
29069 /**
29070  * @class Roo.BorderLayout
29071  * @extends Roo.LayoutManager
29072  * @children Roo.ContentPanel
29073  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29074  * please see: <br><br>
29075  * <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>
29076  * <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>
29077  * Example:
29078  <pre><code>
29079  var layout = new Roo.BorderLayout(document.body, {
29080     north: {
29081         initialSize: 25,
29082         titlebar: false
29083     },
29084     west: {
29085         split:true,
29086         initialSize: 200,
29087         minSize: 175,
29088         maxSize: 400,
29089         titlebar: true,
29090         collapsible: true
29091     },
29092     east: {
29093         split:true,
29094         initialSize: 202,
29095         minSize: 175,
29096         maxSize: 400,
29097         titlebar: true,
29098         collapsible: true
29099     },
29100     south: {
29101         split:true,
29102         initialSize: 100,
29103         minSize: 100,
29104         maxSize: 200,
29105         titlebar: true,
29106         collapsible: true
29107     },
29108     center: {
29109         titlebar: true,
29110         autoScroll:true,
29111         resizeTabs: true,
29112         minTabWidth: 50,
29113         preferredTabWidth: 150
29114     }
29115 });
29116
29117 // shorthand
29118 var CP = Roo.ContentPanel;
29119
29120 layout.beginUpdate();
29121 layout.add("north", new CP("north", "North"));
29122 layout.add("south", new CP("south", {title: "South", closable: true}));
29123 layout.add("west", new CP("west", {title: "West"}));
29124 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29125 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29126 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29127 layout.getRegion("center").showPanel("center1");
29128 layout.endUpdate();
29129 </code></pre>
29130
29131 <b>The container the layout is rendered into can be either the body element or any other element.
29132 If it is not the body element, the container needs to either be an absolute positioned element,
29133 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29134 the container size if it is not the body element.</b>
29135
29136 * @constructor
29137 * Create a new BorderLayout
29138 * @param {String/HTMLElement/Element} container The container this layout is bound to
29139 * @param {Object} config Configuration options
29140  */
29141 Roo.BorderLayout = function(container, config){
29142     config = config || {};
29143     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29144     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29145     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29146         var target = this.factory.validRegions[i];
29147         if(config[target]){
29148             this.addRegion(target, config[target]);
29149         }
29150     }
29151 };
29152
29153 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29154         
29155         /**
29156          * @cfg {Roo.LayoutRegion} east
29157          */
29158         /**
29159          * @cfg {Roo.LayoutRegion} west
29160          */
29161         /**
29162          * @cfg {Roo.LayoutRegion} north
29163          */
29164         /**
29165          * @cfg {Roo.LayoutRegion} south
29166          */
29167         /**
29168          * @cfg {Roo.LayoutRegion} center
29169          */
29170     /**
29171      * Creates and adds a new region if it doesn't already exist.
29172      * @param {String} target The target region key (north, south, east, west or center).
29173      * @param {Object} config The regions config object
29174      * @return {BorderLayoutRegion} The new region
29175      */
29176     addRegion : function(target, config){
29177         if(!this.regions[target]){
29178             var r = this.factory.create(target, this, config);
29179             this.bindRegion(target, r);
29180         }
29181         return this.regions[target];
29182     },
29183
29184     // private (kinda)
29185     bindRegion : function(name, r){
29186         this.regions[name] = r;
29187         r.on("visibilitychange", this.layout, this);
29188         r.on("paneladded", this.layout, this);
29189         r.on("panelremoved", this.layout, this);
29190         r.on("invalidated", this.layout, this);
29191         r.on("resized", this.onRegionResized, this);
29192         r.on("collapsed", this.onRegionCollapsed, this);
29193         r.on("expanded", this.onRegionExpanded, this);
29194     },
29195
29196     /**
29197      * Performs a layout update.
29198      */
29199     layout : function(){
29200         if(this.updating) {
29201             return;
29202         }
29203         var size = this.getViewSize();
29204         var w = size.width;
29205         var h = size.height;
29206         var centerW = w;
29207         var centerH = h;
29208         var centerY = 0;
29209         var centerX = 0;
29210         //var x = 0, y = 0;
29211
29212         var rs = this.regions;
29213         var north = rs["north"];
29214         var south = rs["south"]; 
29215         var west = rs["west"];
29216         var east = rs["east"];
29217         var center = rs["center"];
29218         //if(this.hideOnLayout){ // not supported anymore
29219             //c.el.setStyle("display", "none");
29220         //}
29221         if(north && north.isVisible()){
29222             var b = north.getBox();
29223             var m = north.getMargins();
29224             b.width = w - (m.left+m.right);
29225             b.x = m.left;
29226             b.y = m.top;
29227             centerY = b.height + b.y + m.bottom;
29228             centerH -= centerY;
29229             north.updateBox(this.safeBox(b));
29230         }
29231         if(south && south.isVisible()){
29232             var b = south.getBox();
29233             var m = south.getMargins();
29234             b.width = w - (m.left+m.right);
29235             b.x = m.left;
29236             var totalHeight = (b.height + m.top + m.bottom);
29237             b.y = h - totalHeight + m.top;
29238             centerH -= totalHeight;
29239             south.updateBox(this.safeBox(b));
29240         }
29241         if(west && west.isVisible()){
29242             var b = west.getBox();
29243             var m = west.getMargins();
29244             b.height = centerH - (m.top+m.bottom);
29245             b.x = m.left;
29246             b.y = centerY + m.top;
29247             var totalWidth = (b.width + m.left + m.right);
29248             centerX += totalWidth;
29249             centerW -= totalWidth;
29250             west.updateBox(this.safeBox(b));
29251         }
29252         if(east && east.isVisible()){
29253             var b = east.getBox();
29254             var m = east.getMargins();
29255             b.height = centerH - (m.top+m.bottom);
29256             var totalWidth = (b.width + m.left + m.right);
29257             b.x = w - totalWidth + m.left;
29258             b.y = centerY + m.top;
29259             centerW -= totalWidth;
29260             east.updateBox(this.safeBox(b));
29261         }
29262         if(center){
29263             var m = center.getMargins();
29264             var centerBox = {
29265                 x: centerX + m.left,
29266                 y: centerY + m.top,
29267                 width: centerW - (m.left+m.right),
29268                 height: centerH - (m.top+m.bottom)
29269             };
29270             //if(this.hideOnLayout){
29271                 //center.el.setStyle("display", "block");
29272             //}
29273             center.updateBox(this.safeBox(centerBox));
29274         }
29275         this.el.repaint();
29276         this.fireEvent("layout", this);
29277     },
29278
29279     // private
29280     safeBox : function(box){
29281         box.width = Math.max(0, box.width);
29282         box.height = Math.max(0, box.height);
29283         return box;
29284     },
29285
29286     /**
29287      * Adds a ContentPanel (or subclass) to this layout.
29288      * @param {String} target The target region key (north, south, east, west or center).
29289      * @param {Roo.ContentPanel} panel The panel to add
29290      * @return {Roo.ContentPanel} The added panel
29291      */
29292     add : function(target, panel){
29293          
29294         target = target.toLowerCase();
29295         return this.regions[target].add(panel);
29296     },
29297
29298     /**
29299      * Remove a ContentPanel (or subclass) to this layout.
29300      * @param {String} target The target region key (north, south, east, west or center).
29301      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29302      * @return {Roo.ContentPanel} The removed panel
29303      */
29304     remove : function(target, panel){
29305         target = target.toLowerCase();
29306         return this.regions[target].remove(panel);
29307     },
29308
29309     /**
29310      * Searches all regions for a panel with the specified id
29311      * @param {String} panelId
29312      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29313      */
29314     findPanel : function(panelId){
29315         var rs = this.regions;
29316         for(var target in rs){
29317             if(typeof rs[target] != "function"){
29318                 var p = rs[target].getPanel(panelId);
29319                 if(p){
29320                     return p;
29321                 }
29322             }
29323         }
29324         return null;
29325     },
29326
29327     /**
29328      * Searches all regions for a panel with the specified id and activates (shows) it.
29329      * @param {String/ContentPanel} panelId The panels id or the panel itself
29330      * @return {Roo.ContentPanel} The shown panel or null
29331      */
29332     showPanel : function(panelId) {
29333       var rs = this.regions;
29334       for(var target in rs){
29335          var r = rs[target];
29336          if(typeof r != "function"){
29337             if(r.hasPanel(panelId)){
29338                return r.showPanel(panelId);
29339             }
29340          }
29341       }
29342       return null;
29343    },
29344
29345    /**
29346      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29347      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29348      */
29349     restoreState : function(provider){
29350         if(!provider){
29351             provider = Roo.state.Manager;
29352         }
29353         var sm = new Roo.LayoutStateManager();
29354         sm.init(this, provider);
29355     },
29356
29357     /**
29358      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29359      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29360      * a valid ContentPanel config object.  Example:
29361      * <pre><code>
29362 // Create the main layout
29363 var layout = new Roo.BorderLayout('main-ct', {
29364     west: {
29365         split:true,
29366         minSize: 175,
29367         titlebar: true
29368     },
29369     center: {
29370         title:'Components'
29371     }
29372 }, 'main-ct');
29373
29374 // Create and add multiple ContentPanels at once via configs
29375 layout.batchAdd({
29376    west: {
29377        id: 'source-files',
29378        autoCreate:true,
29379        title:'Ext Source Files',
29380        autoScroll:true,
29381        fitToFrame:true
29382    },
29383    center : {
29384        el: cview,
29385        autoScroll:true,
29386        fitToFrame:true,
29387        toolbar: tb,
29388        resizeEl:'cbody'
29389    }
29390 });
29391 </code></pre>
29392      * @param {Object} regions An object containing ContentPanel configs by region name
29393      */
29394     batchAdd : function(regions){
29395         this.beginUpdate();
29396         for(var rname in regions){
29397             var lr = this.regions[rname];
29398             if(lr){
29399                 this.addTypedPanels(lr, regions[rname]);
29400             }
29401         }
29402         this.endUpdate();
29403     },
29404
29405     // private
29406     addTypedPanels : function(lr, ps){
29407         if(typeof ps == 'string'){
29408             lr.add(new Roo.ContentPanel(ps));
29409         }
29410         else if(ps instanceof Array){
29411             for(var i =0, len = ps.length; i < len; i++){
29412                 this.addTypedPanels(lr, ps[i]);
29413             }
29414         }
29415         else if(!ps.events){ // raw config?
29416             var el = ps.el;
29417             delete ps.el; // prevent conflict
29418             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29419         }
29420         else {  // panel object assumed!
29421             lr.add(ps);
29422         }
29423     },
29424     /**
29425      * Adds a xtype elements to the layout.
29426      * <pre><code>
29427
29428 layout.addxtype({
29429        xtype : 'ContentPanel',
29430        region: 'west',
29431        items: [ .... ]
29432    }
29433 );
29434
29435 layout.addxtype({
29436         xtype : 'NestedLayoutPanel',
29437         region: 'west',
29438         layout: {
29439            center: { },
29440            west: { }   
29441         },
29442         items : [ ... list of content panels or nested layout panels.. ]
29443    }
29444 );
29445 </code></pre>
29446      * @param {Object} cfg Xtype definition of item to add.
29447      */
29448     addxtype : function(cfg)
29449     {
29450         // basically accepts a pannel...
29451         // can accept a layout region..!?!?
29452         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29453         
29454         if (!cfg.xtype.match(/Panel$/)) {
29455             return false;
29456         }
29457         var ret = false;
29458         
29459         if (typeof(cfg.region) == 'undefined') {
29460             Roo.log("Failed to add Panel, region was not set");
29461             Roo.log(cfg);
29462             return false;
29463         }
29464         var region = cfg.region;
29465         delete cfg.region;
29466         
29467           
29468         var xitems = [];
29469         if (cfg.items) {
29470             xitems = cfg.items;
29471             delete cfg.items;
29472         }
29473         var nb = false;
29474         
29475         switch(cfg.xtype) 
29476         {
29477             case 'ContentPanel':  // ContentPanel (el, cfg)
29478             case 'ScrollPanel':  // ContentPanel (el, cfg)
29479             case 'ViewPanel': 
29480                 if(cfg.autoCreate) {
29481                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29482                 } else {
29483                     var el = this.el.createChild();
29484                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29485                 }
29486                 
29487                 this.add(region, ret);
29488                 break;
29489             
29490             
29491             case 'TreePanel': // our new panel!
29492                 cfg.el = this.el.createChild();
29493                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29494                 this.add(region, ret);
29495                 break;
29496             
29497             case 'NestedLayoutPanel': 
29498                 // create a new Layout (which is  a Border Layout...
29499                 var el = this.el.createChild();
29500                 var clayout = cfg.layout;
29501                 delete cfg.layout;
29502                 clayout.items   = clayout.items  || [];
29503                 // replace this exitems with the clayout ones..
29504                 xitems = clayout.items;
29505                  
29506                 
29507                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29508                     cfg.background = false;
29509                 }
29510                 var layout = new Roo.BorderLayout(el, clayout);
29511                 
29512                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29513                 //console.log('adding nested layout panel '  + cfg.toSource());
29514                 this.add(region, ret);
29515                 nb = {}; /// find first...
29516                 break;
29517                 
29518             case 'GridPanel': 
29519             
29520                 // needs grid and region
29521                 
29522                 //var el = this.getRegion(region).el.createChild();
29523                 var el = this.el.createChild();
29524                 // create the grid first...
29525                 
29526                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29527                 delete cfg.grid;
29528                 if (region == 'center' && this.active ) {
29529                     cfg.background = false;
29530                 }
29531                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29532                 
29533                 this.add(region, ret);
29534                 if (cfg.background) {
29535                     ret.on('activate', function(gp) {
29536                         if (!gp.grid.rendered) {
29537                             gp.grid.render();
29538                         }
29539                     });
29540                 } else {
29541                     grid.render();
29542                 }
29543                 break;
29544            
29545            
29546            
29547                 
29548                 
29549                 
29550             default:
29551                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29552                     
29553                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29554                     this.add(region, ret);
29555                 } else {
29556                 
29557                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29558                     return null;
29559                 }
29560                 
29561              // GridPanel (grid, cfg)
29562             
29563         }
29564         this.beginUpdate();
29565         // add children..
29566         var region = '';
29567         var abn = {};
29568         Roo.each(xitems, function(i)  {
29569             region = nb && i.region ? i.region : false;
29570             
29571             var add = ret.addxtype(i);
29572            
29573             if (region) {
29574                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29575                 if (!i.background) {
29576                     abn[region] = nb[region] ;
29577                 }
29578             }
29579             
29580         });
29581         this.endUpdate();
29582
29583         // make the last non-background panel active..
29584         //if (nb) { Roo.log(abn); }
29585         if (nb) {
29586             
29587             for(var r in abn) {
29588                 region = this.getRegion(r);
29589                 if (region) {
29590                     // tried using nb[r], but it does not work..
29591                      
29592                     region.showPanel(abn[r]);
29593                    
29594                 }
29595             }
29596         }
29597         return ret;
29598         
29599     }
29600 });
29601
29602 /**
29603  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29604  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29605  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29606  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29607  * <pre><code>
29608 // shorthand
29609 var CP = Roo.ContentPanel;
29610
29611 var layout = Roo.BorderLayout.create({
29612     north: {
29613         initialSize: 25,
29614         titlebar: false,
29615         panels: [new CP("north", "North")]
29616     },
29617     west: {
29618         split:true,
29619         initialSize: 200,
29620         minSize: 175,
29621         maxSize: 400,
29622         titlebar: true,
29623         collapsible: true,
29624         panels: [new CP("west", {title: "West"})]
29625     },
29626     east: {
29627         split:true,
29628         initialSize: 202,
29629         minSize: 175,
29630         maxSize: 400,
29631         titlebar: true,
29632         collapsible: true,
29633         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29634     },
29635     south: {
29636         split:true,
29637         initialSize: 100,
29638         minSize: 100,
29639         maxSize: 200,
29640         titlebar: true,
29641         collapsible: true,
29642         panels: [new CP("south", {title: "South", closable: true})]
29643     },
29644     center: {
29645         titlebar: true,
29646         autoScroll:true,
29647         resizeTabs: true,
29648         minTabWidth: 50,
29649         preferredTabWidth: 150,
29650         panels: [
29651             new CP("center1", {title: "Close Me", closable: true}),
29652             new CP("center2", {title: "Center Panel", closable: false})
29653         ]
29654     }
29655 }, document.body);
29656
29657 layout.getRegion("center").showPanel("center1");
29658 </code></pre>
29659  * @param config
29660  * @param targetEl
29661  */
29662 Roo.BorderLayout.create = function(config, targetEl){
29663     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29664     layout.beginUpdate();
29665     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29666     for(var j = 0, jlen = regions.length; j < jlen; j++){
29667         var lr = regions[j];
29668         if(layout.regions[lr] && config[lr].panels){
29669             var r = layout.regions[lr];
29670             var ps = config[lr].panels;
29671             layout.addTypedPanels(r, ps);
29672         }
29673     }
29674     layout.endUpdate();
29675     return layout;
29676 };
29677
29678 // private
29679 Roo.BorderLayout.RegionFactory = {
29680     // private
29681     validRegions : ["north","south","east","west","center"],
29682
29683     // private
29684     create : function(target, mgr, config){
29685         target = target.toLowerCase();
29686         if(config.lightweight || config.basic){
29687             return new Roo.BasicLayoutRegion(mgr, config, target);
29688         }
29689         switch(target){
29690             case "north":
29691                 return new Roo.NorthLayoutRegion(mgr, config);
29692             case "south":
29693                 return new Roo.SouthLayoutRegion(mgr, config);
29694             case "east":
29695                 return new Roo.EastLayoutRegion(mgr, config);
29696             case "west":
29697                 return new Roo.WestLayoutRegion(mgr, config);
29698             case "center":
29699                 return new Roo.CenterLayoutRegion(mgr, config);
29700         }
29701         throw 'Layout region "'+target+'" not supported.';
29702     }
29703 };/*
29704  * Based on:
29705  * Ext JS Library 1.1.1
29706  * Copyright(c) 2006-2007, Ext JS, LLC.
29707  *
29708  * Originally Released Under LGPL - original licence link has changed is not relivant.
29709  *
29710  * Fork - LGPL
29711  * <script type="text/javascript">
29712  */
29713  
29714 /**
29715  * @class Roo.BasicLayoutRegion
29716  * @extends Roo.util.Observable
29717  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29718  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29719  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29720  */
29721 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29722     this.mgr = mgr;
29723     this.position  = pos;
29724     this.events = {
29725         /**
29726          * @scope Roo.BasicLayoutRegion
29727          */
29728         
29729         /**
29730          * @event beforeremove
29731          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29732          * @param {Roo.LayoutRegion} this
29733          * @param {Roo.ContentPanel} panel The panel
29734          * @param {Object} e The cancel event object
29735          */
29736         "beforeremove" : true,
29737         /**
29738          * @event invalidated
29739          * Fires when the layout for this region is changed.
29740          * @param {Roo.LayoutRegion} this
29741          */
29742         "invalidated" : true,
29743         /**
29744          * @event visibilitychange
29745          * Fires when this region is shown or hidden 
29746          * @param {Roo.LayoutRegion} this
29747          * @param {Boolean} visibility true or false
29748          */
29749         "visibilitychange" : true,
29750         /**
29751          * @event paneladded
29752          * Fires when a panel is added. 
29753          * @param {Roo.LayoutRegion} this
29754          * @param {Roo.ContentPanel} panel The panel
29755          */
29756         "paneladded" : true,
29757         /**
29758          * @event panelremoved
29759          * Fires when a panel is removed. 
29760          * @param {Roo.LayoutRegion} this
29761          * @param {Roo.ContentPanel} panel The panel
29762          */
29763         "panelremoved" : true,
29764         /**
29765          * @event beforecollapse
29766          * Fires when this region before collapse.
29767          * @param {Roo.LayoutRegion} this
29768          */
29769         "beforecollapse" : true,
29770         /**
29771          * @event collapsed
29772          * Fires when this region is collapsed.
29773          * @param {Roo.LayoutRegion} this
29774          */
29775         "collapsed" : true,
29776         /**
29777          * @event expanded
29778          * Fires when this region is expanded.
29779          * @param {Roo.LayoutRegion} this
29780          */
29781         "expanded" : true,
29782         /**
29783          * @event slideshow
29784          * Fires when this region is slid into view.
29785          * @param {Roo.LayoutRegion} this
29786          */
29787         "slideshow" : true,
29788         /**
29789          * @event slidehide
29790          * Fires when this region slides out of view. 
29791          * @param {Roo.LayoutRegion} this
29792          */
29793         "slidehide" : true,
29794         /**
29795          * @event panelactivated
29796          * Fires when a panel is activated. 
29797          * @param {Roo.LayoutRegion} this
29798          * @param {Roo.ContentPanel} panel The activated panel
29799          */
29800         "panelactivated" : true,
29801         /**
29802          * @event resized
29803          * Fires when the user resizes this region. 
29804          * @param {Roo.LayoutRegion} this
29805          * @param {Number} newSize The new size (width for east/west, height for north/south)
29806          */
29807         "resized" : true
29808     };
29809     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29810     this.panels = new Roo.util.MixedCollection();
29811     this.panels.getKey = this.getPanelId.createDelegate(this);
29812     this.box = null;
29813     this.activePanel = null;
29814     // ensure listeners are added...
29815     
29816     if (config.listeners || config.events) {
29817         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29818             listeners : config.listeners || {},
29819             events : config.events || {}
29820         });
29821     }
29822     
29823     if(skipConfig !== true){
29824         this.applyConfig(config);
29825     }
29826 };
29827
29828 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29829     getPanelId : function(p){
29830         return p.getId();
29831     },
29832     
29833     applyConfig : function(config){
29834         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29835         this.config = config;
29836         
29837     },
29838     
29839     /**
29840      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29841      * the width, for horizontal (north, south) the height.
29842      * @param {Number} newSize The new width or height
29843      */
29844     resizeTo : function(newSize){
29845         var el = this.el ? this.el :
29846                  (this.activePanel ? this.activePanel.getEl() : null);
29847         if(el){
29848             switch(this.position){
29849                 case "east":
29850                 case "west":
29851                     el.setWidth(newSize);
29852                     this.fireEvent("resized", this, newSize);
29853                 break;
29854                 case "north":
29855                 case "south":
29856                     el.setHeight(newSize);
29857                     this.fireEvent("resized", this, newSize);
29858                 break;                
29859             }
29860         }
29861     },
29862     
29863     getBox : function(){
29864         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29865     },
29866     
29867     getMargins : function(){
29868         return this.margins;
29869     },
29870     
29871     updateBox : function(box){
29872         this.box = box;
29873         var el = this.activePanel.getEl();
29874         el.dom.style.left = box.x + "px";
29875         el.dom.style.top = box.y + "px";
29876         this.activePanel.setSize(box.width, box.height);
29877     },
29878     
29879     /**
29880      * Returns the container element for this region.
29881      * @return {Roo.Element}
29882      */
29883     getEl : function(){
29884         return this.activePanel;
29885     },
29886     
29887     /**
29888      * Returns true if this region is currently visible.
29889      * @return {Boolean}
29890      */
29891     isVisible : function(){
29892         return this.activePanel ? true : false;
29893     },
29894     
29895     setActivePanel : function(panel){
29896         panel = this.getPanel(panel);
29897         if(this.activePanel && this.activePanel != panel){
29898             this.activePanel.setActiveState(false);
29899             this.activePanel.getEl().setLeftTop(-10000,-10000);
29900         }
29901         this.activePanel = panel;
29902         panel.setActiveState(true);
29903         if(this.box){
29904             panel.setSize(this.box.width, this.box.height);
29905         }
29906         this.fireEvent("panelactivated", this, panel);
29907         this.fireEvent("invalidated");
29908     },
29909     
29910     /**
29911      * Show the specified panel.
29912      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29913      * @return {Roo.ContentPanel} The shown panel or null
29914      */
29915     showPanel : function(panel){
29916         if(panel = this.getPanel(panel)){
29917             this.setActivePanel(panel);
29918         }
29919         return panel;
29920     },
29921     
29922     /**
29923      * Get the active panel for this region.
29924      * @return {Roo.ContentPanel} The active panel or null
29925      */
29926     getActivePanel : function(){
29927         return this.activePanel;
29928     },
29929     
29930     /**
29931      * Add the passed ContentPanel(s)
29932      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29933      * @return {Roo.ContentPanel} The panel added (if only one was added)
29934      */
29935     add : function(panel){
29936         if(arguments.length > 1){
29937             for(var i = 0, len = arguments.length; i < len; i++) {
29938                 this.add(arguments[i]);
29939             }
29940             return null;
29941         }
29942         if(this.hasPanel(panel)){
29943             this.showPanel(panel);
29944             return panel;
29945         }
29946         var el = panel.getEl();
29947         if(el.dom.parentNode != this.mgr.el.dom){
29948             this.mgr.el.dom.appendChild(el.dom);
29949         }
29950         if(panel.setRegion){
29951             panel.setRegion(this);
29952         }
29953         this.panels.add(panel);
29954         el.setStyle("position", "absolute");
29955         if(!panel.background){
29956             this.setActivePanel(panel);
29957             if(this.config.initialSize && this.panels.getCount()==1){
29958                 this.resizeTo(this.config.initialSize);
29959             }
29960         }
29961         this.fireEvent("paneladded", this, panel);
29962         return panel;
29963     },
29964     
29965     /**
29966      * Returns true if the panel is in this region.
29967      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29968      * @return {Boolean}
29969      */
29970     hasPanel : function(panel){
29971         if(typeof panel == "object"){ // must be panel obj
29972             panel = panel.getId();
29973         }
29974         return this.getPanel(panel) ? true : false;
29975     },
29976     
29977     /**
29978      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29979      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29980      * @param {Boolean} preservePanel Overrides the config preservePanel option
29981      * @return {Roo.ContentPanel} The panel that was removed
29982      */
29983     remove : function(panel, preservePanel){
29984         panel = this.getPanel(panel);
29985         if(!panel){
29986             return null;
29987         }
29988         var e = {};
29989         this.fireEvent("beforeremove", this, panel, e);
29990         if(e.cancel === true){
29991             return null;
29992         }
29993         var panelId = panel.getId();
29994         this.panels.removeKey(panelId);
29995         return panel;
29996     },
29997     
29998     /**
29999      * Returns the panel specified or null if it's not in this region.
30000      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30001      * @return {Roo.ContentPanel}
30002      */
30003     getPanel : function(id){
30004         if(typeof id == "object"){ // must be panel obj
30005             return id;
30006         }
30007         return this.panels.get(id);
30008     },
30009     
30010     /**
30011      * Returns this regions position (north/south/east/west/center).
30012      * @return {String} 
30013      */
30014     getPosition: function(){
30015         return this.position;    
30016     }
30017 });/*
30018  * Based on:
30019  * Ext JS Library 1.1.1
30020  * Copyright(c) 2006-2007, Ext JS, LLC.
30021  *
30022  * Originally Released Under LGPL - original licence link has changed is not relivant.
30023  *
30024  * Fork - LGPL
30025  * <script type="text/javascript">
30026  */
30027  
30028 /**
30029  * @class Roo.LayoutRegion
30030  * @extends Roo.BasicLayoutRegion
30031  * This class represents a region in a layout manager.
30032  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30033  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30034  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30035  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30036  * @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})
30037  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30038  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30039  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30040  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30041  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30042  * @cfg {String}    title           The title for the region (overrides panel titles)
30043  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30044  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30045  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30046  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30047  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30048  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30049  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30050  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30051  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30052  * @cfg {Boolean}   showPin         True to show a pin button
30053  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30054  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30055  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30056  * @cfg {Number}    width           For East/West panels
30057  * @cfg {Number}    height          For North/South panels
30058  * @cfg {Boolean}   split           To show the splitter
30059  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30060  */
30061 Roo.LayoutRegion = function(mgr, config, pos){
30062     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30063     var dh = Roo.DomHelper;
30064     /** This region's container element 
30065     * @type Roo.Element */
30066     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30067     /** This region's title element 
30068     * @type Roo.Element */
30069
30070     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30071         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30072         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30073     ]}, true);
30074     this.titleEl.enableDisplayMode();
30075     /** This region's title text element 
30076     * @type HTMLElement */
30077     this.titleTextEl = this.titleEl.dom.firstChild;
30078     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30079     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30080     this.closeBtn.enableDisplayMode();
30081     this.closeBtn.on("click", this.closeClicked, this);
30082     this.closeBtn.hide();
30083
30084     this.createBody(config);
30085     this.visible = true;
30086     this.collapsed = false;
30087
30088     if(config.hideWhenEmpty){
30089         this.hide();
30090         this.on("paneladded", this.validateVisibility, this);
30091         this.on("panelremoved", this.validateVisibility, this);
30092     }
30093     this.applyConfig(config);
30094 };
30095
30096 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30097
30098     createBody : function(){
30099         /** This region's body element 
30100         * @type Roo.Element */
30101         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30102     },
30103
30104     applyConfig : function(c){
30105         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30106             var dh = Roo.DomHelper;
30107             if(c.titlebar !== false){
30108                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30109                 this.collapseBtn.on("click", this.collapse, this);
30110                 this.collapseBtn.enableDisplayMode();
30111
30112                 if(c.showPin === true || this.showPin){
30113                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30114                     this.stickBtn.enableDisplayMode();
30115                     this.stickBtn.on("click", this.expand, this);
30116                     this.stickBtn.hide();
30117                 }
30118             }
30119             /** This region's collapsed element
30120             * @type Roo.Element */
30121             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30122                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30123             ]}, true);
30124             if(c.floatable !== false){
30125                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30126                this.collapsedEl.on("click", this.collapseClick, this);
30127             }
30128
30129             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30130                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30131                    id: "message", unselectable: "on", style:{"float":"left"}});
30132                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30133              }
30134             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30135             this.expandBtn.on("click", this.expand, this);
30136         }
30137         if(this.collapseBtn){
30138             this.collapseBtn.setVisible(c.collapsible == true);
30139         }
30140         this.cmargins = c.cmargins || this.cmargins ||
30141                          (this.position == "west" || this.position == "east" ?
30142                              {top: 0, left: 2, right:2, bottom: 0} :
30143                              {top: 2, left: 0, right:0, bottom: 2});
30144         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30145         this.bottomTabs = c.tabPosition != "top";
30146         this.autoScroll = c.autoScroll || false;
30147         if(this.autoScroll){
30148             this.bodyEl.setStyle("overflow", "auto");
30149         }else{
30150             this.bodyEl.setStyle("overflow", "hidden");
30151         }
30152         //if(c.titlebar !== false){
30153             if((!c.titlebar && !c.title) || c.titlebar === false){
30154                 this.titleEl.hide();
30155             }else{
30156                 this.titleEl.show();
30157                 if(c.title){
30158                     this.titleTextEl.innerHTML = c.title;
30159                 }
30160             }
30161         //}
30162         this.duration = c.duration || .30;
30163         this.slideDuration = c.slideDuration || .45;
30164         this.config = c;
30165         if(c.collapsed){
30166             this.collapse(true);
30167         }
30168         if(c.hidden){
30169             this.hide();
30170         }
30171     },
30172     /**
30173      * Returns true if this region is currently visible.
30174      * @return {Boolean}
30175      */
30176     isVisible : function(){
30177         return this.visible;
30178     },
30179
30180     /**
30181      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30182      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30183      */
30184     setCollapsedTitle : function(title){
30185         title = title || "&#160;";
30186         if(this.collapsedTitleTextEl){
30187             this.collapsedTitleTextEl.innerHTML = title;
30188         }
30189     },
30190
30191     getBox : function(){
30192         var b;
30193         if(!this.collapsed){
30194             b = this.el.getBox(false, true);
30195         }else{
30196             b = this.collapsedEl.getBox(false, true);
30197         }
30198         return b;
30199     },
30200
30201     getMargins : function(){
30202         return this.collapsed ? this.cmargins : this.margins;
30203     },
30204
30205     highlight : function(){
30206         this.el.addClass("x-layout-panel-dragover");
30207     },
30208
30209     unhighlight : function(){
30210         this.el.removeClass("x-layout-panel-dragover");
30211     },
30212
30213     updateBox : function(box){
30214         this.box = box;
30215         if(!this.collapsed){
30216             this.el.dom.style.left = box.x + "px";
30217             this.el.dom.style.top = box.y + "px";
30218             this.updateBody(box.width, box.height);
30219         }else{
30220             this.collapsedEl.dom.style.left = box.x + "px";
30221             this.collapsedEl.dom.style.top = box.y + "px";
30222             this.collapsedEl.setSize(box.width, box.height);
30223         }
30224         if(this.tabs){
30225             this.tabs.autoSizeTabs();
30226         }
30227     },
30228
30229     updateBody : function(w, h){
30230         if(w !== null){
30231             this.el.setWidth(w);
30232             w -= this.el.getBorderWidth("rl");
30233             if(this.config.adjustments){
30234                 w += this.config.adjustments[0];
30235             }
30236         }
30237         if(h !== null){
30238             this.el.setHeight(h);
30239             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30240             h -= this.el.getBorderWidth("tb");
30241             if(this.config.adjustments){
30242                 h += this.config.adjustments[1];
30243             }
30244             this.bodyEl.setHeight(h);
30245             if(this.tabs){
30246                 h = this.tabs.syncHeight(h);
30247             }
30248         }
30249         if(this.panelSize){
30250             w = w !== null ? w : this.panelSize.width;
30251             h = h !== null ? h : this.panelSize.height;
30252         }
30253         if(this.activePanel){
30254             var el = this.activePanel.getEl();
30255             w = w !== null ? w : el.getWidth();
30256             h = h !== null ? h : el.getHeight();
30257             this.panelSize = {width: w, height: h};
30258             this.activePanel.setSize(w, h);
30259         }
30260         if(Roo.isIE && this.tabs){
30261             this.tabs.el.repaint();
30262         }
30263     },
30264
30265     /**
30266      * Returns the container element for this region.
30267      * @return {Roo.Element}
30268      */
30269     getEl : function(){
30270         return this.el;
30271     },
30272
30273     /**
30274      * Hides this region.
30275      */
30276     hide : function(){
30277         if(!this.collapsed){
30278             this.el.dom.style.left = "-2000px";
30279             this.el.hide();
30280         }else{
30281             this.collapsedEl.dom.style.left = "-2000px";
30282             this.collapsedEl.hide();
30283         }
30284         this.visible = false;
30285         this.fireEvent("visibilitychange", this, false);
30286     },
30287
30288     /**
30289      * Shows this region if it was previously hidden.
30290      */
30291     show : function(){
30292         if(!this.collapsed){
30293             this.el.show();
30294         }else{
30295             this.collapsedEl.show();
30296         }
30297         this.visible = true;
30298         this.fireEvent("visibilitychange", this, true);
30299     },
30300
30301     closeClicked : function(){
30302         if(this.activePanel){
30303             this.remove(this.activePanel);
30304         }
30305     },
30306
30307     collapseClick : function(e){
30308         if(this.isSlid){
30309            e.stopPropagation();
30310            this.slideIn();
30311         }else{
30312            e.stopPropagation();
30313            this.slideOut();
30314         }
30315     },
30316
30317     /**
30318      * Collapses this region.
30319      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30320      */
30321     collapse : function(skipAnim, skipCheck){
30322         if(this.collapsed) {
30323             return;
30324         }
30325         
30326         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30327             
30328             this.collapsed = true;
30329             if(this.split){
30330                 this.split.el.hide();
30331             }
30332             if(this.config.animate && skipAnim !== true){
30333                 this.fireEvent("invalidated", this);
30334                 this.animateCollapse();
30335             }else{
30336                 this.el.setLocation(-20000,-20000);
30337                 this.el.hide();
30338                 this.collapsedEl.show();
30339                 this.fireEvent("collapsed", this);
30340                 this.fireEvent("invalidated", this);
30341             }
30342         }
30343         
30344     },
30345
30346     animateCollapse : function(){
30347         // overridden
30348     },
30349
30350     /**
30351      * Expands this region if it was previously collapsed.
30352      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30353      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30354      */
30355     expand : function(e, skipAnim){
30356         if(e) {
30357             e.stopPropagation();
30358         }
30359         if(!this.collapsed || this.el.hasActiveFx()) {
30360             return;
30361         }
30362         if(this.isSlid){
30363             this.afterSlideIn();
30364             skipAnim = true;
30365         }
30366         this.collapsed = false;
30367         if(this.config.animate && skipAnim !== true){
30368             this.animateExpand();
30369         }else{
30370             this.el.show();
30371             if(this.split){
30372                 this.split.el.show();
30373             }
30374             this.collapsedEl.setLocation(-2000,-2000);
30375             this.collapsedEl.hide();
30376             this.fireEvent("invalidated", this);
30377             this.fireEvent("expanded", this);
30378         }
30379     },
30380
30381     animateExpand : function(){
30382         // overridden
30383     },
30384
30385     initTabs : function()
30386     {
30387         this.bodyEl.setStyle("overflow", "hidden");
30388         var ts = new Roo.TabPanel(
30389                 this.bodyEl.dom,
30390                 {
30391                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30392                     disableTooltips: this.config.disableTabTips,
30393                     toolbar : this.config.toolbar
30394                 }
30395         );
30396         if(this.config.hideTabs){
30397             ts.stripWrap.setDisplayed(false);
30398         }
30399         this.tabs = ts;
30400         ts.resizeTabs = this.config.resizeTabs === true;
30401         ts.minTabWidth = this.config.minTabWidth || 40;
30402         ts.maxTabWidth = this.config.maxTabWidth || 250;
30403         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30404         ts.monitorResize = false;
30405         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30406         ts.bodyEl.addClass('x-layout-tabs-body');
30407         this.panels.each(this.initPanelAsTab, this);
30408     },
30409
30410     initPanelAsTab : function(panel){
30411         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30412                     this.config.closeOnTab && panel.isClosable());
30413         if(panel.tabTip !== undefined){
30414             ti.setTooltip(panel.tabTip);
30415         }
30416         ti.on("activate", function(){
30417               this.setActivePanel(panel);
30418         }, this);
30419         if(this.config.closeOnTab){
30420             ti.on("beforeclose", function(t, e){
30421                 e.cancel = true;
30422                 this.remove(panel);
30423             }, this);
30424         }
30425         return ti;
30426     },
30427
30428     updatePanelTitle : function(panel, title){
30429         if(this.activePanel == panel){
30430             this.updateTitle(title);
30431         }
30432         if(this.tabs){
30433             var ti = this.tabs.getTab(panel.getEl().id);
30434             ti.setText(title);
30435             if(panel.tabTip !== undefined){
30436                 ti.setTooltip(panel.tabTip);
30437             }
30438         }
30439     },
30440
30441     updateTitle : function(title){
30442         if(this.titleTextEl && !this.config.title){
30443             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30444         }
30445     },
30446
30447     setActivePanel : function(panel){
30448         panel = this.getPanel(panel);
30449         if(this.activePanel && this.activePanel != panel){
30450             this.activePanel.setActiveState(false);
30451         }
30452         this.activePanel = panel;
30453         panel.setActiveState(true);
30454         if(this.panelSize){
30455             panel.setSize(this.panelSize.width, this.panelSize.height);
30456         }
30457         if(this.closeBtn){
30458             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30459         }
30460         this.updateTitle(panel.getTitle());
30461         if(this.tabs){
30462             this.fireEvent("invalidated", this);
30463         }
30464         this.fireEvent("panelactivated", this, panel);
30465     },
30466
30467     /**
30468      * Shows the specified panel.
30469      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30470      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30471      */
30472     showPanel : function(panel)
30473     {
30474         panel = this.getPanel(panel);
30475         if(panel){
30476             if(this.tabs){
30477                 var tab = this.tabs.getTab(panel.getEl().id);
30478                 if(tab.isHidden()){
30479                     this.tabs.unhideTab(tab.id);
30480                 }
30481                 tab.activate();
30482             }else{
30483                 this.setActivePanel(panel);
30484             }
30485         }
30486         return panel;
30487     },
30488
30489     /**
30490      * Get the active panel for this region.
30491      * @return {Roo.ContentPanel} The active panel or null
30492      */
30493     getActivePanel : function(){
30494         return this.activePanel;
30495     },
30496
30497     validateVisibility : function(){
30498         if(this.panels.getCount() < 1){
30499             this.updateTitle("&#160;");
30500             this.closeBtn.hide();
30501             this.hide();
30502         }else{
30503             if(!this.isVisible()){
30504                 this.show();
30505             }
30506         }
30507     },
30508
30509     /**
30510      * Adds the passed ContentPanel(s) to this region.
30511      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30512      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30513      */
30514     add : function(panel){
30515         if(arguments.length > 1){
30516             for(var i = 0, len = arguments.length; i < len; i++) {
30517                 this.add(arguments[i]);
30518             }
30519             return null;
30520         }
30521         if(this.hasPanel(panel)){
30522             this.showPanel(panel);
30523             return panel;
30524         }
30525         panel.setRegion(this);
30526         this.panels.add(panel);
30527         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30528             this.bodyEl.dom.appendChild(panel.getEl().dom);
30529             if(panel.background !== true){
30530                 this.setActivePanel(panel);
30531             }
30532             this.fireEvent("paneladded", this, panel);
30533             return panel;
30534         }
30535         if(!this.tabs){
30536             this.initTabs();
30537         }else{
30538             this.initPanelAsTab(panel);
30539         }
30540         if(panel.background !== true){
30541             this.tabs.activate(panel.getEl().id);
30542         }
30543         this.fireEvent("paneladded", this, panel);
30544         return panel;
30545     },
30546
30547     /**
30548      * Hides the tab for the specified panel.
30549      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30550      */
30551     hidePanel : function(panel){
30552         if(this.tabs && (panel = this.getPanel(panel))){
30553             this.tabs.hideTab(panel.getEl().id);
30554         }
30555     },
30556
30557     /**
30558      * Unhides the tab for a previously hidden panel.
30559      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30560      */
30561     unhidePanel : function(panel){
30562         if(this.tabs && (panel = this.getPanel(panel))){
30563             this.tabs.unhideTab(panel.getEl().id);
30564         }
30565     },
30566
30567     clearPanels : function(){
30568         while(this.panels.getCount() > 0){
30569              this.remove(this.panels.first());
30570         }
30571     },
30572
30573     /**
30574      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30575      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30576      * @param {Boolean} preservePanel Overrides the config preservePanel option
30577      * @return {Roo.ContentPanel} The panel that was removed
30578      */
30579     remove : function(panel, preservePanel){
30580         panel = this.getPanel(panel);
30581         if(!panel){
30582             return null;
30583         }
30584         var e = {};
30585         this.fireEvent("beforeremove", this, panel, e);
30586         if(e.cancel === true){
30587             return null;
30588         }
30589         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30590         var panelId = panel.getId();
30591         this.panels.removeKey(panelId);
30592         if(preservePanel){
30593             document.body.appendChild(panel.getEl().dom);
30594         }
30595         if(this.tabs){
30596             this.tabs.removeTab(panel.getEl().id);
30597         }else if (!preservePanel){
30598             this.bodyEl.dom.removeChild(panel.getEl().dom);
30599         }
30600         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30601             var p = this.panels.first();
30602             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30603             tempEl.appendChild(p.getEl().dom);
30604             this.bodyEl.update("");
30605             this.bodyEl.dom.appendChild(p.getEl().dom);
30606             tempEl = null;
30607             this.updateTitle(p.getTitle());
30608             this.tabs = null;
30609             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30610             this.setActivePanel(p);
30611         }
30612         panel.setRegion(null);
30613         if(this.activePanel == panel){
30614             this.activePanel = null;
30615         }
30616         if(this.config.autoDestroy !== false && preservePanel !== true){
30617             try{panel.destroy();}catch(e){}
30618         }
30619         this.fireEvent("panelremoved", this, panel);
30620         return panel;
30621     },
30622
30623     /**
30624      * Returns the TabPanel component used by this region
30625      * @return {Roo.TabPanel}
30626      */
30627     getTabs : function(){
30628         return this.tabs;
30629     },
30630
30631     createTool : function(parentEl, className){
30632         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30633             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30634         btn.addClassOnOver("x-layout-tools-button-over");
30635         return btn;
30636     }
30637 });/*
30638  * Based on:
30639  * Ext JS Library 1.1.1
30640  * Copyright(c) 2006-2007, Ext JS, LLC.
30641  *
30642  * Originally Released Under LGPL - original licence link has changed is not relivant.
30643  *
30644  * Fork - LGPL
30645  * <script type="text/javascript">
30646  */
30647  
30648
30649
30650 /**
30651  * @class Roo.SplitLayoutRegion
30652  * @extends Roo.LayoutRegion
30653  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30654  */
30655 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30656     this.cursor = cursor;
30657     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30658 };
30659
30660 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30661     splitTip : "Drag to resize.",
30662     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30663     useSplitTips : false,
30664
30665     applyConfig : function(config){
30666         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30667         if(config.split){
30668             if(!this.split){
30669                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30670                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30671                 /** The SplitBar for this region 
30672                 * @type Roo.SplitBar */
30673                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30674                 this.split.on("moved", this.onSplitMove, this);
30675                 this.split.useShim = config.useShim === true;
30676                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30677                 if(this.useSplitTips){
30678                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30679                 }
30680                 if(config.collapsible){
30681                     this.split.el.on("dblclick", this.collapse,  this);
30682                 }
30683             }
30684             if(typeof config.minSize != "undefined"){
30685                 this.split.minSize = config.minSize;
30686             }
30687             if(typeof config.maxSize != "undefined"){
30688                 this.split.maxSize = config.maxSize;
30689             }
30690             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30691                 this.hideSplitter();
30692             }
30693         }
30694     },
30695
30696     getHMaxSize : function(){
30697          var cmax = this.config.maxSize || 10000;
30698          var center = this.mgr.getRegion("center");
30699          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30700     },
30701
30702     getVMaxSize : function(){
30703          var cmax = this.config.maxSize || 10000;
30704          var center = this.mgr.getRegion("center");
30705          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30706     },
30707
30708     onSplitMove : function(split, newSize){
30709         this.fireEvent("resized", this, newSize);
30710     },
30711     
30712     /** 
30713      * Returns the {@link Roo.SplitBar} for this region.
30714      * @return {Roo.SplitBar}
30715      */
30716     getSplitBar : function(){
30717         return this.split;
30718     },
30719     
30720     hide : function(){
30721         this.hideSplitter();
30722         Roo.SplitLayoutRegion.superclass.hide.call(this);
30723     },
30724
30725     hideSplitter : function(){
30726         if(this.split){
30727             this.split.el.setLocation(-2000,-2000);
30728             this.split.el.hide();
30729         }
30730     },
30731
30732     show : function(){
30733         if(this.split){
30734             this.split.el.show();
30735         }
30736         Roo.SplitLayoutRegion.superclass.show.call(this);
30737     },
30738     
30739     beforeSlide: function(){
30740         if(Roo.isGecko){// firefox overflow auto bug workaround
30741             this.bodyEl.clip();
30742             if(this.tabs) {
30743                 this.tabs.bodyEl.clip();
30744             }
30745             if(this.activePanel){
30746                 this.activePanel.getEl().clip();
30747                 
30748                 if(this.activePanel.beforeSlide){
30749                     this.activePanel.beforeSlide();
30750                 }
30751             }
30752         }
30753     },
30754     
30755     afterSlide : function(){
30756         if(Roo.isGecko){// firefox overflow auto bug workaround
30757             this.bodyEl.unclip();
30758             if(this.tabs) {
30759                 this.tabs.bodyEl.unclip();
30760             }
30761             if(this.activePanel){
30762                 this.activePanel.getEl().unclip();
30763                 if(this.activePanel.afterSlide){
30764                     this.activePanel.afterSlide();
30765                 }
30766             }
30767         }
30768     },
30769
30770     initAutoHide : function(){
30771         if(this.autoHide !== false){
30772             if(!this.autoHideHd){
30773                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30774                 this.autoHideHd = {
30775                     "mouseout": function(e){
30776                         if(!e.within(this.el, true)){
30777                             st.delay(500);
30778                         }
30779                     },
30780                     "mouseover" : function(e){
30781                         st.cancel();
30782                     },
30783                     scope : this
30784                 };
30785             }
30786             this.el.on(this.autoHideHd);
30787         }
30788     },
30789
30790     clearAutoHide : function(){
30791         if(this.autoHide !== false){
30792             this.el.un("mouseout", this.autoHideHd.mouseout);
30793             this.el.un("mouseover", this.autoHideHd.mouseover);
30794         }
30795     },
30796
30797     clearMonitor : function(){
30798         Roo.get(document).un("click", this.slideInIf, this);
30799     },
30800
30801     // these names are backwards but not changed for compat
30802     slideOut : function(){
30803         if(this.isSlid || this.el.hasActiveFx()){
30804             return;
30805         }
30806         this.isSlid = true;
30807         if(this.collapseBtn){
30808             this.collapseBtn.hide();
30809         }
30810         this.closeBtnState = this.closeBtn.getStyle('display');
30811         this.closeBtn.hide();
30812         if(this.stickBtn){
30813             this.stickBtn.show();
30814         }
30815         this.el.show();
30816         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30817         this.beforeSlide();
30818         this.el.setStyle("z-index", 10001);
30819         this.el.slideIn(this.getSlideAnchor(), {
30820             callback: function(){
30821                 this.afterSlide();
30822                 this.initAutoHide();
30823                 Roo.get(document).on("click", this.slideInIf, this);
30824                 this.fireEvent("slideshow", this);
30825             },
30826             scope: this,
30827             block: true
30828         });
30829     },
30830
30831     afterSlideIn : function(){
30832         this.clearAutoHide();
30833         this.isSlid = false;
30834         this.clearMonitor();
30835         this.el.setStyle("z-index", "");
30836         if(this.collapseBtn){
30837             this.collapseBtn.show();
30838         }
30839         this.closeBtn.setStyle('display', this.closeBtnState);
30840         if(this.stickBtn){
30841             this.stickBtn.hide();
30842         }
30843         this.fireEvent("slidehide", this);
30844     },
30845
30846     slideIn : function(cb){
30847         if(!this.isSlid || this.el.hasActiveFx()){
30848             Roo.callback(cb);
30849             return;
30850         }
30851         this.isSlid = false;
30852         this.beforeSlide();
30853         this.el.slideOut(this.getSlideAnchor(), {
30854             callback: function(){
30855                 this.el.setLeftTop(-10000, -10000);
30856                 this.afterSlide();
30857                 this.afterSlideIn();
30858                 Roo.callback(cb);
30859             },
30860             scope: this,
30861             block: true
30862         });
30863     },
30864     
30865     slideInIf : function(e){
30866         if(!e.within(this.el)){
30867             this.slideIn();
30868         }
30869     },
30870
30871     animateCollapse : function(){
30872         this.beforeSlide();
30873         this.el.setStyle("z-index", 20000);
30874         var anchor = this.getSlideAnchor();
30875         this.el.slideOut(anchor, {
30876             callback : function(){
30877                 this.el.setStyle("z-index", "");
30878                 this.collapsedEl.slideIn(anchor, {duration:.3});
30879                 this.afterSlide();
30880                 this.el.setLocation(-10000,-10000);
30881                 this.el.hide();
30882                 this.fireEvent("collapsed", this);
30883             },
30884             scope: this,
30885             block: true
30886         });
30887     },
30888
30889     animateExpand : function(){
30890         this.beforeSlide();
30891         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30892         this.el.setStyle("z-index", 20000);
30893         this.collapsedEl.hide({
30894             duration:.1
30895         });
30896         this.el.slideIn(this.getSlideAnchor(), {
30897             callback : function(){
30898                 this.el.setStyle("z-index", "");
30899                 this.afterSlide();
30900                 if(this.split){
30901                     this.split.el.show();
30902                 }
30903                 this.fireEvent("invalidated", this);
30904                 this.fireEvent("expanded", this);
30905             },
30906             scope: this,
30907             block: true
30908         });
30909     },
30910
30911     anchors : {
30912         "west" : "left",
30913         "east" : "right",
30914         "north" : "top",
30915         "south" : "bottom"
30916     },
30917
30918     sanchors : {
30919         "west" : "l",
30920         "east" : "r",
30921         "north" : "t",
30922         "south" : "b"
30923     },
30924
30925     canchors : {
30926         "west" : "tl-tr",
30927         "east" : "tr-tl",
30928         "north" : "tl-bl",
30929         "south" : "bl-tl"
30930     },
30931
30932     getAnchor : function(){
30933         return this.anchors[this.position];
30934     },
30935
30936     getCollapseAnchor : function(){
30937         return this.canchors[this.position];
30938     },
30939
30940     getSlideAnchor : function(){
30941         return this.sanchors[this.position];
30942     },
30943
30944     getAlignAdj : function(){
30945         var cm = this.cmargins;
30946         switch(this.position){
30947             case "west":
30948                 return [0, 0];
30949             break;
30950             case "east":
30951                 return [0, 0];
30952             break;
30953             case "north":
30954                 return [0, 0];
30955             break;
30956             case "south":
30957                 return [0, 0];
30958             break;
30959         }
30960     },
30961
30962     getExpandAdj : function(){
30963         var c = this.collapsedEl, cm = this.cmargins;
30964         switch(this.position){
30965             case "west":
30966                 return [-(cm.right+c.getWidth()+cm.left), 0];
30967             break;
30968             case "east":
30969                 return [cm.right+c.getWidth()+cm.left, 0];
30970             break;
30971             case "north":
30972                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30973             break;
30974             case "south":
30975                 return [0, cm.top+cm.bottom+c.getHeight()];
30976             break;
30977         }
30978     }
30979 });/*
30980  * Based on:
30981  * Ext JS Library 1.1.1
30982  * Copyright(c) 2006-2007, Ext JS, LLC.
30983  *
30984  * Originally Released Under LGPL - original licence link has changed is not relivant.
30985  *
30986  * Fork - LGPL
30987  * <script type="text/javascript">
30988  */
30989 /*
30990  * These classes are private internal classes
30991  */
30992 Roo.CenterLayoutRegion = function(mgr, config){
30993     Roo.LayoutRegion.call(this, mgr, config, "center");
30994     this.visible = true;
30995     this.minWidth = config.minWidth || 20;
30996     this.minHeight = config.minHeight || 20;
30997 };
30998
30999 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31000     hide : function(){
31001         // center panel can't be hidden
31002     },
31003     
31004     show : function(){
31005         // center panel can't be hidden
31006     },
31007     
31008     getMinWidth: function(){
31009         return this.minWidth;
31010     },
31011     
31012     getMinHeight: function(){
31013         return this.minHeight;
31014     }
31015 });
31016
31017
31018 Roo.NorthLayoutRegion = function(mgr, config){
31019     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31020     if(this.split){
31021         this.split.placement = Roo.SplitBar.TOP;
31022         this.split.orientation = Roo.SplitBar.VERTICAL;
31023         this.split.el.addClass("x-layout-split-v");
31024     }
31025     var size = config.initialSize || config.height;
31026     if(typeof size != "undefined"){
31027         this.el.setHeight(size);
31028     }
31029 };
31030 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31031     orientation: Roo.SplitBar.VERTICAL,
31032     getBox : function(){
31033         if(this.collapsed){
31034             return this.collapsedEl.getBox();
31035         }
31036         var box = this.el.getBox();
31037         if(this.split){
31038             box.height += this.split.el.getHeight();
31039         }
31040         return box;
31041     },
31042     
31043     updateBox : function(box){
31044         if(this.split && !this.collapsed){
31045             box.height -= this.split.el.getHeight();
31046             this.split.el.setLeft(box.x);
31047             this.split.el.setTop(box.y+box.height);
31048             this.split.el.setWidth(box.width);
31049         }
31050         if(this.collapsed){
31051             this.updateBody(box.width, null);
31052         }
31053         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31054     }
31055 });
31056
31057 Roo.SouthLayoutRegion = function(mgr, config){
31058     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31059     if(this.split){
31060         this.split.placement = Roo.SplitBar.BOTTOM;
31061         this.split.orientation = Roo.SplitBar.VERTICAL;
31062         this.split.el.addClass("x-layout-split-v");
31063     }
31064     var size = config.initialSize || config.height;
31065     if(typeof size != "undefined"){
31066         this.el.setHeight(size);
31067     }
31068 };
31069 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31070     orientation: Roo.SplitBar.VERTICAL,
31071     getBox : function(){
31072         if(this.collapsed){
31073             return this.collapsedEl.getBox();
31074         }
31075         var box = this.el.getBox();
31076         if(this.split){
31077             var sh = this.split.el.getHeight();
31078             box.height += sh;
31079             box.y -= sh;
31080         }
31081         return box;
31082     },
31083     
31084     updateBox : function(box){
31085         if(this.split && !this.collapsed){
31086             var sh = this.split.el.getHeight();
31087             box.height -= sh;
31088             box.y += sh;
31089             this.split.el.setLeft(box.x);
31090             this.split.el.setTop(box.y-sh);
31091             this.split.el.setWidth(box.width);
31092         }
31093         if(this.collapsed){
31094             this.updateBody(box.width, null);
31095         }
31096         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31097     }
31098 });
31099
31100 Roo.EastLayoutRegion = function(mgr, config){
31101     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31102     if(this.split){
31103         this.split.placement = Roo.SplitBar.RIGHT;
31104         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31105         this.split.el.addClass("x-layout-split-h");
31106     }
31107     var size = config.initialSize || config.width;
31108     if(typeof size != "undefined"){
31109         this.el.setWidth(size);
31110     }
31111 };
31112 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31113     orientation: Roo.SplitBar.HORIZONTAL,
31114     getBox : function(){
31115         if(this.collapsed){
31116             return this.collapsedEl.getBox();
31117         }
31118         var box = this.el.getBox();
31119         if(this.split){
31120             var sw = this.split.el.getWidth();
31121             box.width += sw;
31122             box.x -= sw;
31123         }
31124         return box;
31125     },
31126
31127     updateBox : function(box){
31128         if(this.split && !this.collapsed){
31129             var sw = this.split.el.getWidth();
31130             box.width -= sw;
31131             this.split.el.setLeft(box.x);
31132             this.split.el.setTop(box.y);
31133             this.split.el.setHeight(box.height);
31134             box.x += sw;
31135         }
31136         if(this.collapsed){
31137             this.updateBody(null, box.height);
31138         }
31139         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31140     }
31141 });
31142
31143 Roo.WestLayoutRegion = function(mgr, config){
31144     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31145     if(this.split){
31146         this.split.placement = Roo.SplitBar.LEFT;
31147         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31148         this.split.el.addClass("x-layout-split-h");
31149     }
31150     var size = config.initialSize || config.width;
31151     if(typeof size != "undefined"){
31152         this.el.setWidth(size);
31153     }
31154 };
31155 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31156     orientation: Roo.SplitBar.HORIZONTAL,
31157     getBox : function(){
31158         if(this.collapsed){
31159             return this.collapsedEl.getBox();
31160         }
31161         var box = this.el.getBox();
31162         if(this.split){
31163             box.width += this.split.el.getWidth();
31164         }
31165         return box;
31166     },
31167     
31168     updateBox : function(box){
31169         if(this.split && !this.collapsed){
31170             var sw = this.split.el.getWidth();
31171             box.width -= sw;
31172             this.split.el.setLeft(box.x+box.width);
31173             this.split.el.setTop(box.y);
31174             this.split.el.setHeight(box.height);
31175         }
31176         if(this.collapsed){
31177             this.updateBody(null, box.height);
31178         }
31179         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31180     }
31181 });
31182 /*
31183  * Based on:
31184  * Ext JS Library 1.1.1
31185  * Copyright(c) 2006-2007, Ext JS, LLC.
31186  *
31187  * Originally Released Under LGPL - original licence link has changed is not relivant.
31188  *
31189  * Fork - LGPL
31190  * <script type="text/javascript">
31191  */
31192  
31193  
31194 /*
31195  * Private internal class for reading and applying state
31196  */
31197 Roo.LayoutStateManager = function(layout){
31198      // default empty state
31199      this.state = {
31200         north: {},
31201         south: {},
31202         east: {},
31203         west: {}       
31204     };
31205 };
31206
31207 Roo.LayoutStateManager.prototype = {
31208     init : function(layout, provider){
31209         this.provider = provider;
31210         var state = provider.get(layout.id+"-layout-state");
31211         if(state){
31212             var wasUpdating = layout.isUpdating();
31213             if(!wasUpdating){
31214                 layout.beginUpdate();
31215             }
31216             for(var key in state){
31217                 if(typeof state[key] != "function"){
31218                     var rstate = state[key];
31219                     var r = layout.getRegion(key);
31220                     if(r && rstate){
31221                         if(rstate.size){
31222                             r.resizeTo(rstate.size);
31223                         }
31224                         if(rstate.collapsed == true){
31225                             r.collapse(true);
31226                         }else{
31227                             r.expand(null, true);
31228                         }
31229                     }
31230                 }
31231             }
31232             if(!wasUpdating){
31233                 layout.endUpdate();
31234             }
31235             this.state = state; 
31236         }
31237         this.layout = layout;
31238         layout.on("regionresized", this.onRegionResized, this);
31239         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31240         layout.on("regionexpanded", this.onRegionExpanded, this);
31241     },
31242     
31243     storeState : function(){
31244         this.provider.set(this.layout.id+"-layout-state", this.state);
31245     },
31246     
31247     onRegionResized : function(region, newSize){
31248         this.state[region.getPosition()].size = newSize;
31249         this.storeState();
31250     },
31251     
31252     onRegionCollapsed : function(region){
31253         this.state[region.getPosition()].collapsed = true;
31254         this.storeState();
31255     },
31256     
31257     onRegionExpanded : function(region){
31258         this.state[region.getPosition()].collapsed = false;
31259         this.storeState();
31260     }
31261 };/*
31262  * Based on:
31263  * Ext JS Library 1.1.1
31264  * Copyright(c) 2006-2007, Ext JS, LLC.
31265  *
31266  * Originally Released Under LGPL - original licence link has changed is not relivant.
31267  *
31268  * Fork - LGPL
31269  * <script type="text/javascript">
31270  */
31271 /**
31272  * @class Roo.ContentPanel
31273  * @extends Roo.util.Observable
31274  * @children Roo.form.Form Roo.JsonView Roo.View
31275  * @builder-top
31276  * A basic ContentPanel element.
31277  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31278  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31279  * @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
31280  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31281  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31282  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31283  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
31284  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31285  * @cfg {String} title          The title for this panel
31286  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31287  * @cfg {String} url            Calls {@link #setUrl} with this value
31288  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31289  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31290  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31291  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31292  * @cfg {String}    style  Extra style to add to the content panel
31293  * @cfg {Roo.menu.Menu} menu  popup menu
31294
31295  * @constructor
31296  * Create a new ContentPanel.
31297  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31298  * @param {String/Object} config A string to set only the title or a config object
31299  * @param {String} content (optional) Set the HTML content for this panel
31300  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31301  */
31302 Roo.ContentPanel = function(el, config, content){
31303     
31304      
31305     /*
31306     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31307         config = el;
31308         el = Roo.id();
31309     }
31310     if (config && config.parentLayout) { 
31311         el = config.parentLayout.el.createChild(); 
31312     }
31313     */
31314     if(el.autoCreate){ // xtype is available if this is called from factory
31315         config = el;
31316         el = Roo.id();
31317     }
31318     this.el = Roo.get(el);
31319     if(!this.el && config && config.autoCreate){
31320         if(typeof config.autoCreate == "object"){
31321             if(!config.autoCreate.id){
31322                 config.autoCreate.id = config.id||el;
31323             }
31324             this.el = Roo.DomHelper.append(document.body,
31325                         config.autoCreate, true);
31326         }else{
31327             this.el = Roo.DomHelper.append(document.body,
31328                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31329         }
31330     }
31331     
31332     
31333     this.closable = false;
31334     this.loaded = false;
31335     this.active = false;
31336     if(typeof config == "string"){
31337         this.title = config;
31338     }else{
31339         Roo.apply(this, config);
31340     }
31341     
31342     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31343         this.wrapEl = this.el.wrap();
31344         this.toolbar.container = this.el.insertSibling(false, 'before');
31345         this.toolbar = new Roo.Toolbar(this.toolbar);
31346     }
31347     
31348     // xtype created footer. - not sure if will work as we normally have to render first..
31349     if (this.footer && !this.footer.el && this.footer.xtype) {
31350         if (!this.wrapEl) {
31351             this.wrapEl = this.el.wrap();
31352         }
31353     
31354         this.footer.container = this.wrapEl.createChild();
31355          
31356         this.footer = Roo.factory(this.footer, Roo);
31357         
31358     }
31359     
31360     if(this.resizeEl){
31361         this.resizeEl = Roo.get(this.resizeEl, true);
31362     }else{
31363         this.resizeEl = this.el;
31364     }
31365     // handle view.xtype
31366     
31367  
31368     
31369     
31370     this.addEvents({
31371         /**
31372          * @event activate
31373          * Fires when this panel is activated. 
31374          * @param {Roo.ContentPanel} this
31375          */
31376         "activate" : true,
31377         /**
31378          * @event deactivate
31379          * Fires when this panel is activated. 
31380          * @param {Roo.ContentPanel} this
31381          */
31382         "deactivate" : true,
31383
31384         /**
31385          * @event resize
31386          * Fires when this panel is resized if fitToFrame is true.
31387          * @param {Roo.ContentPanel} this
31388          * @param {Number} width The width after any component adjustments
31389          * @param {Number} height The height after any component adjustments
31390          */
31391         "resize" : true,
31392         
31393          /**
31394          * @event render
31395          * Fires when this tab is created
31396          * @param {Roo.ContentPanel} this
31397          */
31398         "render" : true
31399          
31400         
31401     });
31402     
31403
31404     
31405     
31406     if(this.autoScroll){
31407         this.resizeEl.setStyle("overflow", "auto");
31408     } else {
31409         // fix randome scrolling
31410         this.el.on('scroll', function() {
31411             Roo.log('fix random scolling');
31412             this.scrollTo('top',0); 
31413         });
31414     }
31415     content = content || this.content;
31416     if(content){
31417         this.setContent(content);
31418     }
31419     if(config && config.url){
31420         this.setUrl(this.url, this.params, this.loadOnce);
31421     }
31422     
31423     
31424     
31425     Roo.ContentPanel.superclass.constructor.call(this);
31426     
31427     if (this.view && typeof(this.view.xtype) != 'undefined') {
31428         this.view.el = this.el.appendChild(document.createElement("div"));
31429         this.view = Roo.factory(this.view); 
31430         this.view.render  &&  this.view.render(false, '');  
31431     }
31432     
31433     
31434     this.fireEvent('render', this);
31435 };
31436
31437 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31438     tabTip:'',
31439     setRegion : function(region){
31440         this.region = region;
31441         if(region){
31442            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31443         }else{
31444            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31445         } 
31446     },
31447     
31448     /**
31449      * Returns the toolbar for this Panel if one was configured. 
31450      * @return {Roo.Toolbar} 
31451      */
31452     getToolbar : function(){
31453         return this.toolbar;
31454     },
31455     
31456     setActiveState : function(active){
31457         this.active = active;
31458         if(!active){
31459             this.fireEvent("deactivate", this);
31460         }else{
31461             this.fireEvent("activate", this);
31462         }
31463     },
31464     /**
31465      * Updates this panel's element
31466      * @param {String} content The new content
31467      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31468     */
31469     setContent : function(content, loadScripts){
31470         this.el.update(content, loadScripts);
31471     },
31472
31473     ignoreResize : function(w, h){
31474         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31475             return true;
31476         }else{
31477             this.lastSize = {width: w, height: h};
31478             return false;
31479         }
31480     },
31481     /**
31482      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31483      * @return {Roo.UpdateManager} The UpdateManager
31484      */
31485     getUpdateManager : function(){
31486         return this.el.getUpdateManager();
31487     },
31488      /**
31489      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31490      * @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:
31491 <pre><code>
31492 panel.load({
31493     url: "your-url.php",
31494     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31495     callback: yourFunction,
31496     scope: yourObject, //(optional scope)
31497     discardUrl: false,
31498     nocache: false,
31499     text: "Loading...",
31500     timeout: 30,
31501     scripts: false
31502 });
31503 </code></pre>
31504      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31505      * 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.
31506      * @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}
31507      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31508      * @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.
31509      * @return {Roo.ContentPanel} this
31510      */
31511     load : function(){
31512         var um = this.el.getUpdateManager();
31513         um.update.apply(um, arguments);
31514         return this;
31515     },
31516
31517
31518     /**
31519      * 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.
31520      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31521      * @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)
31522      * @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)
31523      * @return {Roo.UpdateManager} The UpdateManager
31524      */
31525     setUrl : function(url, params, loadOnce){
31526         if(this.refreshDelegate){
31527             this.removeListener("activate", this.refreshDelegate);
31528         }
31529         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31530         this.on("activate", this.refreshDelegate);
31531         return this.el.getUpdateManager();
31532     },
31533     
31534     _handleRefresh : function(url, params, loadOnce){
31535         if(!loadOnce || !this.loaded){
31536             var updater = this.el.getUpdateManager();
31537             updater.update(url, params, this._setLoaded.createDelegate(this));
31538         }
31539     },
31540     
31541     _setLoaded : function(){
31542         this.loaded = true;
31543     }, 
31544     
31545     /**
31546      * Returns this panel's id
31547      * @return {String} 
31548      */
31549     getId : function(){
31550         return this.el.id;
31551     },
31552     
31553     /** 
31554      * Returns this panel's element - used by regiosn to add.
31555      * @return {Roo.Element} 
31556      */
31557     getEl : function(){
31558         return this.wrapEl || this.el;
31559     },
31560     
31561     adjustForComponents : function(width, height)
31562     {
31563         //Roo.log('adjustForComponents ');
31564         if(this.resizeEl != this.el){
31565             width -= this.el.getFrameWidth('lr');
31566             height -= this.el.getFrameWidth('tb');
31567         }
31568         if(this.toolbar){
31569             var te = this.toolbar.getEl();
31570             height -= te.getHeight();
31571             te.setWidth(width);
31572         }
31573         if(this.footer){
31574             var te = this.footer.getEl();
31575             //Roo.log("footer:" + te.getHeight());
31576             
31577             height -= te.getHeight();
31578             te.setWidth(width);
31579         }
31580         
31581         
31582         if(this.adjustments){
31583             width += this.adjustments[0];
31584             height += this.adjustments[1];
31585         }
31586         return {"width": width, "height": height};
31587     },
31588     
31589     setSize : function(width, height){
31590         if(this.fitToFrame && !this.ignoreResize(width, height)){
31591             if(this.fitContainer && this.resizeEl != this.el){
31592                 this.el.setSize(width, height);
31593             }
31594             var size = this.adjustForComponents(width, height);
31595             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31596             this.fireEvent('resize', this, size.width, size.height);
31597         }
31598     },
31599     
31600     /**
31601      * Returns this panel's title
31602      * @return {String} 
31603      */
31604     getTitle : function(){
31605         return this.title;
31606     },
31607     
31608     /**
31609      * Set this panel's title
31610      * @param {String} title
31611      */
31612     setTitle : function(title){
31613         this.title = title;
31614         if(this.region){
31615             this.region.updatePanelTitle(this, title);
31616         }
31617     },
31618     
31619     /**
31620      * Returns true is this panel was configured to be closable
31621      * @return {Boolean} 
31622      */
31623     isClosable : function(){
31624         return this.closable;
31625     },
31626     
31627     beforeSlide : function(){
31628         this.el.clip();
31629         this.resizeEl.clip();
31630     },
31631     
31632     afterSlide : function(){
31633         this.el.unclip();
31634         this.resizeEl.unclip();
31635     },
31636     
31637     /**
31638      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31639      *   Will fail silently if the {@link #setUrl} method has not been called.
31640      *   This does not activate the panel, just updates its content.
31641      */
31642     refresh : function(){
31643         if(this.refreshDelegate){
31644            this.loaded = false;
31645            this.refreshDelegate();
31646         }
31647     },
31648     
31649     /**
31650      * Destroys this panel
31651      */
31652     destroy : function(){
31653         this.el.removeAllListeners();
31654         var tempEl = document.createElement("span");
31655         tempEl.appendChild(this.el.dom);
31656         tempEl.innerHTML = "";
31657         this.el.remove();
31658         this.el = null;
31659     },
31660     
31661     /**
31662      * form - if the content panel contains a form - this is a reference to it.
31663      * @type {Roo.form.Form}
31664      */
31665     form : false,
31666     /**
31667      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31668      *    This contains a reference to it.
31669      * @type {Roo.View}
31670      */
31671     view : false,
31672     
31673       /**
31674      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31675      * <pre><code>
31676
31677 layout.addxtype({
31678        xtype : 'Form',
31679        items: [ .... ]
31680    }
31681 );
31682
31683 </code></pre>
31684      * @param {Object} cfg Xtype definition of item to add.
31685      */
31686     
31687     addxtype : function(cfg) {
31688         // add form..
31689         if (cfg.xtype.match(/^Form$/)) {
31690             
31691             var el;
31692             //if (this.footer) {
31693             //    el = this.footer.container.insertSibling(false, 'before');
31694             //} else {
31695                 el = this.el.createChild();
31696             //}
31697
31698             this.form = new  Roo.form.Form(cfg);
31699             
31700             
31701             if ( this.form.allItems.length) {
31702                 this.form.render(el.dom);
31703             }
31704             return this.form;
31705         }
31706         // should only have one of theses..
31707         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31708             // views.. should not be just added - used named prop 'view''
31709             
31710             cfg.el = this.el.appendChild(document.createElement("div"));
31711             // factory?
31712             
31713             var ret = new Roo.factory(cfg);
31714              
31715              ret.render && ret.render(false, ''); // render blank..
31716             this.view = ret;
31717             return ret;
31718         }
31719         return false;
31720     }
31721 });
31722
31723 /**
31724  * @class Roo.GridPanel
31725  * @extends Roo.ContentPanel
31726  * @constructor
31727  * Create a new GridPanel.
31728  * @param {Roo.grid.Grid} grid The grid for this panel
31729  * @param {String/Object} config A string to set only the panel's title, or a config object
31730  */
31731 Roo.GridPanel = function(grid, config){
31732     
31733   
31734     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31735         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31736         
31737     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31738     
31739     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31740     
31741     if(this.toolbar){
31742         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31743     }
31744     // xtype created footer. - not sure if will work as we normally have to render first..
31745     if (this.footer && !this.footer.el && this.footer.xtype) {
31746         
31747         this.footer.container = this.grid.getView().getFooterPanel(true);
31748         this.footer.dataSource = this.grid.dataSource;
31749         this.footer = Roo.factory(this.footer, Roo);
31750         
31751     }
31752     
31753     grid.monitorWindowResize = false; // turn off autosizing
31754     grid.autoHeight = false;
31755     grid.autoWidth = false;
31756     this.grid = grid;
31757     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31758 };
31759
31760 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31761     getId : function(){
31762         return this.grid.id;
31763     },
31764     
31765     /**
31766      * Returns the grid for this panel
31767      * @return {Roo.grid.Grid} 
31768      */
31769     getGrid : function(){
31770         return this.grid;    
31771     },
31772     
31773     setSize : function(width, height){
31774         if(!this.ignoreResize(width, height)){
31775             var grid = this.grid;
31776             var size = this.adjustForComponents(width, height);
31777             grid.getGridEl().setSize(size.width, size.height);
31778             grid.autoSize();
31779         }
31780     },
31781     
31782     beforeSlide : function(){
31783         this.grid.getView().scroller.clip();
31784     },
31785     
31786     afterSlide : function(){
31787         this.grid.getView().scroller.unclip();
31788     },
31789     
31790     destroy : function(){
31791         this.grid.destroy();
31792         delete this.grid;
31793         Roo.GridPanel.superclass.destroy.call(this); 
31794     }
31795 });
31796
31797
31798 /**
31799  * @class Roo.NestedLayoutPanel
31800  * @extends Roo.ContentPanel
31801  * @constructor
31802  * Create a new NestedLayoutPanel.
31803  * 
31804  * 
31805  * @param {Roo.BorderLayout} layout [required] The layout for this panel
31806  * @param {String/Object} config A string to set only the title or a config object
31807  */
31808 Roo.NestedLayoutPanel = function(layout, config)
31809 {
31810     // construct with only one argument..
31811     /* FIXME - implement nicer consturctors
31812     if (layout.layout) {
31813         config = layout;
31814         layout = config.layout;
31815         delete config.layout;
31816     }
31817     if (layout.xtype && !layout.getEl) {
31818         // then layout needs constructing..
31819         layout = Roo.factory(layout, Roo);
31820     }
31821     */
31822     
31823     
31824     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31825     
31826     layout.monitorWindowResize = false; // turn off autosizing
31827     this.layout = layout;
31828     this.layout.getEl().addClass("x-layout-nested-layout");
31829     
31830     
31831     
31832     
31833 };
31834
31835 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31836
31837     setSize : function(width, height){
31838         if(!this.ignoreResize(width, height)){
31839             var size = this.adjustForComponents(width, height);
31840             var el = this.layout.getEl();
31841             el.setSize(size.width, size.height);
31842             var touch = el.dom.offsetWidth;
31843             this.layout.layout();
31844             // ie requires a double layout on the first pass
31845             if(Roo.isIE && !this.initialized){
31846                 this.initialized = true;
31847                 this.layout.layout();
31848             }
31849         }
31850     },
31851     
31852     // activate all subpanels if not currently active..
31853     
31854     setActiveState : function(active){
31855         this.active = active;
31856         if(!active){
31857             this.fireEvent("deactivate", this);
31858             return;
31859         }
31860         
31861         this.fireEvent("activate", this);
31862         // not sure if this should happen before or after..
31863         if (!this.layout) {
31864             return; // should not happen..
31865         }
31866         var reg = false;
31867         for (var r in this.layout.regions) {
31868             reg = this.layout.getRegion(r);
31869             if (reg.getActivePanel()) {
31870                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31871                 reg.setActivePanel(reg.getActivePanel());
31872                 continue;
31873             }
31874             if (!reg.panels.length) {
31875                 continue;
31876             }
31877             reg.showPanel(reg.getPanel(0));
31878         }
31879         
31880         
31881         
31882         
31883     },
31884     
31885     /**
31886      * Returns the nested BorderLayout for this panel
31887      * @return {Roo.BorderLayout} 
31888      */
31889     getLayout : function(){
31890         return this.layout;
31891     },
31892     
31893      /**
31894      * Adds a xtype elements to the layout of the nested panel
31895      * <pre><code>
31896
31897 panel.addxtype({
31898        xtype : 'ContentPanel',
31899        region: 'west',
31900        items: [ .... ]
31901    }
31902 );
31903
31904 panel.addxtype({
31905         xtype : 'NestedLayoutPanel',
31906         region: 'west',
31907         layout: {
31908            center: { },
31909            west: { }   
31910         },
31911         items : [ ... list of content panels or nested layout panels.. ]
31912    }
31913 );
31914 </code></pre>
31915      * @param {Object} cfg Xtype definition of item to add.
31916      */
31917     addxtype : function(cfg) {
31918         return this.layout.addxtype(cfg);
31919     
31920     }
31921 });
31922
31923 Roo.ScrollPanel = function(el, config, content){
31924     config = config || {};
31925     config.fitToFrame = true;
31926     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31927     
31928     this.el.dom.style.overflow = "hidden";
31929     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31930     this.el.removeClass("x-layout-inactive-content");
31931     this.el.on("mousewheel", this.onWheel, this);
31932
31933     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31934     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31935     up.unselectable(); down.unselectable();
31936     up.on("click", this.scrollUp, this);
31937     down.on("click", this.scrollDown, this);
31938     up.addClassOnOver("x-scroller-btn-over");
31939     down.addClassOnOver("x-scroller-btn-over");
31940     up.addClassOnClick("x-scroller-btn-click");
31941     down.addClassOnClick("x-scroller-btn-click");
31942     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31943
31944     this.resizeEl = this.el;
31945     this.el = wrap; this.up = up; this.down = down;
31946 };
31947
31948 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31949     increment : 100,
31950     wheelIncrement : 5,
31951     scrollUp : function(){
31952         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31953     },
31954
31955     scrollDown : function(){
31956         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31957     },
31958
31959     afterScroll : function(){
31960         var el = this.resizeEl;
31961         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31962         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31963         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31964     },
31965
31966     setSize : function(){
31967         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31968         this.afterScroll();
31969     },
31970
31971     onWheel : function(e){
31972         var d = e.getWheelDelta();
31973         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31974         this.afterScroll();
31975         e.stopEvent();
31976     },
31977
31978     setContent : function(content, loadScripts){
31979         this.resizeEl.update(content, loadScripts);
31980     }
31981
31982 });
31983
31984
31985
31986 /**
31987  * @class Roo.TreePanel
31988  * @extends Roo.ContentPanel
31989  * Treepanel component
31990  * 
31991  * @constructor
31992  * Create a new TreePanel. - defaults to fit/scoll contents.
31993  * @param {String/Object} config A string to set only the panel's title, or a config object
31994  */
31995 Roo.TreePanel = function(config){
31996     var el = config.el;
31997     var tree = config.tree;
31998     delete config.tree; 
31999     delete config.el; // hopefull!
32000     
32001     // wrapper for IE7 strict & safari scroll issue
32002     
32003     var treeEl = el.createChild();
32004     config.resizeEl = treeEl;
32005     
32006     
32007     
32008     Roo.TreePanel.superclass.constructor.call(this, el, config);
32009  
32010  
32011     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32012     //console.log(tree);
32013     this.on('activate', function()
32014     {
32015         if (this.tree.rendered) {
32016             return;
32017         }
32018         //console.log('render tree');
32019         this.tree.render();
32020     });
32021     // this should not be needed.. - it's actually the 'el' that resizes?
32022     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32023     
32024     //this.on('resize',  function (cp, w, h) {
32025     //        this.tree.innerCt.setWidth(w);
32026     //        this.tree.innerCt.setHeight(h);
32027     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32028     //});
32029
32030         
32031     
32032 };
32033
32034 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32035     fitToFrame : true,
32036     autoScroll : true,
32037     /*
32038      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
32039      */
32040     tree : false
32041
32042 });
32043
32044
32045
32046
32047
32048
32049
32050
32051
32052
32053
32054 /*
32055  * Based on:
32056  * Ext JS Library 1.1.1
32057  * Copyright(c) 2006-2007, Ext JS, LLC.
32058  *
32059  * Originally Released Under LGPL - original licence link has changed is not relivant.
32060  *
32061  * Fork - LGPL
32062  * <script type="text/javascript">
32063  */
32064  
32065
32066 /**
32067  * @class Roo.ReaderLayout
32068  * @extends Roo.BorderLayout
32069  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32070  * center region containing two nested regions (a top one for a list view and one for item preview below),
32071  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32072  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32073  * expedites the setup of the overall layout and regions for this common application style.
32074  * Example:
32075  <pre><code>
32076 var reader = new Roo.ReaderLayout();
32077 var CP = Roo.ContentPanel;  // shortcut for adding
32078
32079 reader.beginUpdate();
32080 reader.add("north", new CP("north", "North"));
32081 reader.add("west", new CP("west", {title: "West"}));
32082 reader.add("east", new CP("east", {title: "East"}));
32083
32084 reader.regions.listView.add(new CP("listView", "List"));
32085 reader.regions.preview.add(new CP("preview", "Preview"));
32086 reader.endUpdate();
32087 </code></pre>
32088 * @constructor
32089 * Create a new ReaderLayout
32090 * @param {Object} config Configuration options
32091 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32092 * document.body if omitted)
32093 */
32094 Roo.ReaderLayout = function(config, renderTo){
32095     var c = config || {size:{}};
32096     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32097         north: c.north !== false ? Roo.apply({
32098             split:false,
32099             initialSize: 32,
32100             titlebar: false
32101         }, c.north) : false,
32102         west: c.west !== false ? Roo.apply({
32103             split:true,
32104             initialSize: 200,
32105             minSize: 175,
32106             maxSize: 400,
32107             titlebar: true,
32108             collapsible: true,
32109             animate: true,
32110             margins:{left:5,right:0,bottom:5,top:5},
32111             cmargins:{left:5,right:5,bottom:5,top:5}
32112         }, c.west) : false,
32113         east: c.east !== false ? Roo.apply({
32114             split:true,
32115             initialSize: 200,
32116             minSize: 175,
32117             maxSize: 400,
32118             titlebar: true,
32119             collapsible: true,
32120             animate: true,
32121             margins:{left:0,right:5,bottom:5,top:5},
32122             cmargins:{left:5,right:5,bottom:5,top:5}
32123         }, c.east) : false,
32124         center: Roo.apply({
32125             tabPosition: 'top',
32126             autoScroll:false,
32127             closeOnTab: true,
32128             titlebar:false,
32129             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32130         }, c.center)
32131     });
32132
32133     this.el.addClass('x-reader');
32134
32135     this.beginUpdate();
32136
32137     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32138         south: c.preview !== false ? Roo.apply({
32139             split:true,
32140             initialSize: 200,
32141             minSize: 100,
32142             autoScroll:true,
32143             collapsible:true,
32144             titlebar: true,
32145             cmargins:{top:5,left:0, right:0, bottom:0}
32146         }, c.preview) : false,
32147         center: Roo.apply({
32148             autoScroll:false,
32149             titlebar:false,
32150             minHeight:200
32151         }, c.listView)
32152     });
32153     this.add('center', new Roo.NestedLayoutPanel(inner,
32154             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32155
32156     this.endUpdate();
32157
32158     this.regions.preview = inner.getRegion('south');
32159     this.regions.listView = inner.getRegion('center');
32160 };
32161
32162 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32163  * Based on:
32164  * Ext JS Library 1.1.1
32165  * Copyright(c) 2006-2007, Ext JS, LLC.
32166  *
32167  * Originally Released Under LGPL - original licence link has changed is not relivant.
32168  *
32169  * Fork - LGPL
32170  * <script type="text/javascript">
32171  */
32172  
32173 /**
32174  * @class Roo.grid.Grid
32175  * @extends Roo.util.Observable
32176  * This class represents the primary interface of a component based grid control.
32177  * <br><br>Usage:<pre><code>
32178  var grid = new Roo.grid.Grid("my-container-id", {
32179      ds: myDataStore,
32180      cm: myColModel,
32181      selModel: mySelectionModel,
32182      autoSizeColumns: true,
32183      monitorWindowResize: false,
32184      trackMouseOver: true
32185  });
32186  // set any options
32187  grid.render();
32188  * </code></pre>
32189  * <b>Common Problems:</b><br/>
32190  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32191  * element will correct this<br/>
32192  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32193  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32194  * are unpredictable.<br/>
32195  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32196  * grid to calculate dimensions/offsets.<br/>
32197   * @constructor
32198  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32199  * The container MUST have some type of size defined for the grid to fill. The container will be
32200  * automatically set to position relative if it isn't already.
32201  * @param {Object} config A config object that sets properties on this grid.
32202  */
32203 Roo.grid.Grid = function(container, config){
32204         // initialize the container
32205         this.container = Roo.get(container);
32206         this.container.update("");
32207         this.container.setStyle("overflow", "hidden");
32208     this.container.addClass('x-grid-container');
32209
32210     this.id = this.container.id;
32211
32212     Roo.apply(this, config);
32213     // check and correct shorthanded configs
32214     if(this.ds){
32215         this.dataSource = this.ds;
32216         delete this.ds;
32217     }
32218     if(this.cm){
32219         this.colModel = this.cm;
32220         delete this.cm;
32221     }
32222     if(this.sm){
32223         this.selModel = this.sm;
32224         delete this.sm;
32225     }
32226
32227     if (this.selModel) {
32228         this.selModel = Roo.factory(this.selModel, Roo.grid);
32229         this.sm = this.selModel;
32230         this.sm.xmodule = this.xmodule || false;
32231     }
32232     if (typeof(this.colModel.config) == 'undefined') {
32233         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32234         this.cm = this.colModel;
32235         this.cm.xmodule = this.xmodule || false;
32236     }
32237     if (this.dataSource) {
32238         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32239         this.ds = this.dataSource;
32240         this.ds.xmodule = this.xmodule || false;
32241          
32242     }
32243     
32244     
32245     
32246     if(this.width){
32247         this.container.setWidth(this.width);
32248     }
32249
32250     if(this.height){
32251         this.container.setHeight(this.height);
32252     }
32253     /** @private */
32254         this.addEvents({
32255         // raw events
32256         /**
32257          * @event click
32258          * The raw click event for the entire grid.
32259          * @param {Roo.EventObject} e
32260          */
32261         "click" : true,
32262         /**
32263          * @event dblclick
32264          * The raw dblclick event for the entire grid.
32265          * @param {Roo.EventObject} e
32266          */
32267         "dblclick" : true,
32268         /**
32269          * @event contextmenu
32270          * The raw contextmenu event for the entire grid.
32271          * @param {Roo.EventObject} e
32272          */
32273         "contextmenu" : true,
32274         /**
32275          * @event mousedown
32276          * The raw mousedown event for the entire grid.
32277          * @param {Roo.EventObject} e
32278          */
32279         "mousedown" : true,
32280         /**
32281          * @event mouseup
32282          * The raw mouseup event for the entire grid.
32283          * @param {Roo.EventObject} e
32284          */
32285         "mouseup" : true,
32286         /**
32287          * @event mouseover
32288          * The raw mouseover event for the entire grid.
32289          * @param {Roo.EventObject} e
32290          */
32291         "mouseover" : true,
32292         /**
32293          * @event mouseout
32294          * The raw mouseout event for the entire grid.
32295          * @param {Roo.EventObject} e
32296          */
32297         "mouseout" : true,
32298         /**
32299          * @event keypress
32300          * The raw keypress event for the entire grid.
32301          * @param {Roo.EventObject} e
32302          */
32303         "keypress" : true,
32304         /**
32305          * @event keydown
32306          * The raw keydown event for the entire grid.
32307          * @param {Roo.EventObject} e
32308          */
32309         "keydown" : true,
32310
32311         // custom events
32312
32313         /**
32314          * @event cellclick
32315          * Fires when a cell is clicked
32316          * @param {Grid} this
32317          * @param {Number} rowIndex
32318          * @param {Number} columnIndex
32319          * @param {Roo.EventObject} e
32320          */
32321         "cellclick" : true,
32322         /**
32323          * @event celldblclick
32324          * Fires when a cell is double clicked
32325          * @param {Grid} this
32326          * @param {Number} rowIndex
32327          * @param {Number} columnIndex
32328          * @param {Roo.EventObject} e
32329          */
32330         "celldblclick" : true,
32331         /**
32332          * @event rowclick
32333          * Fires when a row is clicked
32334          * @param {Grid} this
32335          * @param {Number} rowIndex
32336          * @param {Roo.EventObject} e
32337          */
32338         "rowclick" : true,
32339         /**
32340          * @event rowdblclick
32341          * Fires when a row is double clicked
32342          * @param {Grid} this
32343          * @param {Number} rowIndex
32344          * @param {Roo.EventObject} e
32345          */
32346         "rowdblclick" : true,
32347         /**
32348          * @event headerclick
32349          * Fires when a header is clicked
32350          * @param {Grid} this
32351          * @param {Number} columnIndex
32352          * @param {Roo.EventObject} e
32353          */
32354         "headerclick" : true,
32355         /**
32356          * @event headerdblclick
32357          * Fires when a header cell is double clicked
32358          * @param {Grid} this
32359          * @param {Number} columnIndex
32360          * @param {Roo.EventObject} e
32361          */
32362         "headerdblclick" : true,
32363         /**
32364          * @event rowcontextmenu
32365          * Fires when a row is right clicked
32366          * @param {Grid} this
32367          * @param {Number} rowIndex
32368          * @param {Roo.EventObject} e
32369          */
32370         "rowcontextmenu" : true,
32371         /**
32372          * @event cellcontextmenu
32373          * Fires when a cell is right clicked
32374          * @param {Grid} this
32375          * @param {Number} rowIndex
32376          * @param {Number} cellIndex
32377          * @param {Roo.EventObject} e
32378          */
32379          "cellcontextmenu" : true,
32380         /**
32381          * @event headercontextmenu
32382          * Fires when a header is right clicked
32383          * @param {Grid} this
32384          * @param {Number} columnIndex
32385          * @param {Roo.EventObject} e
32386          */
32387         "headercontextmenu" : true,
32388         /**
32389          * @event bodyscroll
32390          * Fires when the body element is scrolled
32391          * @param {Number} scrollLeft
32392          * @param {Number} scrollTop
32393          */
32394         "bodyscroll" : true,
32395         /**
32396          * @event columnresize
32397          * Fires when the user resizes a column
32398          * @param {Number} columnIndex
32399          * @param {Number} newSize
32400          */
32401         "columnresize" : true,
32402         /**
32403          * @event columnmove
32404          * Fires when the user moves a column
32405          * @param {Number} oldIndex
32406          * @param {Number} newIndex
32407          */
32408         "columnmove" : true,
32409         /**
32410          * @event startdrag
32411          * Fires when row(s) start being dragged
32412          * @param {Grid} this
32413          * @param {Roo.GridDD} dd The drag drop object
32414          * @param {event} e The raw browser event
32415          */
32416         "startdrag" : true,
32417         /**
32418          * @event enddrag
32419          * Fires when a drag operation is complete
32420          * @param {Grid} this
32421          * @param {Roo.GridDD} dd The drag drop object
32422          * @param {event} e The raw browser event
32423          */
32424         "enddrag" : true,
32425         /**
32426          * @event dragdrop
32427          * Fires when dragged row(s) are dropped on a valid DD target
32428          * @param {Grid} this
32429          * @param {Roo.GridDD} dd The drag drop object
32430          * @param {String} targetId The target drag drop object
32431          * @param {event} e The raw browser event
32432          */
32433         "dragdrop" : true,
32434         /**
32435          * @event dragover
32436          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32437          * @param {Grid} this
32438          * @param {Roo.GridDD} dd The drag drop object
32439          * @param {String} targetId The target drag drop object
32440          * @param {event} e The raw browser event
32441          */
32442         "dragover" : true,
32443         /**
32444          * @event dragenter
32445          *  Fires when the dragged row(s) first cross another DD target while being dragged
32446          * @param {Grid} this
32447          * @param {Roo.GridDD} dd The drag drop object
32448          * @param {String} targetId The target drag drop object
32449          * @param {event} e The raw browser event
32450          */
32451         "dragenter" : true,
32452         /**
32453          * @event dragout
32454          * Fires when the dragged row(s) leave another DD target while being dragged
32455          * @param {Grid} this
32456          * @param {Roo.GridDD} dd The drag drop object
32457          * @param {String} targetId The target drag drop object
32458          * @param {event} e The raw browser event
32459          */
32460         "dragout" : true,
32461         /**
32462          * @event rowclass
32463          * Fires when a row is rendered, so you can change add a style to it.
32464          * @param {GridView} gridview   The grid view
32465          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32466          */
32467         'rowclass' : true,
32468
32469         /**
32470          * @event render
32471          * Fires when the grid is rendered
32472          * @param {Grid} grid
32473          */
32474         'render' : true
32475     });
32476
32477     Roo.grid.Grid.superclass.constructor.call(this);
32478 };
32479 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32480     
32481     /**
32482          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
32483          */
32484         /**
32485          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
32486          */
32487         /**
32488          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
32489          */
32490         /**
32491          * @cfg {Roo.grid.Store} ds The data store for the grid
32492          */
32493         /**
32494          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
32495          */
32496         /**
32497      * @cfg {String} ddGroup - drag drop group.
32498      */
32499       /**
32500      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32501      */
32502
32503     /**
32504      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32505      */
32506     minColumnWidth : 25,
32507
32508     /**
32509      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32510      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32511      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32512      */
32513     autoSizeColumns : false,
32514
32515     /**
32516      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32517      */
32518     autoSizeHeaders : true,
32519
32520     /**
32521      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32522      */
32523     monitorWindowResize : true,
32524
32525     /**
32526      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32527      * rows measured to get a columns size. Default is 0 (all rows).
32528      */
32529     maxRowsToMeasure : 0,
32530
32531     /**
32532      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32533      */
32534     trackMouseOver : true,
32535
32536     /**
32537     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32538     */
32539       /**
32540     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32541     */
32542     
32543     /**
32544     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32545     */
32546     enableDragDrop : false,
32547     
32548     /**
32549     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32550     */
32551     enableColumnMove : true,
32552     
32553     /**
32554     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32555     */
32556     enableColumnHide : true,
32557     
32558     /**
32559     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32560     */
32561     enableRowHeightSync : false,
32562     
32563     /**
32564     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32565     */
32566     stripeRows : true,
32567     
32568     /**
32569     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32570     */
32571     autoHeight : false,
32572
32573     /**
32574      * @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.
32575      */
32576     autoExpandColumn : false,
32577
32578     /**
32579     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32580     * Default is 50.
32581     */
32582     autoExpandMin : 50,
32583
32584     /**
32585     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32586     */
32587     autoExpandMax : 1000,
32588
32589     /**
32590     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32591     */
32592     view : null,
32593
32594     /**
32595     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32596     */
32597     loadMask : false,
32598     /**
32599     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32600     */
32601     dropTarget: false,
32602     
32603    
32604     
32605     // private
32606     rendered : false,
32607
32608     /**
32609     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32610     * of a fixed width. Default is false.
32611     */
32612     /**
32613     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32614     */
32615     
32616     
32617     /**
32618     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32619     * %0 is replaced with the number of selected rows.
32620     */
32621     ddText : "{0} selected row{1}",
32622     
32623     
32624     /**
32625      * Called once after all setup has been completed and the grid is ready to be rendered.
32626      * @return {Roo.grid.Grid} this
32627      */
32628     render : function()
32629     {
32630         var c = this.container;
32631         // try to detect autoHeight/width mode
32632         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32633             this.autoHeight = true;
32634         }
32635         var view = this.getView();
32636         view.init(this);
32637
32638         c.on("click", this.onClick, this);
32639         c.on("dblclick", this.onDblClick, this);
32640         c.on("contextmenu", this.onContextMenu, this);
32641         c.on("keydown", this.onKeyDown, this);
32642         if (Roo.isTouch) {
32643             c.on("touchstart", this.onTouchStart, this);
32644         }
32645
32646         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32647
32648         this.getSelectionModel().init(this);
32649
32650         view.render();
32651
32652         if(this.loadMask){
32653             this.loadMask = new Roo.LoadMask(this.container,
32654                     Roo.apply({store:this.dataSource}, this.loadMask));
32655         }
32656         
32657         
32658         if (this.toolbar && this.toolbar.xtype) {
32659             this.toolbar.container = this.getView().getHeaderPanel(true);
32660             this.toolbar = new Roo.Toolbar(this.toolbar);
32661         }
32662         if (this.footer && this.footer.xtype) {
32663             this.footer.dataSource = this.getDataSource();
32664             this.footer.container = this.getView().getFooterPanel(true);
32665             this.footer = Roo.factory(this.footer, Roo);
32666         }
32667         if (this.dropTarget && this.dropTarget.xtype) {
32668             delete this.dropTarget.xtype;
32669             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32670         }
32671         
32672         
32673         this.rendered = true;
32674         this.fireEvent('render', this);
32675         return this;
32676     },
32677
32678     /**
32679      * Reconfigures the grid to use a different Store and Column Model.
32680      * The View will be bound to the new objects and refreshed.
32681      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32682      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32683      */
32684     reconfigure : function(dataSource, colModel){
32685         if(this.loadMask){
32686             this.loadMask.destroy();
32687             this.loadMask = new Roo.LoadMask(this.container,
32688                     Roo.apply({store:dataSource}, this.loadMask));
32689         }
32690         this.view.bind(dataSource, colModel);
32691         this.dataSource = dataSource;
32692         this.colModel = colModel;
32693         this.view.refresh(true);
32694     },
32695     /**
32696      * addColumns
32697      * Add's a column, default at the end..
32698      
32699      * @param {int} position to add (default end)
32700      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32701      */
32702     addColumns : function(pos, ar)
32703     {
32704         
32705         for (var i =0;i< ar.length;i++) {
32706             var cfg = ar[i];
32707             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32708             this.cm.lookup[cfg.id] = cfg;
32709         }
32710         
32711         
32712         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32713             pos = this.cm.config.length; //this.cm.config.push(cfg);
32714         } 
32715         pos = Math.max(0,pos);
32716         ar.unshift(0);
32717         ar.unshift(pos);
32718         this.cm.config.splice.apply(this.cm.config, ar);
32719         
32720         
32721         
32722         this.view.generateRules(this.cm);
32723         this.view.refresh(true);
32724         
32725     },
32726     
32727     
32728     
32729     
32730     // private
32731     onKeyDown : function(e){
32732         this.fireEvent("keydown", e);
32733     },
32734
32735     /**
32736      * Destroy this grid.
32737      * @param {Boolean} removeEl True to remove the element
32738      */
32739     destroy : function(removeEl, keepListeners){
32740         if(this.loadMask){
32741             this.loadMask.destroy();
32742         }
32743         var c = this.container;
32744         c.removeAllListeners();
32745         this.view.destroy();
32746         this.colModel.purgeListeners();
32747         if(!keepListeners){
32748             this.purgeListeners();
32749         }
32750         c.update("");
32751         if(removeEl === true){
32752             c.remove();
32753         }
32754     },
32755
32756     // private
32757     processEvent : function(name, e){
32758         // does this fire select???
32759         //Roo.log('grid:processEvent '  + name);
32760         
32761         if (name != 'touchstart' ) {
32762             this.fireEvent(name, e);    
32763         }
32764         
32765         var t = e.getTarget();
32766         var v = this.view;
32767         var header = v.findHeaderIndex(t);
32768         if(header !== false){
32769             var ename = name == 'touchstart' ? 'click' : name;
32770              
32771             this.fireEvent("header" + ename, this, header, e);
32772         }else{
32773             var row = v.findRowIndex(t);
32774             var cell = v.findCellIndex(t);
32775             if (name == 'touchstart') {
32776                 // first touch is always a click.
32777                 // hopefull this happens after selection is updated.?
32778                 name = false;
32779                 
32780                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32781                     var cs = this.selModel.getSelectedCell();
32782                     if (row == cs[0] && cell == cs[1]){
32783                         name = 'dblclick';
32784                     }
32785                 }
32786                 if (typeof(this.selModel.getSelections) != 'undefined') {
32787                     var cs = this.selModel.getSelections();
32788                     var ds = this.dataSource;
32789                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32790                         name = 'dblclick';
32791                     }
32792                 }
32793                 if (!name) {
32794                     return;
32795                 }
32796             }
32797             
32798             
32799             if(row !== false){
32800                 this.fireEvent("row" + name, this, row, e);
32801                 if(cell !== false){
32802                     this.fireEvent("cell" + name, this, row, cell, e);
32803                 }
32804             }
32805         }
32806     },
32807
32808     // private
32809     onClick : function(e){
32810         this.processEvent("click", e);
32811     },
32812    // private
32813     onTouchStart : function(e){
32814         this.processEvent("touchstart", e);
32815     },
32816
32817     // private
32818     onContextMenu : function(e, t){
32819         this.processEvent("contextmenu", e);
32820     },
32821
32822     // private
32823     onDblClick : function(e){
32824         this.processEvent("dblclick", e);
32825     },
32826
32827     // private
32828     walkCells : function(row, col, step, fn, scope){
32829         var cm = this.colModel, clen = cm.getColumnCount();
32830         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32831         if(step < 0){
32832             if(col < 0){
32833                 row--;
32834                 first = false;
32835             }
32836             while(row >= 0){
32837                 if(!first){
32838                     col = clen-1;
32839                 }
32840                 first = false;
32841                 while(col >= 0){
32842                     if(fn.call(scope || this, row, col, cm) === true){
32843                         return [row, col];
32844                     }
32845                     col--;
32846                 }
32847                 row--;
32848             }
32849         } else {
32850             if(col >= clen){
32851                 row++;
32852                 first = false;
32853             }
32854             while(row < rlen){
32855                 if(!first){
32856                     col = 0;
32857                 }
32858                 first = false;
32859                 while(col < clen){
32860                     if(fn.call(scope || this, row, col, cm) === true){
32861                         return [row, col];
32862                     }
32863                     col++;
32864                 }
32865                 row++;
32866             }
32867         }
32868         return null;
32869     },
32870
32871     // private
32872     getSelections : function(){
32873         return this.selModel.getSelections();
32874     },
32875
32876     /**
32877      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32878      * but if manual update is required this method will initiate it.
32879      */
32880     autoSize : function(){
32881         if(this.rendered){
32882             this.view.layout();
32883             if(this.view.adjustForScroll){
32884                 this.view.adjustForScroll();
32885             }
32886         }
32887     },
32888
32889     /**
32890      * Returns the grid's underlying element.
32891      * @return {Element} The element
32892      */
32893     getGridEl : function(){
32894         return this.container;
32895     },
32896
32897     // private for compatibility, overridden by editor grid
32898     stopEditing : function(){},
32899
32900     /**
32901      * Returns the grid's SelectionModel.
32902      * @return {SelectionModel}
32903      */
32904     getSelectionModel : function(){
32905         if(!this.selModel){
32906             this.selModel = new Roo.grid.RowSelectionModel();
32907         }
32908         return this.selModel;
32909     },
32910
32911     /**
32912      * Returns the grid's DataSource.
32913      * @return {DataSource}
32914      */
32915     getDataSource : function(){
32916         return this.dataSource;
32917     },
32918
32919     /**
32920      * Returns the grid's ColumnModel.
32921      * @return {ColumnModel}
32922      */
32923     getColumnModel : function(){
32924         return this.colModel;
32925     },
32926
32927     /**
32928      * Returns the grid's GridView object.
32929      * @return {GridView}
32930      */
32931     getView : function(){
32932         if(!this.view){
32933             this.view = new Roo.grid.GridView(this.viewConfig);
32934             this.relayEvents(this.view, [
32935                 "beforerowremoved", "beforerowsinserted",
32936                 "beforerefresh", "rowremoved",
32937                 "rowsinserted", "rowupdated" ,"refresh"
32938             ]);
32939         }
32940         return this.view;
32941     },
32942     /**
32943      * Called to get grid's drag proxy text, by default returns this.ddText.
32944      * Override this to put something different in the dragged text.
32945      * @return {String}
32946      */
32947     getDragDropText : function(){
32948         var count = this.selModel.getCount();
32949         return String.format(this.ddText, count, count == 1 ? '' : 's');
32950     }
32951 });
32952 /*
32953  * Based on:
32954  * Ext JS Library 1.1.1
32955  * Copyright(c) 2006-2007, Ext JS, LLC.
32956  *
32957  * Originally Released Under LGPL - original licence link has changed is not relivant.
32958  *
32959  * Fork - LGPL
32960  * <script type="text/javascript">
32961  */
32962  /**
32963  * @class Roo.grid.AbstractGridView
32964  * @extends Roo.util.Observable
32965  * @abstract
32966  * Abstract base class for grid Views
32967  * @constructor
32968  */
32969 Roo.grid.AbstractGridView = function(){
32970         this.grid = null;
32971         
32972         this.events = {
32973             "beforerowremoved" : true,
32974             "beforerowsinserted" : true,
32975             "beforerefresh" : true,
32976             "rowremoved" : true,
32977             "rowsinserted" : true,
32978             "rowupdated" : true,
32979             "refresh" : true
32980         };
32981     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32982 };
32983
32984 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32985     rowClass : "x-grid-row",
32986     cellClass : "x-grid-cell",
32987     tdClass : "x-grid-td",
32988     hdClass : "x-grid-hd",
32989     splitClass : "x-grid-hd-split",
32990     
32991     init: function(grid){
32992         this.grid = grid;
32993                 var cid = this.grid.getGridEl().id;
32994         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32995         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32996         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32997         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32998         },
32999         
33000     getColumnRenderers : function(){
33001         var renderers = [];
33002         var cm = this.grid.colModel;
33003         var colCount = cm.getColumnCount();
33004         for(var i = 0; i < colCount; i++){
33005             renderers[i] = cm.getRenderer(i);
33006         }
33007         return renderers;
33008     },
33009     
33010     getColumnIds : function(){
33011         var ids = [];
33012         var cm = this.grid.colModel;
33013         var colCount = cm.getColumnCount();
33014         for(var i = 0; i < colCount; i++){
33015             ids[i] = cm.getColumnId(i);
33016         }
33017         return ids;
33018     },
33019     
33020     getDataIndexes : function(){
33021         if(!this.indexMap){
33022             this.indexMap = this.buildIndexMap();
33023         }
33024         return this.indexMap.colToData;
33025     },
33026     
33027     getColumnIndexByDataIndex : function(dataIndex){
33028         if(!this.indexMap){
33029             this.indexMap = this.buildIndexMap();
33030         }
33031         return this.indexMap.dataToCol[dataIndex];
33032     },
33033     
33034     /**
33035      * Set a css style for a column dynamically. 
33036      * @param {Number} colIndex The index of the column
33037      * @param {String} name The css property name
33038      * @param {String} value The css value
33039      */
33040     setCSSStyle : function(colIndex, name, value){
33041         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33042         Roo.util.CSS.updateRule(selector, name, value);
33043     },
33044     
33045     generateRules : function(cm){
33046         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33047         Roo.util.CSS.removeStyleSheet(rulesId);
33048         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33049             var cid = cm.getColumnId(i);
33050             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33051                          this.tdSelector, cid, " {\n}\n",
33052                          this.hdSelector, cid, " {\n}\n",
33053                          this.splitSelector, cid, " {\n}\n");
33054         }
33055         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33056     }
33057 });/*
33058  * Based on:
33059  * Ext JS Library 1.1.1
33060  * Copyright(c) 2006-2007, Ext JS, LLC.
33061  *
33062  * Originally Released Under LGPL - original licence link has changed is not relivant.
33063  *
33064  * Fork - LGPL
33065  * <script type="text/javascript">
33066  */
33067
33068 // private
33069 // This is a support class used internally by the Grid components
33070 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33071     this.grid = grid;
33072     this.view = grid.getView();
33073     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33074     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33075     if(hd2){
33076         this.setHandleElId(Roo.id(hd));
33077         this.setOuterHandleElId(Roo.id(hd2));
33078     }
33079     this.scroll = false;
33080 };
33081 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33082     maxDragWidth: 120,
33083     getDragData : function(e){
33084         var t = Roo.lib.Event.getTarget(e);
33085         var h = this.view.findHeaderCell(t);
33086         if(h){
33087             return {ddel: h.firstChild, header:h};
33088         }
33089         return false;
33090     },
33091
33092     onInitDrag : function(e){
33093         this.view.headersDisabled = true;
33094         var clone = this.dragData.ddel.cloneNode(true);
33095         clone.id = Roo.id();
33096         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33097         this.proxy.update(clone);
33098         return true;
33099     },
33100
33101     afterValidDrop : function(){
33102         var v = this.view;
33103         setTimeout(function(){
33104             v.headersDisabled = false;
33105         }, 50);
33106     },
33107
33108     afterInvalidDrop : function(){
33109         var v = this.view;
33110         setTimeout(function(){
33111             v.headersDisabled = false;
33112         }, 50);
33113     }
33114 });
33115 /*
33116  * Based on:
33117  * Ext JS Library 1.1.1
33118  * Copyright(c) 2006-2007, Ext JS, LLC.
33119  *
33120  * Originally Released Under LGPL - original licence link has changed is not relivant.
33121  *
33122  * Fork - LGPL
33123  * <script type="text/javascript">
33124  */
33125 // private
33126 // This is a support class used internally by the Grid components
33127 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33128     this.grid = grid;
33129     this.view = grid.getView();
33130     // split the proxies so they don't interfere with mouse events
33131     this.proxyTop = Roo.DomHelper.append(document.body, {
33132         cls:"col-move-top", html:"&#160;"
33133     }, true);
33134     this.proxyBottom = Roo.DomHelper.append(document.body, {
33135         cls:"col-move-bottom", html:"&#160;"
33136     }, true);
33137     this.proxyTop.hide = this.proxyBottom.hide = function(){
33138         this.setLeftTop(-100,-100);
33139         this.setStyle("visibility", "hidden");
33140     };
33141     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33142     // temporarily disabled
33143     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33144     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33145 };
33146 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33147     proxyOffsets : [-4, -9],
33148     fly: Roo.Element.fly,
33149
33150     getTargetFromEvent : function(e){
33151         var t = Roo.lib.Event.getTarget(e);
33152         var cindex = this.view.findCellIndex(t);
33153         if(cindex !== false){
33154             return this.view.getHeaderCell(cindex);
33155         }
33156         return null;
33157     },
33158
33159     nextVisible : function(h){
33160         var v = this.view, cm = this.grid.colModel;
33161         h = h.nextSibling;
33162         while(h){
33163             if(!cm.isHidden(v.getCellIndex(h))){
33164                 return h;
33165             }
33166             h = h.nextSibling;
33167         }
33168         return null;
33169     },
33170
33171     prevVisible : function(h){
33172         var v = this.view, cm = this.grid.colModel;
33173         h = h.prevSibling;
33174         while(h){
33175             if(!cm.isHidden(v.getCellIndex(h))){
33176                 return h;
33177             }
33178             h = h.prevSibling;
33179         }
33180         return null;
33181     },
33182
33183     positionIndicator : function(h, n, e){
33184         var x = Roo.lib.Event.getPageX(e);
33185         var r = Roo.lib.Dom.getRegion(n.firstChild);
33186         var px, pt, py = r.top + this.proxyOffsets[1];
33187         if((r.right - x) <= (r.right-r.left)/2){
33188             px = r.right+this.view.borderWidth;
33189             pt = "after";
33190         }else{
33191             px = r.left;
33192             pt = "before";
33193         }
33194         var oldIndex = this.view.getCellIndex(h);
33195         var newIndex = this.view.getCellIndex(n);
33196
33197         if(this.grid.colModel.isFixed(newIndex)){
33198             return false;
33199         }
33200
33201         var locked = this.grid.colModel.isLocked(newIndex);
33202
33203         if(pt == "after"){
33204             newIndex++;
33205         }
33206         if(oldIndex < newIndex){
33207             newIndex--;
33208         }
33209         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33210             return false;
33211         }
33212         px +=  this.proxyOffsets[0];
33213         this.proxyTop.setLeftTop(px, py);
33214         this.proxyTop.show();
33215         if(!this.bottomOffset){
33216             this.bottomOffset = this.view.mainHd.getHeight();
33217         }
33218         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33219         this.proxyBottom.show();
33220         return pt;
33221     },
33222
33223     onNodeEnter : function(n, dd, e, data){
33224         if(data.header != n){
33225             this.positionIndicator(data.header, n, e);
33226         }
33227     },
33228
33229     onNodeOver : function(n, dd, e, data){
33230         var result = false;
33231         if(data.header != n){
33232             result = this.positionIndicator(data.header, n, e);
33233         }
33234         if(!result){
33235             this.proxyTop.hide();
33236             this.proxyBottom.hide();
33237         }
33238         return result ? this.dropAllowed : this.dropNotAllowed;
33239     },
33240
33241     onNodeOut : function(n, dd, e, data){
33242         this.proxyTop.hide();
33243         this.proxyBottom.hide();
33244     },
33245
33246     onNodeDrop : function(n, dd, e, data){
33247         var h = data.header;
33248         if(h != n){
33249             var cm = this.grid.colModel;
33250             var x = Roo.lib.Event.getPageX(e);
33251             var r = Roo.lib.Dom.getRegion(n.firstChild);
33252             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33253             var oldIndex = this.view.getCellIndex(h);
33254             var newIndex = this.view.getCellIndex(n);
33255             var locked = cm.isLocked(newIndex);
33256             if(pt == "after"){
33257                 newIndex++;
33258             }
33259             if(oldIndex < newIndex){
33260                 newIndex--;
33261             }
33262             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33263                 return false;
33264             }
33265             cm.setLocked(oldIndex, locked, true);
33266             cm.moveColumn(oldIndex, newIndex);
33267             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33268             return true;
33269         }
33270         return false;
33271     }
33272 });
33273 /*
33274  * Based on:
33275  * Ext JS Library 1.1.1
33276  * Copyright(c) 2006-2007, Ext JS, LLC.
33277  *
33278  * Originally Released Under LGPL - original licence link has changed is not relivant.
33279  *
33280  * Fork - LGPL
33281  * <script type="text/javascript">
33282  */
33283   
33284 /**
33285  * @class Roo.grid.GridView
33286  * @extends Roo.util.Observable
33287  *
33288  * @constructor
33289  * @param {Object} config
33290  */
33291 Roo.grid.GridView = function(config){
33292     Roo.grid.GridView.superclass.constructor.call(this);
33293     this.el = null;
33294
33295     Roo.apply(this, config);
33296 };
33297
33298 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33299
33300     unselectable :  'unselectable="on"',
33301     unselectableCls :  'x-unselectable',
33302     
33303     
33304     rowClass : "x-grid-row",
33305
33306     cellClass : "x-grid-col",
33307
33308     tdClass : "x-grid-td",
33309
33310     hdClass : "x-grid-hd",
33311
33312     splitClass : "x-grid-split",
33313
33314     sortClasses : ["sort-asc", "sort-desc"],
33315
33316     enableMoveAnim : false,
33317
33318     hlColor: "C3DAF9",
33319
33320     dh : Roo.DomHelper,
33321
33322     fly : Roo.Element.fly,
33323
33324     css : Roo.util.CSS,
33325
33326     borderWidth: 1,
33327
33328     splitOffset: 3,
33329
33330     scrollIncrement : 22,
33331
33332     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33333
33334     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33335
33336     bind : function(ds, cm){
33337         if(this.ds){
33338             this.ds.un("load", this.onLoad, this);
33339             this.ds.un("datachanged", this.onDataChange, this);
33340             this.ds.un("add", this.onAdd, this);
33341             this.ds.un("remove", this.onRemove, this);
33342             this.ds.un("update", this.onUpdate, this);
33343             this.ds.un("clear", this.onClear, this);
33344         }
33345         if(ds){
33346             ds.on("load", this.onLoad, this);
33347             ds.on("datachanged", this.onDataChange, this);
33348             ds.on("add", this.onAdd, this);
33349             ds.on("remove", this.onRemove, this);
33350             ds.on("update", this.onUpdate, this);
33351             ds.on("clear", this.onClear, this);
33352         }
33353         this.ds = ds;
33354
33355         if(this.cm){
33356             this.cm.un("widthchange", this.onColWidthChange, this);
33357             this.cm.un("headerchange", this.onHeaderChange, this);
33358             this.cm.un("hiddenchange", this.onHiddenChange, this);
33359             this.cm.un("columnmoved", this.onColumnMove, this);
33360             this.cm.un("columnlockchange", this.onColumnLock, this);
33361         }
33362         if(cm){
33363             this.generateRules(cm);
33364             cm.on("widthchange", this.onColWidthChange, this);
33365             cm.on("headerchange", this.onHeaderChange, this);
33366             cm.on("hiddenchange", this.onHiddenChange, this);
33367             cm.on("columnmoved", this.onColumnMove, this);
33368             cm.on("columnlockchange", this.onColumnLock, this);
33369         }
33370         this.cm = cm;
33371     },
33372
33373     init: function(grid){
33374         Roo.grid.GridView.superclass.init.call(this, grid);
33375
33376         this.bind(grid.dataSource, grid.colModel);
33377
33378         grid.on("headerclick", this.handleHeaderClick, this);
33379
33380         if(grid.trackMouseOver){
33381             grid.on("mouseover", this.onRowOver, this);
33382             grid.on("mouseout", this.onRowOut, this);
33383         }
33384         grid.cancelTextSelection = function(){};
33385         this.gridId = grid.id;
33386
33387         var tpls = this.templates || {};
33388
33389         if(!tpls.master){
33390             tpls.master = new Roo.Template(
33391                '<div class="x-grid" hidefocus="true">',
33392                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33393                   '<div class="x-grid-topbar"></div>',
33394                   '<div class="x-grid-scroller"><div></div></div>',
33395                   '<div class="x-grid-locked">',
33396                       '<div class="x-grid-header">{lockedHeader}</div>',
33397                       '<div class="x-grid-body">{lockedBody}</div>',
33398                   "</div>",
33399                   '<div class="x-grid-viewport">',
33400                       '<div class="x-grid-header">{header}</div>',
33401                       '<div class="x-grid-body">{body}</div>',
33402                   "</div>",
33403                   '<div class="x-grid-bottombar"></div>',
33404                  
33405                   '<div class="x-grid-resize-proxy">&#160;</div>',
33406                "</div>"
33407             );
33408             tpls.master.disableformats = true;
33409         }
33410
33411         if(!tpls.header){
33412             tpls.header = new Roo.Template(
33413                '<table border="0" cellspacing="0" cellpadding="0">',
33414                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33415                "</table>{splits}"
33416             );
33417             tpls.header.disableformats = true;
33418         }
33419         tpls.header.compile();
33420
33421         if(!tpls.hcell){
33422             tpls.hcell = new Roo.Template(
33423                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33424                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33425                 "</div></td>"
33426              );
33427              tpls.hcell.disableFormats = true;
33428         }
33429         tpls.hcell.compile();
33430
33431         if(!tpls.hsplit){
33432             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33433                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33434             tpls.hsplit.disableFormats = true;
33435         }
33436         tpls.hsplit.compile();
33437
33438         if(!tpls.body){
33439             tpls.body = new Roo.Template(
33440                '<table border="0" cellspacing="0" cellpadding="0">',
33441                "<tbody>{rows}</tbody>",
33442                "</table>"
33443             );
33444             tpls.body.disableFormats = true;
33445         }
33446         tpls.body.compile();
33447
33448         if(!tpls.row){
33449             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33450             tpls.row.disableFormats = true;
33451         }
33452         tpls.row.compile();
33453
33454         if(!tpls.cell){
33455             tpls.cell = new Roo.Template(
33456                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33457                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33458                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33459                 "</td>"
33460             );
33461             tpls.cell.disableFormats = true;
33462         }
33463         tpls.cell.compile();
33464
33465         this.templates = tpls;
33466     },
33467
33468     // remap these for backwards compat
33469     onColWidthChange : function(){
33470         this.updateColumns.apply(this, arguments);
33471     },
33472     onHeaderChange : function(){
33473         this.updateHeaders.apply(this, arguments);
33474     }, 
33475     onHiddenChange : function(){
33476         this.handleHiddenChange.apply(this, arguments);
33477     },
33478     onColumnMove : function(){
33479         this.handleColumnMove.apply(this, arguments);
33480     },
33481     onColumnLock : function(){
33482         this.handleLockChange.apply(this, arguments);
33483     },
33484
33485     onDataChange : function(){
33486         this.refresh();
33487         this.updateHeaderSortState();
33488     },
33489
33490     onClear : function(){
33491         this.refresh();
33492     },
33493
33494     onUpdate : function(ds, record){
33495         this.refreshRow(record);
33496     },
33497
33498     refreshRow : function(record){
33499         var ds = this.ds, index;
33500         if(typeof record == 'number'){
33501             index = record;
33502             record = ds.getAt(index);
33503         }else{
33504             index = ds.indexOf(record);
33505         }
33506         this.insertRows(ds, index, index, true);
33507         this.onRemove(ds, record, index+1, true);
33508         this.syncRowHeights(index, index);
33509         this.layout();
33510         this.fireEvent("rowupdated", this, index, record);
33511     },
33512
33513     onAdd : function(ds, records, index){
33514         this.insertRows(ds, index, index + (records.length-1));
33515     },
33516
33517     onRemove : function(ds, record, index, isUpdate){
33518         if(isUpdate !== true){
33519             this.fireEvent("beforerowremoved", this, index, record);
33520         }
33521         var bt = this.getBodyTable(), lt = this.getLockedTable();
33522         if(bt.rows[index]){
33523             bt.firstChild.removeChild(bt.rows[index]);
33524         }
33525         if(lt.rows[index]){
33526             lt.firstChild.removeChild(lt.rows[index]);
33527         }
33528         if(isUpdate !== true){
33529             this.stripeRows(index);
33530             this.syncRowHeights(index, index);
33531             this.layout();
33532             this.fireEvent("rowremoved", this, index, record);
33533         }
33534     },
33535
33536     onLoad : function(){
33537         this.scrollToTop();
33538     },
33539
33540     /**
33541      * Scrolls the grid to the top
33542      */
33543     scrollToTop : function(){
33544         if(this.scroller){
33545             this.scroller.dom.scrollTop = 0;
33546             this.syncScroll();
33547         }
33548     },
33549
33550     /**
33551      * Gets a panel in the header of the grid that can be used for toolbars etc.
33552      * After modifying the contents of this panel a call to grid.autoSize() may be
33553      * required to register any changes in size.
33554      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33555      * @return Roo.Element
33556      */
33557     getHeaderPanel : function(doShow){
33558         if(doShow){
33559             this.headerPanel.show();
33560         }
33561         return this.headerPanel;
33562     },
33563
33564     /**
33565      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33566      * After modifying the contents of this panel a call to grid.autoSize() may be
33567      * required to register any changes in size.
33568      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33569      * @return Roo.Element
33570      */
33571     getFooterPanel : function(doShow){
33572         if(doShow){
33573             this.footerPanel.show();
33574         }
33575         return this.footerPanel;
33576     },
33577
33578     initElements : function(){
33579         var E = Roo.Element;
33580         var el = this.grid.getGridEl().dom.firstChild;
33581         var cs = el.childNodes;
33582
33583         this.el = new E(el);
33584         
33585          this.focusEl = new E(el.firstChild);
33586         this.focusEl.swallowEvent("click", true);
33587         
33588         this.headerPanel = new E(cs[1]);
33589         this.headerPanel.enableDisplayMode("block");
33590
33591         this.scroller = new E(cs[2]);
33592         this.scrollSizer = new E(this.scroller.dom.firstChild);
33593
33594         this.lockedWrap = new E(cs[3]);
33595         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33596         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33597
33598         this.mainWrap = new E(cs[4]);
33599         this.mainHd = new E(this.mainWrap.dom.firstChild);
33600         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33601
33602         this.footerPanel = new E(cs[5]);
33603         this.footerPanel.enableDisplayMode("block");
33604
33605         this.resizeProxy = new E(cs[6]);
33606
33607         this.headerSelector = String.format(
33608            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33609            this.lockedHd.id, this.mainHd.id
33610         );
33611
33612         this.splitterSelector = String.format(
33613            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33614            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33615         );
33616     },
33617     idToCssName : function(s)
33618     {
33619         return s.replace(/[^a-z0-9]+/ig, '-');
33620     },
33621
33622     getHeaderCell : function(index){
33623         return Roo.DomQuery.select(this.headerSelector)[index];
33624     },
33625
33626     getHeaderCellMeasure : function(index){
33627         return this.getHeaderCell(index).firstChild;
33628     },
33629
33630     getHeaderCellText : function(index){
33631         return this.getHeaderCell(index).firstChild.firstChild;
33632     },
33633
33634     getLockedTable : function(){
33635         return this.lockedBody.dom.firstChild;
33636     },
33637
33638     getBodyTable : function(){
33639         return this.mainBody.dom.firstChild;
33640     },
33641
33642     getLockedRow : function(index){
33643         return this.getLockedTable().rows[index];
33644     },
33645
33646     getRow : function(index){
33647         return this.getBodyTable().rows[index];
33648     },
33649
33650     getRowComposite : function(index){
33651         if(!this.rowEl){
33652             this.rowEl = new Roo.CompositeElementLite();
33653         }
33654         var els = [], lrow, mrow;
33655         if(lrow = this.getLockedRow(index)){
33656             els.push(lrow);
33657         }
33658         if(mrow = this.getRow(index)){
33659             els.push(mrow);
33660         }
33661         this.rowEl.elements = els;
33662         return this.rowEl;
33663     },
33664     /**
33665      * Gets the 'td' of the cell
33666      * 
33667      * @param {Integer} rowIndex row to select
33668      * @param {Integer} colIndex column to select
33669      * 
33670      * @return {Object} 
33671      */
33672     getCell : function(rowIndex, colIndex){
33673         var locked = this.cm.getLockedCount();
33674         var source;
33675         if(colIndex < locked){
33676             source = this.lockedBody.dom.firstChild;
33677         }else{
33678             source = this.mainBody.dom.firstChild;
33679             colIndex -= locked;
33680         }
33681         return source.rows[rowIndex].childNodes[colIndex];
33682     },
33683
33684     getCellText : function(rowIndex, colIndex){
33685         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33686     },
33687
33688     getCellBox : function(cell){
33689         var b = this.fly(cell).getBox();
33690         if(Roo.isOpera){ // opera fails to report the Y
33691             b.y = cell.offsetTop + this.mainBody.getY();
33692         }
33693         return b;
33694     },
33695
33696     getCellIndex : function(cell){
33697         var id = String(cell.className).match(this.cellRE);
33698         if(id){
33699             return parseInt(id[1], 10);
33700         }
33701         return 0;
33702     },
33703
33704     findHeaderIndex : function(n){
33705         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33706         return r ? this.getCellIndex(r) : false;
33707     },
33708
33709     findHeaderCell : function(n){
33710         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33711         return r ? r : false;
33712     },
33713
33714     findRowIndex : function(n){
33715         if(!n){
33716             return false;
33717         }
33718         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33719         return r ? r.rowIndex : false;
33720     },
33721
33722     findCellIndex : function(node){
33723         var stop = this.el.dom;
33724         while(node && node != stop){
33725             if(this.findRE.test(node.className)){
33726                 return this.getCellIndex(node);
33727             }
33728             node = node.parentNode;
33729         }
33730         return false;
33731     },
33732
33733     getColumnId : function(index){
33734         return this.cm.getColumnId(index);
33735     },
33736
33737     getSplitters : function()
33738     {
33739         if(this.splitterSelector){
33740            return Roo.DomQuery.select(this.splitterSelector);
33741         }else{
33742             return null;
33743       }
33744     },
33745
33746     getSplitter : function(index){
33747         return this.getSplitters()[index];
33748     },
33749
33750     onRowOver : function(e, t){
33751         var row;
33752         if((row = this.findRowIndex(t)) !== false){
33753             this.getRowComposite(row).addClass("x-grid-row-over");
33754         }
33755     },
33756
33757     onRowOut : function(e, t){
33758         var row;
33759         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33760             this.getRowComposite(row).removeClass("x-grid-row-over");
33761         }
33762     },
33763
33764     renderHeaders : function(){
33765         var cm = this.cm;
33766         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33767         var cb = [], lb = [], sb = [], lsb = [], p = {};
33768         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33769             p.cellId = "x-grid-hd-0-" + i;
33770             p.splitId = "x-grid-csplit-0-" + i;
33771             p.id = cm.getColumnId(i);
33772             p.value = cm.getColumnHeader(i) || "";
33773             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33774             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33775             if(!cm.isLocked(i)){
33776                 cb[cb.length] = ct.apply(p);
33777                 sb[sb.length] = st.apply(p);
33778             }else{
33779                 lb[lb.length] = ct.apply(p);
33780                 lsb[lsb.length] = st.apply(p);
33781             }
33782         }
33783         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33784                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33785     },
33786
33787     updateHeaders : function(){
33788         var html = this.renderHeaders();
33789         this.lockedHd.update(html[0]);
33790         this.mainHd.update(html[1]);
33791     },
33792
33793     /**
33794      * Focuses the specified row.
33795      * @param {Number} row The row index
33796      */
33797     focusRow : function(row)
33798     {
33799         //Roo.log('GridView.focusRow');
33800         var x = this.scroller.dom.scrollLeft;
33801         this.focusCell(row, 0, false);
33802         this.scroller.dom.scrollLeft = x;
33803     },
33804
33805     /**
33806      * Focuses the specified cell.
33807      * @param {Number} row The row index
33808      * @param {Number} col The column index
33809      * @param {Boolean} hscroll false to disable horizontal scrolling
33810      */
33811     focusCell : function(row, col, hscroll)
33812     {
33813         //Roo.log('GridView.focusCell');
33814         var el = this.ensureVisible(row, col, hscroll);
33815         this.focusEl.alignTo(el, "tl-tl");
33816         if(Roo.isGecko){
33817             this.focusEl.focus();
33818         }else{
33819             this.focusEl.focus.defer(1, this.focusEl);
33820         }
33821     },
33822
33823     /**
33824      * Scrolls the specified cell into view
33825      * @param {Number} row The row index
33826      * @param {Number} col The column index
33827      * @param {Boolean} hscroll false to disable horizontal scrolling
33828      */
33829     ensureVisible : function(row, col, hscroll)
33830     {
33831         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33832         //return null; //disable for testing.
33833         if(typeof row != "number"){
33834             row = row.rowIndex;
33835         }
33836         if(row < 0 && row >= this.ds.getCount()){
33837             return  null;
33838         }
33839         col = (col !== undefined ? col : 0);
33840         var cm = this.grid.colModel;
33841         while(cm.isHidden(col)){
33842             col++;
33843         }
33844
33845         var el = this.getCell(row, col);
33846         if(!el){
33847             return null;
33848         }
33849         var c = this.scroller.dom;
33850
33851         var ctop = parseInt(el.offsetTop, 10);
33852         var cleft = parseInt(el.offsetLeft, 10);
33853         var cbot = ctop + el.offsetHeight;
33854         var cright = cleft + el.offsetWidth;
33855         
33856         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33857         var stop = parseInt(c.scrollTop, 10);
33858         var sleft = parseInt(c.scrollLeft, 10);
33859         var sbot = stop + ch;
33860         var sright = sleft + c.clientWidth;
33861         /*
33862         Roo.log('GridView.ensureVisible:' +
33863                 ' ctop:' + ctop +
33864                 ' c.clientHeight:' + c.clientHeight +
33865                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33866                 ' stop:' + stop +
33867                 ' cbot:' + cbot +
33868                 ' sbot:' + sbot +
33869                 ' ch:' + ch  
33870                 );
33871         */
33872         if(ctop < stop){
33873             c.scrollTop = ctop;
33874             //Roo.log("set scrolltop to ctop DISABLE?");
33875         }else if(cbot > sbot){
33876             //Roo.log("set scrolltop to cbot-ch");
33877             c.scrollTop = cbot-ch;
33878         }
33879         
33880         if(hscroll !== false){
33881             if(cleft < sleft){
33882                 c.scrollLeft = cleft;
33883             }else if(cright > sright){
33884                 c.scrollLeft = cright-c.clientWidth;
33885             }
33886         }
33887          
33888         return el;
33889     },
33890
33891     updateColumns : function(){
33892         this.grid.stopEditing();
33893         var cm = this.grid.colModel, colIds = this.getColumnIds();
33894         //var totalWidth = cm.getTotalWidth();
33895         var pos = 0;
33896         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33897             //if(cm.isHidden(i)) continue;
33898             var w = cm.getColumnWidth(i);
33899             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33900             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33901         }
33902         this.updateSplitters();
33903     },
33904
33905     generateRules : function(cm){
33906         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33907         Roo.util.CSS.removeStyleSheet(rulesId);
33908         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33909             var cid = cm.getColumnId(i);
33910             var align = '';
33911             if(cm.config[i].align){
33912                 align = 'text-align:'+cm.config[i].align+';';
33913             }
33914             var hidden = '';
33915             if(cm.isHidden(i)){
33916                 hidden = 'display:none;';
33917             }
33918             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33919             ruleBuf.push(
33920                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33921                     this.hdSelector, cid, " {\n", align, width, "}\n",
33922                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33923                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33924         }
33925         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33926     },
33927
33928     updateSplitters : function(){
33929         var cm = this.cm, s = this.getSplitters();
33930         if(s){ // splitters not created yet
33931             var pos = 0, locked = true;
33932             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33933                 if(cm.isHidden(i)) {
33934                     continue;
33935                 }
33936                 var w = cm.getColumnWidth(i); // make sure it's a number
33937                 if(!cm.isLocked(i) && locked){
33938                     pos = 0;
33939                     locked = false;
33940                 }
33941                 pos += w;
33942                 s[i].style.left = (pos-this.splitOffset) + "px";
33943             }
33944         }
33945     },
33946
33947     handleHiddenChange : function(colModel, colIndex, hidden){
33948         if(hidden){
33949             this.hideColumn(colIndex);
33950         }else{
33951             this.unhideColumn(colIndex);
33952         }
33953     },
33954
33955     hideColumn : function(colIndex){
33956         var cid = this.getColumnId(colIndex);
33957         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33958         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33959         if(Roo.isSafari){
33960             this.updateHeaders();
33961         }
33962         this.updateSplitters();
33963         this.layout();
33964     },
33965
33966     unhideColumn : function(colIndex){
33967         var cid = this.getColumnId(colIndex);
33968         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33969         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33970
33971         if(Roo.isSafari){
33972             this.updateHeaders();
33973         }
33974         this.updateSplitters();
33975         this.layout();
33976     },
33977
33978     insertRows : function(dm, firstRow, lastRow, isUpdate){
33979         if(firstRow == 0 && lastRow == dm.getCount()-1){
33980             this.refresh();
33981         }else{
33982             if(!isUpdate){
33983                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33984             }
33985             var s = this.getScrollState();
33986             var markup = this.renderRows(firstRow, lastRow);
33987             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33988             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33989             this.restoreScroll(s);
33990             if(!isUpdate){
33991                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33992                 this.syncRowHeights(firstRow, lastRow);
33993                 this.stripeRows(firstRow);
33994                 this.layout();
33995             }
33996         }
33997     },
33998
33999     bufferRows : function(markup, target, index){
34000         var before = null, trows = target.rows, tbody = target.tBodies[0];
34001         if(index < trows.length){
34002             before = trows[index];
34003         }
34004         var b = document.createElement("div");
34005         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34006         var rows = b.firstChild.rows;
34007         for(var i = 0, len = rows.length; i < len; i++){
34008             if(before){
34009                 tbody.insertBefore(rows[0], before);
34010             }else{
34011                 tbody.appendChild(rows[0]);
34012             }
34013         }
34014         b.innerHTML = "";
34015         b = null;
34016     },
34017
34018     deleteRows : function(dm, firstRow, lastRow){
34019         if(dm.getRowCount()<1){
34020             this.fireEvent("beforerefresh", this);
34021             this.mainBody.update("");
34022             this.lockedBody.update("");
34023             this.fireEvent("refresh", this);
34024         }else{
34025             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34026             var bt = this.getBodyTable();
34027             var tbody = bt.firstChild;
34028             var rows = bt.rows;
34029             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34030                 tbody.removeChild(rows[firstRow]);
34031             }
34032             this.stripeRows(firstRow);
34033             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34034         }
34035     },
34036
34037     updateRows : function(dataSource, firstRow, lastRow){
34038         var s = this.getScrollState();
34039         this.refresh();
34040         this.restoreScroll(s);
34041     },
34042
34043     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34044         if(!noRefresh){
34045            this.refresh();
34046         }
34047         this.updateHeaderSortState();
34048     },
34049
34050     getScrollState : function(){
34051         
34052         var sb = this.scroller.dom;
34053         return {left: sb.scrollLeft, top: sb.scrollTop};
34054     },
34055
34056     stripeRows : function(startRow){
34057         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34058             return;
34059         }
34060         startRow = startRow || 0;
34061         var rows = this.getBodyTable().rows;
34062         var lrows = this.getLockedTable().rows;
34063         var cls = ' x-grid-row-alt ';
34064         for(var i = startRow, len = rows.length; i < len; i++){
34065             var row = rows[i], lrow = lrows[i];
34066             var isAlt = ((i+1) % 2 == 0);
34067             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34068             if(isAlt == hasAlt){
34069                 continue;
34070             }
34071             if(isAlt){
34072                 row.className += " x-grid-row-alt";
34073             }else{
34074                 row.className = row.className.replace("x-grid-row-alt", "");
34075             }
34076             if(lrow){
34077                 lrow.className = row.className;
34078             }
34079         }
34080     },
34081
34082     restoreScroll : function(state){
34083         //Roo.log('GridView.restoreScroll');
34084         var sb = this.scroller.dom;
34085         sb.scrollLeft = state.left;
34086         sb.scrollTop = state.top;
34087         this.syncScroll();
34088     },
34089
34090     syncScroll : function(){
34091         //Roo.log('GridView.syncScroll');
34092         var sb = this.scroller.dom;
34093         var sh = this.mainHd.dom;
34094         var bs = this.mainBody.dom;
34095         var lv = this.lockedBody.dom;
34096         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34097         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34098     },
34099
34100     handleScroll : function(e){
34101         this.syncScroll();
34102         var sb = this.scroller.dom;
34103         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34104         e.stopEvent();
34105     },
34106
34107     handleWheel : function(e){
34108         var d = e.getWheelDelta();
34109         this.scroller.dom.scrollTop -= d*22;
34110         // set this here to prevent jumpy scrolling on large tables
34111         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34112         e.stopEvent();
34113     },
34114
34115     renderRows : function(startRow, endRow){
34116         // pull in all the crap needed to render rows
34117         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34118         var colCount = cm.getColumnCount();
34119
34120         if(ds.getCount() < 1){
34121             return ["", ""];
34122         }
34123
34124         // build a map for all the columns
34125         var cs = [];
34126         for(var i = 0; i < colCount; i++){
34127             var name = cm.getDataIndex(i);
34128             cs[i] = {
34129                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34130                 renderer : cm.getRenderer(i),
34131                 id : cm.getColumnId(i),
34132                 locked : cm.isLocked(i),
34133                 has_editor : cm.isCellEditable(i)
34134             };
34135         }
34136
34137         startRow = startRow || 0;
34138         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34139
34140         // records to render
34141         var rs = ds.getRange(startRow, endRow);
34142
34143         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34144     },
34145
34146     // As much as I hate to duplicate code, this was branched because FireFox really hates
34147     // [].join("") on strings. The performance difference was substantial enough to
34148     // branch this function
34149     doRender : Roo.isGecko ?
34150             function(cs, rs, ds, startRow, colCount, stripe){
34151                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34152                 // buffers
34153                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34154                 
34155                 var hasListener = this.grid.hasListener('rowclass');
34156                 var rowcfg = {};
34157                 for(var j = 0, len = rs.length; j < len; j++){
34158                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34159                     for(var i = 0; i < colCount; i++){
34160                         c = cs[i];
34161                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34162                         p.id = c.id;
34163                         p.css = p.attr = "";
34164                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34165                         if(p.value == undefined || p.value === "") {
34166                             p.value = "&#160;";
34167                         }
34168                         if(c.has_editor){
34169                             p.css += ' x-grid-editable-cell';
34170                         }
34171                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34172                             p.css +=  ' x-grid-dirty-cell';
34173                         }
34174                         var markup = ct.apply(p);
34175                         if(!c.locked){
34176                             cb+= markup;
34177                         }else{
34178                             lcb+= markup;
34179                         }
34180                     }
34181                     var alt = [];
34182                     if(stripe && ((rowIndex+1) % 2 == 0)){
34183                         alt.push("x-grid-row-alt")
34184                     }
34185                     if(r.dirty){
34186                         alt.push(  " x-grid-dirty-row");
34187                     }
34188                     rp.cells = lcb;
34189                     if(this.getRowClass){
34190                         alt.push(this.getRowClass(r, rowIndex));
34191                     }
34192                     if (hasListener) {
34193                         rowcfg = {
34194                              
34195                             record: r,
34196                             rowIndex : rowIndex,
34197                             rowClass : ''
34198                         };
34199                         this.grid.fireEvent('rowclass', this, rowcfg);
34200                         alt.push(rowcfg.rowClass);
34201                     }
34202                     rp.alt = alt.join(" ");
34203                     lbuf+= rt.apply(rp);
34204                     rp.cells = cb;
34205                     buf+=  rt.apply(rp);
34206                 }
34207                 return [lbuf, buf];
34208             } :
34209             function(cs, rs, ds, startRow, colCount, stripe){
34210                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34211                 // buffers
34212                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34213                 var hasListener = this.grid.hasListener('rowclass');
34214  
34215                 var rowcfg = {};
34216                 for(var j = 0, len = rs.length; j < len; j++){
34217                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34218                     for(var i = 0; i < colCount; i++){
34219                         c = cs[i];
34220                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34221                         p.id = c.id;
34222                         p.css = p.attr = "";
34223                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34224                         if(p.value == undefined || p.value === "") {
34225                             p.value = "&#160;";
34226                         }
34227                         //Roo.log(c);
34228                          if(c.has_editor){
34229                             p.css += ' x-grid-editable-cell';
34230                         }
34231                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34232                             p.css += ' x-grid-dirty-cell' 
34233                         }
34234                         
34235                         var markup = ct.apply(p);
34236                         if(!c.locked){
34237                             cb[cb.length] = markup;
34238                         }else{
34239                             lcb[lcb.length] = markup;
34240                         }
34241                     }
34242                     var alt = [];
34243                     if(stripe && ((rowIndex+1) % 2 == 0)){
34244                         alt.push( "x-grid-row-alt");
34245                     }
34246                     if(r.dirty){
34247                         alt.push(" x-grid-dirty-row");
34248                     }
34249                     rp.cells = lcb;
34250                     if(this.getRowClass){
34251                         alt.push( this.getRowClass(r, rowIndex));
34252                     }
34253                     if (hasListener) {
34254                         rowcfg = {
34255                              
34256                             record: r,
34257                             rowIndex : rowIndex,
34258                             rowClass : ''
34259                         };
34260                         this.grid.fireEvent('rowclass', this, rowcfg);
34261                         alt.push(rowcfg.rowClass);
34262                     }
34263                     
34264                     rp.alt = alt.join(" ");
34265                     rp.cells = lcb.join("");
34266                     lbuf[lbuf.length] = rt.apply(rp);
34267                     rp.cells = cb.join("");
34268                     buf[buf.length] =  rt.apply(rp);
34269                 }
34270                 return [lbuf.join(""), buf.join("")];
34271             },
34272
34273     renderBody : function(){
34274         var markup = this.renderRows();
34275         var bt = this.templates.body;
34276         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34277     },
34278
34279     /**
34280      * Refreshes the grid
34281      * @param {Boolean} headersToo
34282      */
34283     refresh : function(headersToo){
34284         this.fireEvent("beforerefresh", this);
34285         this.grid.stopEditing();
34286         var result = this.renderBody();
34287         this.lockedBody.update(result[0]);
34288         this.mainBody.update(result[1]);
34289         if(headersToo === true){
34290             this.updateHeaders();
34291             this.updateColumns();
34292             this.updateSplitters();
34293             this.updateHeaderSortState();
34294         }
34295         this.syncRowHeights();
34296         this.layout();
34297         this.fireEvent("refresh", this);
34298     },
34299
34300     handleColumnMove : function(cm, oldIndex, newIndex){
34301         this.indexMap = null;
34302         var s = this.getScrollState();
34303         this.refresh(true);
34304         this.restoreScroll(s);
34305         this.afterMove(newIndex);
34306     },
34307
34308     afterMove : function(colIndex){
34309         if(this.enableMoveAnim && Roo.enableFx){
34310             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34311         }
34312         // if multisort - fix sortOrder, and reload..
34313         if (this.grid.dataSource.multiSort) {
34314             // the we can call sort again..
34315             var dm = this.grid.dataSource;
34316             var cm = this.grid.colModel;
34317             var so = [];
34318             for(var i = 0; i < cm.config.length; i++ ) {
34319                 
34320                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34321                     continue; // dont' bother, it's not in sort list or being set.
34322                 }
34323                 
34324                 so.push(cm.config[i].dataIndex);
34325             };
34326             dm.sortOrder = so;
34327             dm.load(dm.lastOptions);
34328             
34329             
34330         }
34331         
34332     },
34333
34334     updateCell : function(dm, rowIndex, dataIndex){
34335         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34336         if(typeof colIndex == "undefined"){ // not present in grid
34337             return;
34338         }
34339         var cm = this.grid.colModel;
34340         var cell = this.getCell(rowIndex, colIndex);
34341         var cellText = this.getCellText(rowIndex, colIndex);
34342
34343         var p = {
34344             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34345             id : cm.getColumnId(colIndex),
34346             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34347         };
34348         var renderer = cm.getRenderer(colIndex);
34349         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34350         if(typeof val == "undefined" || val === "") {
34351             val = "&#160;";
34352         }
34353         cellText.innerHTML = val;
34354         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34355         this.syncRowHeights(rowIndex, rowIndex);
34356     },
34357
34358     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34359         var maxWidth = 0;
34360         if(this.grid.autoSizeHeaders){
34361             var h = this.getHeaderCellMeasure(colIndex);
34362             maxWidth = Math.max(maxWidth, h.scrollWidth);
34363         }
34364         var tb, index;
34365         if(this.cm.isLocked(colIndex)){
34366             tb = this.getLockedTable();
34367             index = colIndex;
34368         }else{
34369             tb = this.getBodyTable();
34370             index = colIndex - this.cm.getLockedCount();
34371         }
34372         if(tb && tb.rows){
34373             var rows = tb.rows;
34374             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34375             for(var i = 0; i < stopIndex; i++){
34376                 var cell = rows[i].childNodes[index].firstChild;
34377                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34378             }
34379         }
34380         return maxWidth + /*margin for error in IE*/ 5;
34381     },
34382     /**
34383      * Autofit a column to its content.
34384      * @param {Number} colIndex
34385      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34386      */
34387      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34388          if(this.cm.isHidden(colIndex)){
34389              return; // can't calc a hidden column
34390          }
34391         if(forceMinSize){
34392             var cid = this.cm.getColumnId(colIndex);
34393             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34394            if(this.grid.autoSizeHeaders){
34395                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34396            }
34397         }
34398         var newWidth = this.calcColumnWidth(colIndex);
34399         this.cm.setColumnWidth(colIndex,
34400             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34401         if(!suppressEvent){
34402             this.grid.fireEvent("columnresize", colIndex, newWidth);
34403         }
34404     },
34405
34406     /**
34407      * Autofits all columns to their content and then expands to fit any extra space in the grid
34408      */
34409      autoSizeColumns : function(){
34410         var cm = this.grid.colModel;
34411         var colCount = cm.getColumnCount();
34412         for(var i = 0; i < colCount; i++){
34413             this.autoSizeColumn(i, true, true);
34414         }
34415         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34416             this.fitColumns();
34417         }else{
34418             this.updateColumns();
34419             this.layout();
34420         }
34421     },
34422
34423     /**
34424      * Autofits all columns to the grid's width proportionate with their current size
34425      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34426      */
34427     fitColumns : function(reserveScrollSpace){
34428         var cm = this.grid.colModel;
34429         var colCount = cm.getColumnCount();
34430         var cols = [];
34431         var width = 0;
34432         var i, w;
34433         for (i = 0; i < colCount; i++){
34434             if(!cm.isHidden(i) && !cm.isFixed(i)){
34435                 w = cm.getColumnWidth(i);
34436                 cols.push(i);
34437                 cols.push(w);
34438                 width += w;
34439             }
34440         }
34441         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34442         if(reserveScrollSpace){
34443             avail -= 17;
34444         }
34445         var frac = (avail - cm.getTotalWidth())/width;
34446         while (cols.length){
34447             w = cols.pop();
34448             i = cols.pop();
34449             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34450         }
34451         this.updateColumns();
34452         this.layout();
34453     },
34454
34455     onRowSelect : function(rowIndex){
34456         var row = this.getRowComposite(rowIndex);
34457         row.addClass("x-grid-row-selected");
34458     },
34459
34460     onRowDeselect : function(rowIndex){
34461         var row = this.getRowComposite(rowIndex);
34462         row.removeClass("x-grid-row-selected");
34463     },
34464
34465     onCellSelect : function(row, col){
34466         var cell = this.getCell(row, col);
34467         if(cell){
34468             Roo.fly(cell).addClass("x-grid-cell-selected");
34469         }
34470     },
34471
34472     onCellDeselect : function(row, col){
34473         var cell = this.getCell(row, col);
34474         if(cell){
34475             Roo.fly(cell).removeClass("x-grid-cell-selected");
34476         }
34477     },
34478
34479     updateHeaderSortState : function(){
34480         
34481         // sort state can be single { field: xxx, direction : yyy}
34482         // or   { xxx=>ASC , yyy : DESC ..... }
34483         
34484         var mstate = {};
34485         if (!this.ds.multiSort) { 
34486             var state = this.ds.getSortState();
34487             if(!state){
34488                 return;
34489             }
34490             mstate[state.field] = state.direction;
34491             // FIXME... - this is not used here.. but might be elsewhere..
34492             this.sortState = state;
34493             
34494         } else {
34495             mstate = this.ds.sortToggle;
34496         }
34497         //remove existing sort classes..
34498         
34499         var sc = this.sortClasses;
34500         var hds = this.el.select(this.headerSelector).removeClass(sc);
34501         
34502         for(var f in mstate) {
34503         
34504             var sortColumn = this.cm.findColumnIndex(f);
34505             
34506             if(sortColumn != -1){
34507                 var sortDir = mstate[f];        
34508                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34509             }
34510         }
34511         
34512          
34513         
34514     },
34515
34516
34517     handleHeaderClick : function(g, index,e){
34518         
34519         Roo.log("header click");
34520         
34521         if (Roo.isTouch) {
34522             // touch events on header are handled by context
34523             this.handleHdCtx(g,index,e);
34524             return;
34525         }
34526         
34527         
34528         if(this.headersDisabled){
34529             return;
34530         }
34531         var dm = g.dataSource, cm = g.colModel;
34532         if(!cm.isSortable(index)){
34533             return;
34534         }
34535         g.stopEditing();
34536         
34537         if (dm.multiSort) {
34538             // update the sortOrder
34539             var so = [];
34540             for(var i = 0; i < cm.config.length; i++ ) {
34541                 
34542                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34543                     continue; // dont' bother, it's not in sort list or being set.
34544                 }
34545                 
34546                 so.push(cm.config[i].dataIndex);
34547             };
34548             dm.sortOrder = so;
34549         }
34550         
34551         
34552         dm.sort(cm.getDataIndex(index));
34553     },
34554
34555
34556     destroy : function(){
34557         if(this.colMenu){
34558             this.colMenu.removeAll();
34559             Roo.menu.MenuMgr.unregister(this.colMenu);
34560             this.colMenu.getEl().remove();
34561             delete this.colMenu;
34562         }
34563         if(this.hmenu){
34564             this.hmenu.removeAll();
34565             Roo.menu.MenuMgr.unregister(this.hmenu);
34566             this.hmenu.getEl().remove();
34567             delete this.hmenu;
34568         }
34569         if(this.grid.enableColumnMove){
34570             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34571             if(dds){
34572                 for(var dd in dds){
34573                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34574                         var elid = dds[dd].dragElId;
34575                         dds[dd].unreg();
34576                         Roo.get(elid).remove();
34577                     } else if(dds[dd].config.isTarget){
34578                         dds[dd].proxyTop.remove();
34579                         dds[dd].proxyBottom.remove();
34580                         dds[dd].unreg();
34581                     }
34582                     if(Roo.dd.DDM.locationCache[dd]){
34583                         delete Roo.dd.DDM.locationCache[dd];
34584                     }
34585                 }
34586                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34587             }
34588         }
34589         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34590         this.bind(null, null);
34591         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34592     },
34593
34594     handleLockChange : function(){
34595         this.refresh(true);
34596     },
34597
34598     onDenyColumnLock : function(){
34599
34600     },
34601
34602     onDenyColumnHide : function(){
34603
34604     },
34605
34606     handleHdMenuClick : function(item){
34607         var index = this.hdCtxIndex;
34608         var cm = this.cm, ds = this.ds;
34609         switch(item.id){
34610             case "asc":
34611                 ds.sort(cm.getDataIndex(index), "ASC");
34612                 break;
34613             case "desc":
34614                 ds.sort(cm.getDataIndex(index), "DESC");
34615                 break;
34616             case "lock":
34617                 var lc = cm.getLockedCount();
34618                 if(cm.getColumnCount(true) <= lc+1){
34619                     this.onDenyColumnLock();
34620                     return;
34621                 }
34622                 if(lc != index){
34623                     cm.setLocked(index, true, true);
34624                     cm.moveColumn(index, lc);
34625                     this.grid.fireEvent("columnmove", index, lc);
34626                 }else{
34627                     cm.setLocked(index, true);
34628                 }
34629             break;
34630             case "unlock":
34631                 var lc = cm.getLockedCount();
34632                 if((lc-1) != index){
34633                     cm.setLocked(index, false, true);
34634                     cm.moveColumn(index, lc-1);
34635                     this.grid.fireEvent("columnmove", index, lc-1);
34636                 }else{
34637                     cm.setLocked(index, false);
34638                 }
34639             break;
34640             case 'wider': // used to expand cols on touch..
34641             case 'narrow':
34642                 var cw = cm.getColumnWidth(index);
34643                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34644                 cw = Math.max(0, cw);
34645                 cw = Math.min(cw,4000);
34646                 cm.setColumnWidth(index, cw);
34647                 break;
34648                 
34649             default:
34650                 index = cm.getIndexById(item.id.substr(4));
34651                 if(index != -1){
34652                     if(item.checked && cm.getColumnCount(true) <= 1){
34653                         this.onDenyColumnHide();
34654                         return false;
34655                     }
34656                     cm.setHidden(index, item.checked);
34657                 }
34658         }
34659         return true;
34660     },
34661
34662     beforeColMenuShow : function(){
34663         var cm = this.cm,  colCount = cm.getColumnCount();
34664         this.colMenu.removeAll();
34665         for(var i = 0; i < colCount; i++){
34666             this.colMenu.add(new Roo.menu.CheckItem({
34667                 id: "col-"+cm.getColumnId(i),
34668                 text: cm.getColumnHeader(i),
34669                 checked: !cm.isHidden(i),
34670                 hideOnClick:false
34671             }));
34672         }
34673     },
34674
34675     handleHdCtx : function(g, index, e){
34676         e.stopEvent();
34677         var hd = this.getHeaderCell(index);
34678         this.hdCtxIndex = index;
34679         var ms = this.hmenu.items, cm = this.cm;
34680         ms.get("asc").setDisabled(!cm.isSortable(index));
34681         ms.get("desc").setDisabled(!cm.isSortable(index));
34682         if(this.grid.enableColLock !== false){
34683             ms.get("lock").setDisabled(cm.isLocked(index));
34684             ms.get("unlock").setDisabled(!cm.isLocked(index));
34685         }
34686         this.hmenu.show(hd, "tl-bl");
34687     },
34688
34689     handleHdOver : function(e){
34690         var hd = this.findHeaderCell(e.getTarget());
34691         if(hd && !this.headersDisabled){
34692             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34693                this.fly(hd).addClass("x-grid-hd-over");
34694             }
34695         }
34696     },
34697
34698     handleHdOut : function(e){
34699         var hd = this.findHeaderCell(e.getTarget());
34700         if(hd){
34701             this.fly(hd).removeClass("x-grid-hd-over");
34702         }
34703     },
34704
34705     handleSplitDblClick : function(e, t){
34706         var i = this.getCellIndex(t);
34707         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34708             this.autoSizeColumn(i, true);
34709             this.layout();
34710         }
34711     },
34712
34713     render : function(){
34714
34715         var cm = this.cm;
34716         var colCount = cm.getColumnCount();
34717
34718         if(this.grid.monitorWindowResize === true){
34719             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34720         }
34721         var header = this.renderHeaders();
34722         var body = this.templates.body.apply({rows:""});
34723         var html = this.templates.master.apply({
34724             lockedBody: body,
34725             body: body,
34726             lockedHeader: header[0],
34727             header: header[1]
34728         });
34729
34730         //this.updateColumns();
34731
34732         this.grid.getGridEl().dom.innerHTML = html;
34733
34734         this.initElements();
34735         
34736         // a kludge to fix the random scolling effect in webkit
34737         this.el.on("scroll", function() {
34738             this.el.dom.scrollTop=0; // hopefully not recursive..
34739         },this);
34740
34741         this.scroller.on("scroll", this.handleScroll, this);
34742         this.lockedBody.on("mousewheel", this.handleWheel, this);
34743         this.mainBody.on("mousewheel", this.handleWheel, this);
34744
34745         this.mainHd.on("mouseover", this.handleHdOver, this);
34746         this.mainHd.on("mouseout", this.handleHdOut, this);
34747         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34748                 {delegate: "."+this.splitClass});
34749
34750         this.lockedHd.on("mouseover", this.handleHdOver, this);
34751         this.lockedHd.on("mouseout", this.handleHdOut, this);
34752         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34753                 {delegate: "."+this.splitClass});
34754
34755         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34756             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34757         }
34758
34759         this.updateSplitters();
34760
34761         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34762             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34763             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34764         }
34765
34766         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34767             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34768             this.hmenu.add(
34769                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34770                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34771             );
34772             if(this.grid.enableColLock !== false){
34773                 this.hmenu.add('-',
34774                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34775                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34776                 );
34777             }
34778             if (Roo.isTouch) {
34779                  this.hmenu.add('-',
34780                     {id:"wider", text: this.columnsWiderText},
34781                     {id:"narrow", text: this.columnsNarrowText }
34782                 );
34783                 
34784                  
34785             }
34786             
34787             if(this.grid.enableColumnHide !== false){
34788
34789                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34790                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34791                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34792
34793                 this.hmenu.add('-',
34794                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34795                 );
34796             }
34797             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34798
34799             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34800         }
34801
34802         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34803             this.dd = new Roo.grid.GridDragZone(this.grid, {
34804                 ddGroup : this.grid.ddGroup || 'GridDD'
34805             });
34806             
34807         }
34808
34809         /*
34810         for(var i = 0; i < colCount; i++){
34811             if(cm.isHidden(i)){
34812                 this.hideColumn(i);
34813             }
34814             if(cm.config[i].align){
34815                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34816                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34817             }
34818         }*/
34819         
34820         this.updateHeaderSortState();
34821
34822         this.beforeInitialResize();
34823         this.layout(true);
34824
34825         // two part rendering gives faster view to the user
34826         this.renderPhase2.defer(1, this);
34827     },
34828
34829     renderPhase2 : function(){
34830         // render the rows now
34831         this.refresh();
34832         if(this.grid.autoSizeColumns){
34833             this.autoSizeColumns();
34834         }
34835     },
34836
34837     beforeInitialResize : function(){
34838
34839     },
34840
34841     onColumnSplitterMoved : function(i, w){
34842         this.userResized = true;
34843         var cm = this.grid.colModel;
34844         cm.setColumnWidth(i, w, true);
34845         var cid = cm.getColumnId(i);
34846         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34847         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34848         this.updateSplitters();
34849         this.layout();
34850         this.grid.fireEvent("columnresize", i, w);
34851     },
34852
34853     syncRowHeights : function(startIndex, endIndex){
34854         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34855             startIndex = startIndex || 0;
34856             var mrows = this.getBodyTable().rows;
34857             var lrows = this.getLockedTable().rows;
34858             var len = mrows.length-1;
34859             endIndex = Math.min(endIndex || len, len);
34860             for(var i = startIndex; i <= endIndex; i++){
34861                 var m = mrows[i], l = lrows[i];
34862                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34863                 m.style.height = l.style.height = h + "px";
34864             }
34865         }
34866     },
34867
34868     layout : function(initialRender, is2ndPass)
34869     {
34870         var g = this.grid;
34871         var auto = g.autoHeight;
34872         var scrollOffset = 16;
34873         var c = g.getGridEl(), cm = this.cm,
34874                 expandCol = g.autoExpandColumn,
34875                 gv = this;
34876         //c.beginMeasure();
34877
34878         if(!c.dom.offsetWidth){ // display:none?
34879             if(initialRender){
34880                 this.lockedWrap.show();
34881                 this.mainWrap.show();
34882             }
34883             return;
34884         }
34885
34886         var hasLock = this.cm.isLocked(0);
34887
34888         var tbh = this.headerPanel.getHeight();
34889         var bbh = this.footerPanel.getHeight();
34890
34891         if(auto){
34892             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34893             var newHeight = ch + c.getBorderWidth("tb");
34894             if(g.maxHeight){
34895                 newHeight = Math.min(g.maxHeight, newHeight);
34896             }
34897             c.setHeight(newHeight);
34898         }
34899
34900         if(g.autoWidth){
34901             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34902         }
34903
34904         var s = this.scroller;
34905
34906         var csize = c.getSize(true);
34907
34908         this.el.setSize(csize.width, csize.height);
34909
34910         this.headerPanel.setWidth(csize.width);
34911         this.footerPanel.setWidth(csize.width);
34912
34913         var hdHeight = this.mainHd.getHeight();
34914         var vw = csize.width;
34915         var vh = csize.height - (tbh + bbh);
34916
34917         s.setSize(vw, vh);
34918
34919         var bt = this.getBodyTable();
34920         
34921         if(cm.getLockedCount() == cm.config.length){
34922             bt = this.getLockedTable();
34923         }
34924         
34925         var ltWidth = hasLock ?
34926                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34927
34928         var scrollHeight = bt.offsetHeight;
34929         var scrollWidth = ltWidth + bt.offsetWidth;
34930         var vscroll = false, hscroll = false;
34931
34932         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34933
34934         var lw = this.lockedWrap, mw = this.mainWrap;
34935         var lb = this.lockedBody, mb = this.mainBody;
34936
34937         setTimeout(function(){
34938             var t = s.dom.offsetTop;
34939             var w = s.dom.clientWidth,
34940                 h = s.dom.clientHeight;
34941
34942             lw.setTop(t);
34943             lw.setSize(ltWidth, h);
34944
34945             mw.setLeftTop(ltWidth, t);
34946             mw.setSize(w-ltWidth, h);
34947
34948             lb.setHeight(h-hdHeight);
34949             mb.setHeight(h-hdHeight);
34950
34951             if(is2ndPass !== true && !gv.userResized && expandCol){
34952                 // high speed resize without full column calculation
34953                 
34954                 var ci = cm.getIndexById(expandCol);
34955                 if (ci < 0) {
34956                     ci = cm.findColumnIndex(expandCol);
34957                 }
34958                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34959                 var expandId = cm.getColumnId(ci);
34960                 var  tw = cm.getTotalWidth(false);
34961                 var currentWidth = cm.getColumnWidth(ci);
34962                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34963                 if(currentWidth != cw){
34964                     cm.setColumnWidth(ci, cw, true);
34965                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34966                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34967                     gv.updateSplitters();
34968                     gv.layout(false, true);
34969                 }
34970             }
34971
34972             if(initialRender){
34973                 lw.show();
34974                 mw.show();
34975             }
34976             //c.endMeasure();
34977         }, 10);
34978     },
34979
34980     onWindowResize : function(){
34981         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34982             return;
34983         }
34984         this.layout();
34985     },
34986
34987     appendFooter : function(parentEl){
34988         return null;
34989     },
34990
34991     sortAscText : "Sort Ascending",
34992     sortDescText : "Sort Descending",
34993     lockText : "Lock Column",
34994     unlockText : "Unlock Column",
34995     columnsText : "Columns",
34996  
34997     columnsWiderText : "Wider",
34998     columnsNarrowText : "Thinner"
34999 });
35000
35001
35002 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35003     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35004     this.proxy.el.addClass('x-grid3-col-dd');
35005 };
35006
35007 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35008     handleMouseDown : function(e){
35009
35010     },
35011
35012     callHandleMouseDown : function(e){
35013         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35014     }
35015 });
35016 /*
35017  * Based on:
35018  * Ext JS Library 1.1.1
35019  * Copyright(c) 2006-2007, Ext JS, LLC.
35020  *
35021  * Originally Released Under LGPL - original licence link has changed is not relivant.
35022  *
35023  * Fork - LGPL
35024  * <script type="text/javascript">
35025  */
35026  /**
35027  * @extends Roo.dd.DDProxy
35028  * @class Roo.grid.SplitDragZone
35029  * Support for Column Header resizing
35030  * @constructor
35031  * @param {Object} config
35032  */
35033 // private
35034 // This is a support class used internally by the Grid components
35035 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35036     this.grid = grid;
35037     this.view = grid.getView();
35038     this.proxy = this.view.resizeProxy;
35039     Roo.grid.SplitDragZone.superclass.constructor.call(
35040         this,
35041         hd, // ID
35042         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35043         {  // CONFIG
35044             dragElId : Roo.id(this.proxy.dom),
35045             resizeFrame:false
35046         }
35047     );
35048     
35049     this.setHandleElId(Roo.id(hd));
35050     if (hd2 !== false) {
35051         this.setOuterHandleElId(Roo.id(hd2));
35052     }
35053     
35054     this.scroll = false;
35055 };
35056 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35057     fly: Roo.Element.fly,
35058
35059     b4StartDrag : function(x, y){
35060         this.view.headersDisabled = true;
35061         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35062                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35063         );
35064         this.proxy.setHeight(h);
35065         
35066         // for old system colWidth really stored the actual width?
35067         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35068         // which in reality did not work.. - it worked only for fixed sizes
35069         // for resizable we need to use actual sizes.
35070         var w = this.cm.getColumnWidth(this.cellIndex);
35071         if (!this.view.mainWrap) {
35072             // bootstrap.
35073             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35074         }
35075         
35076         
35077         
35078         // this was w-this.grid.minColumnWidth;
35079         // doesnt really make sense? - w = thie curren width or the rendered one?
35080         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35081         this.resetConstraints();
35082         this.setXConstraint(minw, 1000);
35083         this.setYConstraint(0, 0);
35084         this.minX = x - minw;
35085         this.maxX = x + 1000;
35086         this.startPos = x;
35087         if (!this.view.mainWrap) { // this is Bootstrap code..
35088             this.getDragEl().style.display='block';
35089         }
35090         
35091         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35092     },
35093
35094
35095     handleMouseDown : function(e){
35096         ev = Roo.EventObject.setEvent(e);
35097         var t = this.fly(ev.getTarget());
35098         if(t.hasClass("x-grid-split")){
35099             this.cellIndex = this.view.getCellIndex(t.dom);
35100             this.split = t.dom;
35101             this.cm = this.grid.colModel;
35102             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35103                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35104             }
35105         }
35106     },
35107
35108     endDrag : function(e){
35109         this.view.headersDisabled = false;
35110         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35111         var diff = endX - this.startPos;
35112         // 
35113         var w = this.cm.getColumnWidth(this.cellIndex);
35114         if (!this.view.mainWrap) {
35115             w = 0;
35116         }
35117         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35118     },
35119
35120     autoOffset : function(){
35121         this.setDelta(0,0);
35122     }
35123 });/*
35124  * Based on:
35125  * Ext JS Library 1.1.1
35126  * Copyright(c) 2006-2007, Ext JS, LLC.
35127  *
35128  * Originally Released Under LGPL - original licence link has changed is not relivant.
35129  *
35130  * Fork - LGPL
35131  * <script type="text/javascript">
35132  */
35133  
35134 // private
35135 // This is a support class used internally by the Grid components
35136 Roo.grid.GridDragZone = function(grid, config){
35137     this.view = grid.getView();
35138     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35139     if(this.view.lockedBody){
35140         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35141         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35142     }
35143     this.scroll = false;
35144     this.grid = grid;
35145     this.ddel = document.createElement('div');
35146     this.ddel.className = 'x-grid-dd-wrap';
35147 };
35148
35149 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35150     ddGroup : "GridDD",
35151
35152     getDragData : function(e){
35153         var t = Roo.lib.Event.getTarget(e);
35154         var rowIndex = this.view.findRowIndex(t);
35155         var sm = this.grid.selModel;
35156             
35157         //Roo.log(rowIndex);
35158         
35159         if (sm.getSelectedCell) {
35160             // cell selection..
35161             if (!sm.getSelectedCell()) {
35162                 return false;
35163             }
35164             if (rowIndex != sm.getSelectedCell()[0]) {
35165                 return false;
35166             }
35167         
35168         }
35169         if (sm.getSelections && sm.getSelections().length < 1) {
35170             return false;
35171         }
35172         
35173         
35174         // before it used to all dragging of unseleted... - now we dont do that.
35175         if(rowIndex !== false){
35176             
35177             // if editorgrid.. 
35178             
35179             
35180             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35181                
35182             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35183               //  
35184             //}
35185             if (e.hasModifier()){
35186                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35187             }
35188             
35189             Roo.log("getDragData");
35190             
35191             return {
35192                 grid: this.grid,
35193                 ddel: this.ddel,
35194                 rowIndex: rowIndex,
35195                 selections: sm.getSelections ? sm.getSelections() : (
35196                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35197             };
35198         }
35199         return false;
35200     },
35201     
35202     
35203     onInitDrag : function(e){
35204         var data = this.dragData;
35205         this.ddel.innerHTML = this.grid.getDragDropText();
35206         this.proxy.update(this.ddel);
35207         // fire start drag?
35208     },
35209
35210     afterRepair : function(){
35211         this.dragging = false;
35212     },
35213
35214     getRepairXY : function(e, data){
35215         return false;
35216     },
35217
35218     onEndDrag : function(data, e){
35219         // fire end drag?
35220     },
35221
35222     onValidDrop : function(dd, e, id){
35223         // fire drag drop?
35224         this.hideProxy();
35225     },
35226
35227     beforeInvalidDrop : function(e, id){
35228
35229     }
35230 });/*
35231  * Based on:
35232  * Ext JS Library 1.1.1
35233  * Copyright(c) 2006-2007, Ext JS, LLC.
35234  *
35235  * Originally Released Under LGPL - original licence link has changed is not relivant.
35236  *
35237  * Fork - LGPL
35238  * <script type="text/javascript">
35239  */
35240  
35241
35242 /**
35243  * @class Roo.grid.ColumnModel
35244  * @extends Roo.util.Observable
35245  * This is the default implementation of a ColumnModel used by the Grid. It defines
35246  * the columns in the grid.
35247  * <br>Usage:<br>
35248  <pre><code>
35249  var colModel = new Roo.grid.ColumnModel([
35250         {header: "Ticker", width: 60, sortable: true, locked: true},
35251         {header: "Company Name", width: 150, sortable: true},
35252         {header: "Market Cap.", width: 100, sortable: true},
35253         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35254         {header: "Employees", width: 100, sortable: true, resizable: false}
35255  ]);
35256  </code></pre>
35257  * <p>
35258  
35259  * The config options listed for this class are options which may appear in each
35260  * individual column definition.
35261  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35262  * @constructor
35263  * @param {Object} config An Array of column config objects. See this class's
35264  * config objects for details.
35265 */
35266 Roo.grid.ColumnModel = function(config){
35267         /**
35268      * The config passed into the constructor
35269      */
35270     this.config = []; //config;
35271     this.lookup = {};
35272
35273     // if no id, create one
35274     // if the column does not have a dataIndex mapping,
35275     // map it to the order it is in the config
35276     for(var i = 0, len = config.length; i < len; i++){
35277         this.addColumn(config[i]);
35278         
35279     }
35280
35281     /**
35282      * The width of columns which have no width specified (defaults to 100)
35283      * @type Number
35284      */
35285     this.defaultWidth = 100;
35286
35287     /**
35288      * Default sortable of columns which have no sortable specified (defaults to false)
35289      * @type Boolean
35290      */
35291     this.defaultSortable = false;
35292
35293     this.addEvents({
35294         /**
35295              * @event widthchange
35296              * Fires when the width of a column changes.
35297              * @param {ColumnModel} this
35298              * @param {Number} columnIndex The column index
35299              * @param {Number} newWidth The new width
35300              */
35301             "widthchange": true,
35302         /**
35303              * @event headerchange
35304              * Fires when the text of a header changes.
35305              * @param {ColumnModel} this
35306              * @param {Number} columnIndex The column index
35307              * @param {Number} newText The new header text
35308              */
35309             "headerchange": true,
35310         /**
35311              * @event hiddenchange
35312              * Fires when a column is hidden or "unhidden".
35313              * @param {ColumnModel} this
35314              * @param {Number} columnIndex The column index
35315              * @param {Boolean} hidden true if hidden, false otherwise
35316              */
35317             "hiddenchange": true,
35318             /**
35319          * @event columnmoved
35320          * Fires when a column is moved.
35321          * @param {ColumnModel} this
35322          * @param {Number} oldIndex
35323          * @param {Number} newIndex
35324          */
35325         "columnmoved" : true,
35326         /**
35327          * @event columlockchange
35328          * Fires when a column's locked state is changed
35329          * @param {ColumnModel} this
35330          * @param {Number} colIndex
35331          * @param {Boolean} locked true if locked
35332          */
35333         "columnlockchange" : true
35334     });
35335     Roo.grid.ColumnModel.superclass.constructor.call(this);
35336 };
35337 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35338     /**
35339      * @cfg {String} header The header text to display in the Grid view.
35340      */
35341         /**
35342      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35343      */
35344         /**
35345      * @cfg {String} smHeader Header at Bootsrap Small width
35346      */
35347         /**
35348      * @cfg {String} mdHeader Header at Bootsrap Medium width
35349      */
35350         /**
35351      * @cfg {String} lgHeader Header at Bootsrap Large width
35352      */
35353         /**
35354      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35355      */
35356     /**
35357      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35358      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35359      * specified, the column's index is used as an index into the Record's data Array.
35360      */
35361     /**
35362      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35363      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35364      */
35365     /**
35366      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35367      * Defaults to the value of the {@link #defaultSortable} property.
35368      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35369      */
35370     /**
35371      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35372      */
35373     /**
35374      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35375      */
35376     /**
35377      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35378      */
35379     /**
35380      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35381      */
35382     /**
35383      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35384      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35385      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35386      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35387      */
35388        /**
35389      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35390      */
35391     /**
35392      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35393      */
35394     /**
35395      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35396      */
35397     /**
35398      * @cfg {String} cursor (Optional)
35399      */
35400     /**
35401      * @cfg {String} tooltip (Optional)
35402      */
35403     /**
35404      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35405      */
35406     /**
35407      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35408      */
35409     /**
35410      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35411      */
35412     /**
35413      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35414      */
35415         /**
35416      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35417      */
35418     /**
35419      * Returns the id of the column at the specified index.
35420      * @param {Number} index The column index
35421      * @return {String} the id
35422      */
35423     getColumnId : function(index){
35424         return this.config[index].id;
35425     },
35426
35427     /**
35428      * Returns the column for a specified id.
35429      * @param {String} id The column id
35430      * @return {Object} the column
35431      */
35432     getColumnById : function(id){
35433         return this.lookup[id];
35434     },
35435
35436     
35437     /**
35438      * Returns the column Object for a specified dataIndex.
35439      * @param {String} dataIndex The column dataIndex
35440      * @return {Object|Boolean} the column or false if not found
35441      */
35442     getColumnByDataIndex: function(dataIndex){
35443         var index = this.findColumnIndex(dataIndex);
35444         return index > -1 ? this.config[index] : false;
35445     },
35446     
35447     /**
35448      * Returns the index for a specified column id.
35449      * @param {String} id The column id
35450      * @return {Number} the index, or -1 if not found
35451      */
35452     getIndexById : function(id){
35453         for(var i = 0, len = this.config.length; i < len; i++){
35454             if(this.config[i].id == id){
35455                 return i;
35456             }
35457         }
35458         return -1;
35459     },
35460     
35461     /**
35462      * Returns the index for a specified column dataIndex.
35463      * @param {String} dataIndex The column dataIndex
35464      * @return {Number} the index, or -1 if not found
35465      */
35466     
35467     findColumnIndex : function(dataIndex){
35468         for(var i = 0, len = this.config.length; i < len; i++){
35469             if(this.config[i].dataIndex == dataIndex){
35470                 return i;
35471             }
35472         }
35473         return -1;
35474     },
35475     
35476     
35477     moveColumn : function(oldIndex, newIndex){
35478         var c = this.config[oldIndex];
35479         this.config.splice(oldIndex, 1);
35480         this.config.splice(newIndex, 0, c);
35481         this.dataMap = null;
35482         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35483     },
35484
35485     isLocked : function(colIndex){
35486         return this.config[colIndex].locked === true;
35487     },
35488
35489     setLocked : function(colIndex, value, suppressEvent){
35490         if(this.isLocked(colIndex) == value){
35491             return;
35492         }
35493         this.config[colIndex].locked = value;
35494         if(!suppressEvent){
35495             this.fireEvent("columnlockchange", this, colIndex, value);
35496         }
35497     },
35498
35499     getTotalLockedWidth : function(){
35500         var totalWidth = 0;
35501         for(var i = 0; i < this.config.length; i++){
35502             if(this.isLocked(i) && !this.isHidden(i)){
35503                 this.totalWidth += this.getColumnWidth(i);
35504             }
35505         }
35506         return totalWidth;
35507     },
35508
35509     getLockedCount : function(){
35510         for(var i = 0, len = this.config.length; i < len; i++){
35511             if(!this.isLocked(i)){
35512                 return i;
35513             }
35514         }
35515         
35516         return this.config.length;
35517     },
35518
35519     /**
35520      * Returns the number of columns.
35521      * @return {Number}
35522      */
35523     getColumnCount : function(visibleOnly){
35524         if(visibleOnly === true){
35525             var c = 0;
35526             for(var i = 0, len = this.config.length; i < len; i++){
35527                 if(!this.isHidden(i)){
35528                     c++;
35529                 }
35530             }
35531             return c;
35532         }
35533         return this.config.length;
35534     },
35535
35536     /**
35537      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35538      * @param {Function} fn
35539      * @param {Object} scope (optional)
35540      * @return {Array} result
35541      */
35542     getColumnsBy : function(fn, scope){
35543         var r = [];
35544         for(var i = 0, len = this.config.length; i < len; i++){
35545             var c = this.config[i];
35546             if(fn.call(scope||this, c, i) === true){
35547                 r[r.length] = c;
35548             }
35549         }
35550         return r;
35551     },
35552
35553     /**
35554      * Returns true if the specified column is sortable.
35555      * @param {Number} col The column index
35556      * @return {Boolean}
35557      */
35558     isSortable : function(col){
35559         if(typeof this.config[col].sortable == "undefined"){
35560             return this.defaultSortable;
35561         }
35562         return this.config[col].sortable;
35563     },
35564
35565     /**
35566      * Returns the rendering (formatting) function defined for the column.
35567      * @param {Number} col The column index.
35568      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35569      */
35570     getRenderer : function(col){
35571         if(!this.config[col].renderer){
35572             return Roo.grid.ColumnModel.defaultRenderer;
35573         }
35574         return this.config[col].renderer;
35575     },
35576
35577     /**
35578      * Sets the rendering (formatting) function for a column.
35579      * @param {Number} col The column index
35580      * @param {Function} fn The function to use to process the cell's raw data
35581      * to return HTML markup for the grid view. The render function is called with
35582      * the following parameters:<ul>
35583      * <li>Data value.</li>
35584      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35585      * <li>css A CSS style string to apply to the table cell.</li>
35586      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35587      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35588      * <li>Row index</li>
35589      * <li>Column index</li>
35590      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35591      */
35592     setRenderer : function(col, fn){
35593         this.config[col].renderer = fn;
35594     },
35595
35596     /**
35597      * Returns the width for the specified column.
35598      * @param {Number} col The column index
35599      * @param (optional) {String} gridSize bootstrap width size.
35600      * @return {Number}
35601      */
35602     getColumnWidth : function(col, gridSize)
35603         {
35604                 var cfg = this.config[col];
35605                 
35606                 if (typeof(gridSize) == 'undefined') {
35607                         return cfg.width * 1 || this.defaultWidth;
35608                 }
35609                 if (gridSize === false) { // if we set it..
35610                         return cfg.width || false;
35611                 }
35612                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35613                 
35614                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35615                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35616                                 continue;
35617                         }
35618                         return cfg[ sizes[i] ];
35619                 }
35620                 return 1;
35621                 
35622     },
35623
35624     /**
35625      * Sets the width for a column.
35626      * @param {Number} col The column index
35627      * @param {Number} width The new width
35628      */
35629     setColumnWidth : function(col, width, suppressEvent){
35630         this.config[col].width = width;
35631         this.totalWidth = null;
35632         if(!suppressEvent){
35633              this.fireEvent("widthchange", this, col, width);
35634         }
35635     },
35636
35637     /**
35638      * Returns the total width of all columns.
35639      * @param {Boolean} includeHidden True to include hidden column widths
35640      * @return {Number}
35641      */
35642     getTotalWidth : function(includeHidden){
35643         if(!this.totalWidth){
35644             this.totalWidth = 0;
35645             for(var i = 0, len = this.config.length; i < len; i++){
35646                 if(includeHidden || !this.isHidden(i)){
35647                     this.totalWidth += this.getColumnWidth(i);
35648                 }
35649             }
35650         }
35651         return this.totalWidth;
35652     },
35653
35654     /**
35655      * Returns the header for the specified column.
35656      * @param {Number} col The column index
35657      * @return {String}
35658      */
35659     getColumnHeader : function(col){
35660         return this.config[col].header;
35661     },
35662
35663     /**
35664      * Sets the header for a column.
35665      * @param {Number} col The column index
35666      * @param {String} header The new header
35667      */
35668     setColumnHeader : function(col, header){
35669         this.config[col].header = header;
35670         this.fireEvent("headerchange", this, col, header);
35671     },
35672
35673     /**
35674      * Returns the tooltip for the specified column.
35675      * @param {Number} col The column index
35676      * @return {String}
35677      */
35678     getColumnTooltip : function(col){
35679             return this.config[col].tooltip;
35680     },
35681     /**
35682      * Sets the tooltip for a column.
35683      * @param {Number} col The column index
35684      * @param {String} tooltip The new tooltip
35685      */
35686     setColumnTooltip : function(col, tooltip){
35687             this.config[col].tooltip = tooltip;
35688     },
35689
35690     /**
35691      * Returns the dataIndex for the specified column.
35692      * @param {Number} col The column index
35693      * @return {Number}
35694      */
35695     getDataIndex : function(col){
35696         return this.config[col].dataIndex;
35697     },
35698
35699     /**
35700      * Sets the dataIndex for a column.
35701      * @param {Number} col The column index
35702      * @param {Number} dataIndex The new dataIndex
35703      */
35704     setDataIndex : function(col, dataIndex){
35705         this.config[col].dataIndex = dataIndex;
35706     },
35707
35708     
35709     
35710     /**
35711      * Returns true if the cell is editable.
35712      * @param {Number} colIndex The column index
35713      * @param {Number} rowIndex The row index - this is nto actually used..?
35714      * @return {Boolean}
35715      */
35716     isCellEditable : function(colIndex, rowIndex){
35717         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35718     },
35719
35720     /**
35721      * Returns the editor defined for the cell/column.
35722      * return false or null to disable editing.
35723      * @param {Number} colIndex The column index
35724      * @param {Number} rowIndex The row index
35725      * @return {Object}
35726      */
35727     getCellEditor : function(colIndex, rowIndex){
35728         return this.config[colIndex].editor;
35729     },
35730
35731     /**
35732      * Sets if a column is editable.
35733      * @param {Number} col The column index
35734      * @param {Boolean} editable True if the column is editable
35735      */
35736     setEditable : function(col, editable){
35737         this.config[col].editable = editable;
35738     },
35739
35740
35741     /**
35742      * Returns true if the column is hidden.
35743      * @param {Number} colIndex The column index
35744      * @return {Boolean}
35745      */
35746     isHidden : function(colIndex){
35747         return this.config[colIndex].hidden;
35748     },
35749
35750
35751     /**
35752      * Returns true if the column width cannot be changed
35753      */
35754     isFixed : function(colIndex){
35755         return this.config[colIndex].fixed;
35756     },
35757
35758     /**
35759      * Returns true if the column can be resized
35760      * @return {Boolean}
35761      */
35762     isResizable : function(colIndex){
35763         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35764     },
35765     /**
35766      * Sets if a column is hidden.
35767      * @param {Number} colIndex The column index
35768      * @param {Boolean} hidden True if the column is hidden
35769      */
35770     setHidden : function(colIndex, hidden){
35771         this.config[colIndex].hidden = hidden;
35772         this.totalWidth = null;
35773         this.fireEvent("hiddenchange", this, colIndex, hidden);
35774     },
35775
35776     /**
35777      * Sets the editor for a column.
35778      * @param {Number} col The column index
35779      * @param {Object} editor The editor object
35780      */
35781     setEditor : function(col, editor){
35782         this.config[col].editor = editor;
35783     },
35784     /**
35785      * Add a column (experimental...) - defaults to adding to the end..
35786      * @param {Object} config 
35787     */
35788     addColumn : function(c)
35789     {
35790     
35791         var i = this.config.length;
35792         this.config[i] = c;
35793         
35794         if(typeof c.dataIndex == "undefined"){
35795             c.dataIndex = i;
35796         }
35797         if(typeof c.renderer == "string"){
35798             c.renderer = Roo.util.Format[c.renderer];
35799         }
35800         if(typeof c.id == "undefined"){
35801             c.id = Roo.id();
35802         }
35803         if(c.editor && c.editor.xtype){
35804             c.editor  = Roo.factory(c.editor, Roo.grid);
35805         }
35806         if(c.editor && c.editor.isFormField){
35807             c.editor = new Roo.grid.GridEditor(c.editor);
35808         }
35809         this.lookup[c.id] = c;
35810     }
35811     
35812 });
35813
35814 Roo.grid.ColumnModel.defaultRenderer = function(value)
35815 {
35816     if(typeof value == "object") {
35817         return value;
35818     }
35819         if(typeof value == "string" && value.length < 1){
35820             return "&#160;";
35821         }
35822     
35823         return String.format("{0}", value);
35824 };
35825
35826 // Alias for backwards compatibility
35827 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35828 /*
35829  * Based on:
35830  * Ext JS Library 1.1.1
35831  * Copyright(c) 2006-2007, Ext JS, LLC.
35832  *
35833  * Originally Released Under LGPL - original licence link has changed is not relivant.
35834  *
35835  * Fork - LGPL
35836  * <script type="text/javascript">
35837  */
35838
35839 /**
35840  * @class Roo.grid.AbstractSelectionModel
35841  * @extends Roo.util.Observable
35842  * @abstract
35843  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35844  * implemented by descendant classes.  This class should not be directly instantiated.
35845  * @constructor
35846  */
35847 Roo.grid.AbstractSelectionModel = function(){
35848     this.locked = false;
35849     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35850 };
35851
35852 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35853     /** @ignore Called by the grid automatically. Do not call directly. */
35854     init : function(grid){
35855         this.grid = grid;
35856         this.initEvents();
35857     },
35858
35859     /**
35860      * Locks the selections.
35861      */
35862     lock : function(){
35863         this.locked = true;
35864     },
35865
35866     /**
35867      * Unlocks the selections.
35868      */
35869     unlock : function(){
35870         this.locked = false;
35871     },
35872
35873     /**
35874      * Returns true if the selections are locked.
35875      * @return {Boolean}
35876      */
35877     isLocked : function(){
35878         return this.locked;
35879     }
35880 });/*
35881  * Based on:
35882  * Ext JS Library 1.1.1
35883  * Copyright(c) 2006-2007, Ext JS, LLC.
35884  *
35885  * Originally Released Under LGPL - original licence link has changed is not relivant.
35886  *
35887  * Fork - LGPL
35888  * <script type="text/javascript">
35889  */
35890 /**
35891  * @extends Roo.grid.AbstractSelectionModel
35892  * @class Roo.grid.RowSelectionModel
35893  * The default SelectionModel used by {@link Roo.grid.Grid}.
35894  * It supports multiple selections and keyboard selection/navigation. 
35895  * @constructor
35896  * @param {Object} config
35897  */
35898 Roo.grid.RowSelectionModel = function(config){
35899     Roo.apply(this, config);
35900     this.selections = new Roo.util.MixedCollection(false, function(o){
35901         return o.id;
35902     });
35903
35904     this.last = false;
35905     this.lastActive = false;
35906
35907     this.addEvents({
35908         /**
35909         * @event selectionchange
35910         * Fires when the selection changes
35911         * @param {SelectionModel} this
35912         */
35913        "selectionchange" : true,
35914        /**
35915         * @event afterselectionchange
35916         * Fires after the selection changes (eg. by key press or clicking)
35917         * @param {SelectionModel} this
35918         */
35919        "afterselectionchange" : true,
35920        /**
35921         * @event beforerowselect
35922         * Fires when a row is selected being selected, return false to cancel.
35923         * @param {SelectionModel} this
35924         * @param {Number} rowIndex The selected index
35925         * @param {Boolean} keepExisting False if other selections will be cleared
35926         */
35927        "beforerowselect" : true,
35928        /**
35929         * @event rowselect
35930         * Fires when a row is selected.
35931         * @param {SelectionModel} this
35932         * @param {Number} rowIndex The selected index
35933         * @param {Roo.data.Record} r The record
35934         */
35935        "rowselect" : true,
35936        /**
35937         * @event rowdeselect
35938         * Fires when a row is deselected.
35939         * @param {SelectionModel} this
35940         * @param {Number} rowIndex The selected index
35941         */
35942         "rowdeselect" : true
35943     });
35944     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35945     this.locked = false;
35946 };
35947
35948 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35949     /**
35950      * @cfg {Boolean} singleSelect
35951      * True to allow selection of only one row at a time (defaults to false)
35952      */
35953     singleSelect : false,
35954
35955     // private
35956     initEvents : function(){
35957
35958         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35959             this.grid.on("mousedown", this.handleMouseDown, this);
35960         }else{ // allow click to work like normal
35961             this.grid.on("rowclick", this.handleDragableRowClick, this);
35962         }
35963         // bootstrap does not have a view..
35964         var view = this.grid.view ? this.grid.view : this.grid;
35965         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35966             "up" : function(e){
35967                 if(!e.shiftKey){
35968                     this.selectPrevious(e.shiftKey);
35969                 }else if(this.last !== false && this.lastActive !== false){
35970                     var last = this.last;
35971                     this.selectRange(this.last,  this.lastActive-1);
35972                     view.focusRow(this.lastActive);
35973                     if(last !== false){
35974                         this.last = last;
35975                     }
35976                 }else{
35977                     this.selectFirstRow();
35978                 }
35979                 this.fireEvent("afterselectionchange", this);
35980             },
35981             "down" : function(e){
35982                 if(!e.shiftKey){
35983                     this.selectNext(e.shiftKey);
35984                 }else if(this.last !== false && this.lastActive !== false){
35985                     var last = this.last;
35986                     this.selectRange(this.last,  this.lastActive+1);
35987                     view.focusRow(this.lastActive);
35988                     if(last !== false){
35989                         this.last = last;
35990                     }
35991                 }else{
35992                     this.selectFirstRow();
35993                 }
35994                 this.fireEvent("afterselectionchange", this);
35995             },
35996             scope: this
35997         });
35998
35999          
36000         view.on("refresh", this.onRefresh, this);
36001         view.on("rowupdated", this.onRowUpdated, this);
36002         view.on("rowremoved", this.onRemove, this);
36003     },
36004
36005     // private
36006     onRefresh : function(){
36007         var ds = this.grid.ds, i, v = this.grid.view;
36008         var s = this.selections;
36009         s.each(function(r){
36010             if((i = ds.indexOfId(r.id)) != -1){
36011                 v.onRowSelect(i);
36012                 s.add(ds.getAt(i)); // updating the selection relate data
36013             }else{
36014                 s.remove(r);
36015             }
36016         });
36017     },
36018
36019     // private
36020     onRemove : function(v, index, r){
36021         this.selections.remove(r);
36022     },
36023
36024     // private
36025     onRowUpdated : function(v, index, r){
36026         if(this.isSelected(r)){
36027             v.onRowSelect(index);
36028         }
36029     },
36030
36031     /**
36032      * Select records.
36033      * @param {Array} records The records to select
36034      * @param {Boolean} keepExisting (optional) True to keep existing selections
36035      */
36036     selectRecords : function(records, keepExisting){
36037         if(!keepExisting){
36038             this.clearSelections();
36039         }
36040         var ds = this.grid.ds;
36041         for(var i = 0, len = records.length; i < len; i++){
36042             this.selectRow(ds.indexOf(records[i]), true);
36043         }
36044     },
36045
36046     /**
36047      * Gets the number of selected rows.
36048      * @return {Number}
36049      */
36050     getCount : function(){
36051         return this.selections.length;
36052     },
36053
36054     /**
36055      * Selects the first row in the grid.
36056      */
36057     selectFirstRow : function(){
36058         this.selectRow(0);
36059     },
36060
36061     /**
36062      * Select the last row.
36063      * @param {Boolean} keepExisting (optional) True to keep existing selections
36064      */
36065     selectLastRow : function(keepExisting){
36066         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36067     },
36068
36069     /**
36070      * Selects the row immediately following the last selected row.
36071      * @param {Boolean} keepExisting (optional) True to keep existing selections
36072      */
36073     selectNext : function(keepExisting){
36074         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36075             this.selectRow(this.last+1, keepExisting);
36076             var view = this.grid.view ? this.grid.view : this.grid;
36077             view.focusRow(this.last);
36078         }
36079     },
36080
36081     /**
36082      * Selects the row that precedes the last selected row.
36083      * @param {Boolean} keepExisting (optional) True to keep existing selections
36084      */
36085     selectPrevious : function(keepExisting){
36086         if(this.last){
36087             this.selectRow(this.last-1, keepExisting);
36088             var view = this.grid.view ? this.grid.view : this.grid;
36089             view.focusRow(this.last);
36090         }
36091     },
36092
36093     /**
36094      * Returns the selected records
36095      * @return {Array} Array of selected records
36096      */
36097     getSelections : function(){
36098         return [].concat(this.selections.items);
36099     },
36100
36101     /**
36102      * Returns the first selected record.
36103      * @return {Record}
36104      */
36105     getSelected : function(){
36106         return this.selections.itemAt(0);
36107     },
36108
36109
36110     /**
36111      * Clears all selections.
36112      */
36113     clearSelections : function(fast){
36114         if(this.locked) {
36115             return;
36116         }
36117         if(fast !== true){
36118             var ds = this.grid.ds;
36119             var s = this.selections;
36120             s.each(function(r){
36121                 this.deselectRow(ds.indexOfId(r.id));
36122             }, this);
36123             s.clear();
36124         }else{
36125             this.selections.clear();
36126         }
36127         this.last = false;
36128     },
36129
36130
36131     /**
36132      * Selects all rows.
36133      */
36134     selectAll : function(){
36135         if(this.locked) {
36136             return;
36137         }
36138         this.selections.clear();
36139         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36140             this.selectRow(i, true);
36141         }
36142     },
36143
36144     /**
36145      * Returns True if there is a selection.
36146      * @return {Boolean}
36147      */
36148     hasSelection : function(){
36149         return this.selections.length > 0;
36150     },
36151
36152     /**
36153      * Returns True if the specified row is selected.
36154      * @param {Number/Record} record The record or index of the record to check
36155      * @return {Boolean}
36156      */
36157     isSelected : function(index){
36158         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36159         return (r && this.selections.key(r.id) ? true : false);
36160     },
36161
36162     /**
36163      * Returns True if the specified record id is selected.
36164      * @param {String} id The id of record to check
36165      * @return {Boolean}
36166      */
36167     isIdSelected : function(id){
36168         return (this.selections.key(id) ? true : false);
36169     },
36170
36171     // private
36172     handleMouseDown : function(e, t)
36173     {
36174         var view = this.grid.view ? this.grid.view : this.grid;
36175         var rowIndex;
36176         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36177             return;
36178         };
36179         if(e.shiftKey && this.last !== false){
36180             var last = this.last;
36181             this.selectRange(last, rowIndex, e.ctrlKey);
36182             this.last = last; // reset the last
36183             view.focusRow(rowIndex);
36184         }else{
36185             var isSelected = this.isSelected(rowIndex);
36186             if(e.button !== 0 && isSelected){
36187                 view.focusRow(rowIndex);
36188             }else if(e.ctrlKey && isSelected){
36189                 this.deselectRow(rowIndex);
36190             }else if(!isSelected){
36191                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36192                 view.focusRow(rowIndex);
36193             }
36194         }
36195         this.fireEvent("afterselectionchange", this);
36196     },
36197     // private
36198     handleDragableRowClick :  function(grid, rowIndex, e) 
36199     {
36200         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36201             this.selectRow(rowIndex, false);
36202             var view = this.grid.view ? this.grid.view : this.grid;
36203             view.focusRow(rowIndex);
36204              this.fireEvent("afterselectionchange", this);
36205         }
36206     },
36207     
36208     /**
36209      * Selects multiple rows.
36210      * @param {Array} rows Array of the indexes of the row to select
36211      * @param {Boolean} keepExisting (optional) True to keep existing selections
36212      */
36213     selectRows : function(rows, keepExisting){
36214         if(!keepExisting){
36215             this.clearSelections();
36216         }
36217         for(var i = 0, len = rows.length; i < len; i++){
36218             this.selectRow(rows[i], true);
36219         }
36220     },
36221
36222     /**
36223      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36224      * @param {Number} startRow The index of the first row in the range
36225      * @param {Number} endRow The index of the last row in the range
36226      * @param {Boolean} keepExisting (optional) True to retain existing selections
36227      */
36228     selectRange : function(startRow, endRow, keepExisting){
36229         if(this.locked) {
36230             return;
36231         }
36232         if(!keepExisting){
36233             this.clearSelections();
36234         }
36235         if(startRow <= endRow){
36236             for(var i = startRow; i <= endRow; i++){
36237                 this.selectRow(i, true);
36238             }
36239         }else{
36240             for(var i = startRow; i >= endRow; i--){
36241                 this.selectRow(i, true);
36242             }
36243         }
36244     },
36245
36246     /**
36247      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36248      * @param {Number} startRow The index of the first row in the range
36249      * @param {Number} endRow The index of the last row in the range
36250      */
36251     deselectRange : function(startRow, endRow, preventViewNotify){
36252         if(this.locked) {
36253             return;
36254         }
36255         for(var i = startRow; i <= endRow; i++){
36256             this.deselectRow(i, preventViewNotify);
36257         }
36258     },
36259
36260     /**
36261      * Selects a row.
36262      * @param {Number} row The index of the row to select
36263      * @param {Boolean} keepExisting (optional) True to keep existing selections
36264      */
36265     selectRow : function(index, keepExisting, preventViewNotify){
36266         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36267             return;
36268         }
36269         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36270             if(!keepExisting || this.singleSelect){
36271                 this.clearSelections();
36272             }
36273             var r = this.grid.ds.getAt(index);
36274             this.selections.add(r);
36275             this.last = this.lastActive = index;
36276             if(!preventViewNotify){
36277                 var view = this.grid.view ? this.grid.view : this.grid;
36278                 view.onRowSelect(index);
36279             }
36280             this.fireEvent("rowselect", this, index, r);
36281             this.fireEvent("selectionchange", this);
36282         }
36283     },
36284
36285     /**
36286      * Deselects a row.
36287      * @param {Number} row The index of the row to deselect
36288      */
36289     deselectRow : function(index, preventViewNotify){
36290         if(this.locked) {
36291             return;
36292         }
36293         if(this.last == index){
36294             this.last = false;
36295         }
36296         if(this.lastActive == index){
36297             this.lastActive = false;
36298         }
36299         var r = this.grid.ds.getAt(index);
36300         this.selections.remove(r);
36301         if(!preventViewNotify){
36302             var view = this.grid.view ? this.grid.view : this.grid;
36303             view.onRowDeselect(index);
36304         }
36305         this.fireEvent("rowdeselect", this, index);
36306         this.fireEvent("selectionchange", this);
36307     },
36308
36309     // private
36310     restoreLast : function(){
36311         if(this._last){
36312             this.last = this._last;
36313         }
36314     },
36315
36316     // private
36317     acceptsNav : function(row, col, cm){
36318         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36319     },
36320
36321     // private
36322     onEditorKey : function(field, e){
36323         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36324         if(k == e.TAB){
36325             e.stopEvent();
36326             ed.completeEdit();
36327             if(e.shiftKey){
36328                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36329             }else{
36330                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36331             }
36332         }else if(k == e.ENTER && !e.ctrlKey){
36333             e.stopEvent();
36334             ed.completeEdit();
36335             if(e.shiftKey){
36336                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36337             }else{
36338                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36339             }
36340         }else if(k == e.ESC){
36341             ed.cancelEdit();
36342         }
36343         if(newCell){
36344             g.startEditing(newCell[0], newCell[1]);
36345         }
36346     }
36347 });/*
36348  * Based on:
36349  * Ext JS Library 1.1.1
36350  * Copyright(c) 2006-2007, Ext JS, LLC.
36351  *
36352  * Originally Released Under LGPL - original licence link has changed is not relivant.
36353  *
36354  * Fork - LGPL
36355  * <script type="text/javascript">
36356  */
36357 /**
36358  * @class Roo.grid.CellSelectionModel
36359  * @extends Roo.grid.AbstractSelectionModel
36360  * This class provides the basic implementation for cell selection in a grid.
36361  * @constructor
36362  * @param {Object} config The object containing the configuration of this model.
36363  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36364  */
36365 Roo.grid.CellSelectionModel = function(config){
36366     Roo.apply(this, config);
36367
36368     this.selection = null;
36369
36370     this.addEvents({
36371         /**
36372              * @event beforerowselect
36373              * Fires before a cell is selected.
36374              * @param {SelectionModel} this
36375              * @param {Number} rowIndex The selected row index
36376              * @param {Number} colIndex The selected cell index
36377              */
36378             "beforecellselect" : true,
36379         /**
36380              * @event cellselect
36381              * Fires when a cell is selected.
36382              * @param {SelectionModel} this
36383              * @param {Number} rowIndex The selected row index
36384              * @param {Number} colIndex The selected cell index
36385              */
36386             "cellselect" : true,
36387         /**
36388              * @event selectionchange
36389              * Fires when the active selection changes.
36390              * @param {SelectionModel} this
36391              * @param {Object} selection null for no selection or an object (o) with two properties
36392                 <ul>
36393                 <li>o.record: the record object for the row the selection is in</li>
36394                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36395                 </ul>
36396              */
36397             "selectionchange" : true,
36398         /**
36399              * @event tabend
36400              * Fires when the tab (or enter) was pressed on the last editable cell
36401              * You can use this to trigger add new row.
36402              * @param {SelectionModel} this
36403              */
36404             "tabend" : true,
36405          /**
36406              * @event beforeeditnext
36407              * Fires before the next editable sell is made active
36408              * You can use this to skip to another cell or fire the tabend
36409              *    if you set cell to false
36410              * @param {Object} eventdata object : { cell : [ row, col ] } 
36411              */
36412             "beforeeditnext" : true
36413     });
36414     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36415 };
36416
36417 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36418     
36419     enter_is_tab: false,
36420
36421     /** @ignore */
36422     initEvents : function(){
36423         this.grid.on("mousedown", this.handleMouseDown, this);
36424         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36425         var view = this.grid.view;
36426         view.on("refresh", this.onViewChange, this);
36427         view.on("rowupdated", this.onRowUpdated, this);
36428         view.on("beforerowremoved", this.clearSelections, this);
36429         view.on("beforerowsinserted", this.clearSelections, this);
36430         if(this.grid.isEditor){
36431             this.grid.on("beforeedit", this.beforeEdit,  this);
36432         }
36433     },
36434
36435         //private
36436     beforeEdit : function(e){
36437         this.select(e.row, e.column, false, true, e.record);
36438     },
36439
36440         //private
36441     onRowUpdated : function(v, index, r){
36442         if(this.selection && this.selection.record == r){
36443             v.onCellSelect(index, this.selection.cell[1]);
36444         }
36445     },
36446
36447         //private
36448     onViewChange : function(){
36449         this.clearSelections(true);
36450     },
36451
36452         /**
36453          * Returns the currently selected cell,.
36454          * @return {Array} The selected cell (row, column) or null if none selected.
36455          */
36456     getSelectedCell : function(){
36457         return this.selection ? this.selection.cell : null;
36458     },
36459
36460     /**
36461      * Clears all selections.
36462      * @param {Boolean} true to prevent the gridview from being notified about the change.
36463      */
36464     clearSelections : function(preventNotify){
36465         var s = this.selection;
36466         if(s){
36467             if(preventNotify !== true){
36468                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36469             }
36470             this.selection = null;
36471             this.fireEvent("selectionchange", this, null);
36472         }
36473     },
36474
36475     /**
36476      * Returns true if there is a selection.
36477      * @return {Boolean}
36478      */
36479     hasSelection : function(){
36480         return this.selection ? true : false;
36481     },
36482
36483     /** @ignore */
36484     handleMouseDown : function(e, t){
36485         var v = this.grid.getView();
36486         if(this.isLocked()){
36487             return;
36488         };
36489         var row = v.findRowIndex(t);
36490         var cell = v.findCellIndex(t);
36491         if(row !== false && cell !== false){
36492             this.select(row, cell);
36493         }
36494     },
36495
36496     /**
36497      * Selects a cell.
36498      * @param {Number} rowIndex
36499      * @param {Number} collIndex
36500      */
36501     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36502         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36503             this.clearSelections();
36504             r = r || this.grid.dataSource.getAt(rowIndex);
36505             this.selection = {
36506                 record : r,
36507                 cell : [rowIndex, colIndex]
36508             };
36509             if(!preventViewNotify){
36510                 var v = this.grid.getView();
36511                 v.onCellSelect(rowIndex, colIndex);
36512                 if(preventFocus !== true){
36513                     v.focusCell(rowIndex, colIndex);
36514                 }
36515             }
36516             this.fireEvent("cellselect", this, rowIndex, colIndex);
36517             this.fireEvent("selectionchange", this, this.selection);
36518         }
36519     },
36520
36521         //private
36522     isSelectable : function(rowIndex, colIndex, cm){
36523         return !cm.isHidden(colIndex);
36524     },
36525
36526     /** @ignore */
36527     handleKeyDown : function(e){
36528         //Roo.log('Cell Sel Model handleKeyDown');
36529         if(!e.isNavKeyPress()){
36530             return;
36531         }
36532         var g = this.grid, s = this.selection;
36533         if(!s){
36534             e.stopEvent();
36535             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36536             if(cell){
36537                 this.select(cell[0], cell[1]);
36538             }
36539             return;
36540         }
36541         var sm = this;
36542         var walk = function(row, col, step){
36543             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36544         };
36545         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36546         var newCell;
36547
36548       
36549
36550         switch(k){
36551             case e.TAB:
36552                 // handled by onEditorKey
36553                 if (g.isEditor && g.editing) {
36554                     return;
36555                 }
36556                 if(e.shiftKey) {
36557                     newCell = walk(r, c-1, -1);
36558                 } else {
36559                     newCell = walk(r, c+1, 1);
36560                 }
36561                 break;
36562             
36563             case e.DOWN:
36564                newCell = walk(r+1, c, 1);
36565                 break;
36566             
36567             case e.UP:
36568                 newCell = walk(r-1, c, -1);
36569                 break;
36570             
36571             case e.RIGHT:
36572                 newCell = walk(r, c+1, 1);
36573                 break;
36574             
36575             case e.LEFT:
36576                 newCell = walk(r, c-1, -1);
36577                 break;
36578             
36579             case e.ENTER:
36580                 
36581                 if(g.isEditor && !g.editing){
36582                    g.startEditing(r, c);
36583                    e.stopEvent();
36584                    return;
36585                 }
36586                 
36587                 
36588              break;
36589         };
36590         if(newCell){
36591             this.select(newCell[0], newCell[1]);
36592             e.stopEvent();
36593             
36594         }
36595     },
36596
36597     acceptsNav : function(row, col, cm){
36598         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36599     },
36600     /**
36601      * Selects a cell.
36602      * @param {Number} field (not used) - as it's normally used as a listener
36603      * @param {Number} e - event - fake it by using
36604      *
36605      * var e = Roo.EventObjectImpl.prototype;
36606      * e.keyCode = e.TAB
36607      *
36608      * 
36609      */
36610     onEditorKey : function(field, e){
36611         
36612         var k = e.getKey(),
36613             newCell,
36614             g = this.grid,
36615             ed = g.activeEditor,
36616             forward = false;
36617         ///Roo.log('onEditorKey' + k);
36618         
36619         
36620         if (this.enter_is_tab && k == e.ENTER) {
36621             k = e.TAB;
36622         }
36623         
36624         if(k == e.TAB){
36625             if(e.shiftKey){
36626                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36627             }else{
36628                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36629                 forward = true;
36630             }
36631             
36632             e.stopEvent();
36633             
36634         } else if(k == e.ENTER &&  !e.ctrlKey){
36635             ed.completeEdit();
36636             e.stopEvent();
36637             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36638         
36639                 } else if(k == e.ESC){
36640             ed.cancelEdit();
36641         }
36642                 
36643         if (newCell) {
36644             var ecall = { cell : newCell, forward : forward };
36645             this.fireEvent('beforeeditnext', ecall );
36646             newCell = ecall.cell;
36647                         forward = ecall.forward;
36648         }
36649                 
36650         if(newCell){
36651             //Roo.log('next cell after edit');
36652             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36653         } else if (forward) {
36654             // tabbed past last
36655             this.fireEvent.defer(100, this, ['tabend',this]);
36656         }
36657     }
36658 });/*
36659  * Based on:
36660  * Ext JS Library 1.1.1
36661  * Copyright(c) 2006-2007, Ext JS, LLC.
36662  *
36663  * Originally Released Under LGPL - original licence link has changed is not relivant.
36664  *
36665  * Fork - LGPL
36666  * <script type="text/javascript">
36667  */
36668  
36669 /**
36670  * @class Roo.grid.EditorGrid
36671  * @extends Roo.grid.Grid
36672  * Class for creating and editable grid.
36673  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36674  * The container MUST have some type of size defined for the grid to fill. The container will be 
36675  * automatically set to position relative if it isn't already.
36676  * @param {Object} dataSource The data model to bind to
36677  * @param {Object} colModel The column model with info about this grid's columns
36678  */
36679 Roo.grid.EditorGrid = function(container, config){
36680     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36681     this.getGridEl().addClass("xedit-grid");
36682
36683     if(!this.selModel){
36684         this.selModel = new Roo.grid.CellSelectionModel();
36685     }
36686
36687     this.activeEditor = null;
36688
36689         this.addEvents({
36690             /**
36691              * @event beforeedit
36692              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36693              * <ul style="padding:5px;padding-left:16px;">
36694              * <li>grid - This grid</li>
36695              * <li>record - The record being edited</li>
36696              * <li>field - The field name being edited</li>
36697              * <li>value - The value for the field being edited.</li>
36698              * <li>row - The grid row index</li>
36699              * <li>column - The grid column index</li>
36700              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36701              * </ul>
36702              * @param {Object} e An edit event (see above for description)
36703              */
36704             "beforeedit" : true,
36705             /**
36706              * @event afteredit
36707              * Fires after a cell is edited. <br />
36708              * <ul style="padding:5px;padding-left:16px;">
36709              * <li>grid - This grid</li>
36710              * <li>record - The record being edited</li>
36711              * <li>field - The field name being edited</li>
36712              * <li>value - The value being set</li>
36713              * <li>originalValue - The original value for the field, before the edit.</li>
36714              * <li>row - The grid row index</li>
36715              * <li>column - The grid column index</li>
36716              * </ul>
36717              * @param {Object} e An edit event (see above for description)
36718              */
36719             "afteredit" : true,
36720             /**
36721              * @event validateedit
36722              * Fires after a cell is edited, but before the value is set in the record. 
36723          * You can use this to modify the value being set in the field, Return false
36724              * to cancel the change. The edit event object has the following properties <br />
36725              * <ul style="padding:5px;padding-left:16px;">
36726          * <li>editor - This editor</li>
36727              * <li>grid - This grid</li>
36728              * <li>record - The record being edited</li>
36729              * <li>field - The field name being edited</li>
36730              * <li>value - The value being set</li>
36731              * <li>originalValue - The original value for the field, before the edit.</li>
36732              * <li>row - The grid row index</li>
36733              * <li>column - The grid column index</li>
36734              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36735              * </ul>
36736              * @param {Object} e An edit event (see above for description)
36737              */
36738             "validateedit" : true
36739         });
36740     this.on("bodyscroll", this.stopEditing,  this);
36741     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36742 };
36743
36744 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36745     /**
36746      * @cfg {Number} clicksToEdit
36747      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36748      */
36749     clicksToEdit: 2,
36750
36751     // private
36752     isEditor : true,
36753     // private
36754     trackMouseOver: false, // causes very odd FF errors
36755
36756     onCellDblClick : function(g, row, col){
36757         this.startEditing(row, col);
36758     },
36759
36760     onEditComplete : function(ed, value, startValue){
36761         this.editing = false;
36762         this.activeEditor = null;
36763         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36764         var r = ed.record;
36765         var field = this.colModel.getDataIndex(ed.col);
36766         var e = {
36767             grid: this,
36768             record: r,
36769             field: field,
36770             originalValue: startValue,
36771             value: value,
36772             row: ed.row,
36773             column: ed.col,
36774             cancel:false,
36775             editor: ed
36776         };
36777         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36778         cell.show();
36779           
36780         if(String(value) !== String(startValue)){
36781             
36782             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36783                 r.set(field, e.value);
36784                 // if we are dealing with a combo box..
36785                 // then we also set the 'name' colum to be the displayField
36786                 if (ed.field.displayField && ed.field.name) {
36787                     r.set(ed.field.name, ed.field.el.dom.value);
36788                 }
36789                 
36790                 delete e.cancel; //?? why!!!
36791                 this.fireEvent("afteredit", e);
36792             }
36793         } else {
36794             this.fireEvent("afteredit", e); // always fire it!
36795         }
36796         this.view.focusCell(ed.row, ed.col);
36797     },
36798
36799     /**
36800      * Starts editing the specified for the specified row/column
36801      * @param {Number} rowIndex
36802      * @param {Number} colIndex
36803      */
36804     startEditing : function(row, col){
36805         this.stopEditing();
36806         if(this.colModel.isCellEditable(col, row)){
36807             this.view.ensureVisible(row, col, true);
36808           
36809             var r = this.dataSource.getAt(row);
36810             var field = this.colModel.getDataIndex(col);
36811             var cell = Roo.get(this.view.getCell(row,col));
36812             var e = {
36813                 grid: this,
36814                 record: r,
36815                 field: field,
36816                 value: r.data[field],
36817                 row: row,
36818                 column: col,
36819                 cancel:false 
36820             };
36821             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36822                 this.editing = true;
36823                 var ed = this.colModel.getCellEditor(col, row);
36824                 
36825                 if (!ed) {
36826                     return;
36827                 }
36828                 if(!ed.rendered){
36829                     ed.render(ed.parentEl || document.body);
36830                 }
36831                 ed.field.reset();
36832                
36833                 cell.hide();
36834                 
36835                 (function(){ // complex but required for focus issues in safari, ie and opera
36836                     ed.row = row;
36837                     ed.col = col;
36838                     ed.record = r;
36839                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36840                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36841                     this.activeEditor = ed;
36842                     var v = r.data[field];
36843                     ed.startEdit(this.view.getCell(row, col), v);
36844                     // combo's with 'displayField and name set
36845                     if (ed.field.displayField && ed.field.name) {
36846                         ed.field.el.dom.value = r.data[ed.field.name];
36847                     }
36848                     
36849                     
36850                 }).defer(50, this);
36851             }
36852         }
36853     },
36854         
36855     /**
36856      * Stops any active editing
36857      */
36858     stopEditing : function(){
36859         if(this.activeEditor){
36860             this.activeEditor.completeEdit();
36861         }
36862         this.activeEditor = null;
36863     },
36864         
36865          /**
36866      * Called to get grid's drag proxy text, by default returns this.ddText.
36867      * @return {String}
36868      */
36869     getDragDropText : function(){
36870         var count = this.selModel.getSelectedCell() ? 1 : 0;
36871         return String.format(this.ddText, count, count == 1 ? '' : 's');
36872     }
36873         
36874 });/*
36875  * Based on:
36876  * Ext JS Library 1.1.1
36877  * Copyright(c) 2006-2007, Ext JS, LLC.
36878  *
36879  * Originally Released Under LGPL - original licence link has changed is not relivant.
36880  *
36881  * Fork - LGPL
36882  * <script type="text/javascript">
36883  */
36884
36885 // private - not really -- you end up using it !
36886 // This is a support class used internally by the Grid components
36887
36888 /**
36889  * @class Roo.grid.GridEditor
36890  * @extends Roo.Editor
36891  * Class for creating and editable grid elements.
36892  * @param {Object} config any settings (must include field)
36893  */
36894 Roo.grid.GridEditor = function(field, config){
36895     if (!config && field.field) {
36896         config = field;
36897         field = Roo.factory(config.field, Roo.form);
36898     }
36899     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36900     field.monitorTab = false;
36901 };
36902
36903 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36904     
36905     /**
36906      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36907      */
36908     
36909     alignment: "tl-tl",
36910     autoSize: "width",
36911     hideEl : false,
36912     cls: "x-small-editor x-grid-editor",
36913     shim:false,
36914     shadow:"frame"
36915 });/*
36916  * Based on:
36917  * Ext JS Library 1.1.1
36918  * Copyright(c) 2006-2007, Ext JS, LLC.
36919  *
36920  * Originally Released Under LGPL - original licence link has changed is not relivant.
36921  *
36922  * Fork - LGPL
36923  * <script type="text/javascript">
36924  */
36925   
36926
36927   
36928 Roo.grid.PropertyRecord = Roo.data.Record.create([
36929     {name:'name',type:'string'},  'value'
36930 ]);
36931
36932
36933 Roo.grid.PropertyStore = function(grid, source){
36934     this.grid = grid;
36935     this.store = new Roo.data.Store({
36936         recordType : Roo.grid.PropertyRecord
36937     });
36938     this.store.on('update', this.onUpdate,  this);
36939     if(source){
36940         this.setSource(source);
36941     }
36942     Roo.grid.PropertyStore.superclass.constructor.call(this);
36943 };
36944
36945
36946
36947 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36948     setSource : function(o){
36949         this.source = o;
36950         this.store.removeAll();
36951         var data = [];
36952         for(var k in o){
36953             if(this.isEditableValue(o[k])){
36954                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36955             }
36956         }
36957         this.store.loadRecords({records: data}, {}, true);
36958     },
36959
36960     onUpdate : function(ds, record, type){
36961         if(type == Roo.data.Record.EDIT){
36962             var v = record.data['value'];
36963             var oldValue = record.modified['value'];
36964             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36965                 this.source[record.id] = v;
36966                 record.commit();
36967                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36968             }else{
36969                 record.reject();
36970             }
36971         }
36972     },
36973
36974     getProperty : function(row){
36975        return this.store.getAt(row);
36976     },
36977
36978     isEditableValue: function(val){
36979         if(val && val instanceof Date){
36980             return true;
36981         }else if(typeof val == 'object' || typeof val == 'function'){
36982             return false;
36983         }
36984         return true;
36985     },
36986
36987     setValue : function(prop, value){
36988         this.source[prop] = value;
36989         this.store.getById(prop).set('value', value);
36990     },
36991
36992     getSource : function(){
36993         return this.source;
36994     }
36995 });
36996
36997 Roo.grid.PropertyColumnModel = function(grid, store){
36998     this.grid = grid;
36999     var g = Roo.grid;
37000     g.PropertyColumnModel.superclass.constructor.call(this, [
37001         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37002         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37003     ]);
37004     this.store = store;
37005     this.bselect = Roo.DomHelper.append(document.body, {
37006         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37007             {tag: 'option', value: 'true', html: 'true'},
37008             {tag: 'option', value: 'false', html: 'false'}
37009         ]
37010     });
37011     Roo.id(this.bselect);
37012     var f = Roo.form;
37013     this.editors = {
37014         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37015         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37016         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37017         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37018         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37019     };
37020     this.renderCellDelegate = this.renderCell.createDelegate(this);
37021     this.renderPropDelegate = this.renderProp.createDelegate(this);
37022 };
37023
37024 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37025     
37026     
37027     nameText : 'Name',
37028     valueText : 'Value',
37029     
37030     dateFormat : 'm/j/Y',
37031     
37032     
37033     renderDate : function(dateVal){
37034         return dateVal.dateFormat(this.dateFormat);
37035     },
37036
37037     renderBool : function(bVal){
37038         return bVal ? 'true' : 'false';
37039     },
37040
37041     isCellEditable : function(colIndex, rowIndex){
37042         return colIndex == 1;
37043     },
37044
37045     getRenderer : function(col){
37046         return col == 1 ?
37047             this.renderCellDelegate : this.renderPropDelegate;
37048     },
37049
37050     renderProp : function(v){
37051         return this.getPropertyName(v);
37052     },
37053
37054     renderCell : function(val){
37055         var rv = val;
37056         if(val instanceof Date){
37057             rv = this.renderDate(val);
37058         }else if(typeof val == 'boolean'){
37059             rv = this.renderBool(val);
37060         }
37061         return Roo.util.Format.htmlEncode(rv);
37062     },
37063
37064     getPropertyName : function(name){
37065         var pn = this.grid.propertyNames;
37066         return pn && pn[name] ? pn[name] : name;
37067     },
37068
37069     getCellEditor : function(colIndex, rowIndex){
37070         var p = this.store.getProperty(rowIndex);
37071         var n = p.data['name'], val = p.data['value'];
37072         
37073         if(typeof(this.grid.customEditors[n]) == 'string'){
37074             return this.editors[this.grid.customEditors[n]];
37075         }
37076         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37077             return this.grid.customEditors[n];
37078         }
37079         if(val instanceof Date){
37080             return this.editors['date'];
37081         }else if(typeof val == 'number'){
37082             return this.editors['number'];
37083         }else if(typeof val == 'boolean'){
37084             return this.editors['boolean'];
37085         }else{
37086             return this.editors['string'];
37087         }
37088     }
37089 });
37090
37091 /**
37092  * @class Roo.grid.PropertyGrid
37093  * @extends Roo.grid.EditorGrid
37094  * This class represents the  interface of a component based property grid control.
37095  * <br><br>Usage:<pre><code>
37096  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37097       
37098  });
37099  // set any options
37100  grid.render();
37101  * </code></pre>
37102   
37103  * @constructor
37104  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37105  * The container MUST have some type of size defined for the grid to fill. The container will be
37106  * automatically set to position relative if it isn't already.
37107  * @param {Object} config A config object that sets properties on this grid.
37108  */
37109 Roo.grid.PropertyGrid = function(container, config){
37110     config = config || {};
37111     var store = new Roo.grid.PropertyStore(this);
37112     this.store = store;
37113     var cm = new Roo.grid.PropertyColumnModel(this, store);
37114     store.store.sort('name', 'ASC');
37115     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37116         ds: store.store,
37117         cm: cm,
37118         enableColLock:false,
37119         enableColumnMove:false,
37120         stripeRows:false,
37121         trackMouseOver: false,
37122         clicksToEdit:1
37123     }, config));
37124     this.getGridEl().addClass('x-props-grid');
37125     this.lastEditRow = null;
37126     this.on('columnresize', this.onColumnResize, this);
37127     this.addEvents({
37128          /**
37129              * @event beforepropertychange
37130              * Fires before a property changes (return false to stop?)
37131              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37132              * @param {String} id Record Id
37133              * @param {String} newval New Value
37134          * @param {String} oldval Old Value
37135              */
37136         "beforepropertychange": true,
37137         /**
37138              * @event propertychange
37139              * Fires after a property changes
37140              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37141              * @param {String} id Record Id
37142              * @param {String} newval New Value
37143          * @param {String} oldval Old Value
37144              */
37145         "propertychange": true
37146     });
37147     this.customEditors = this.customEditors || {};
37148 };
37149 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37150     
37151      /**
37152      * @cfg {Object} customEditors map of colnames=> custom editors.
37153      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37154      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37155      * false disables editing of the field.
37156          */
37157     
37158       /**
37159      * @cfg {Object} propertyNames map of property Names to their displayed value
37160          */
37161     
37162     render : function(){
37163         Roo.grid.PropertyGrid.superclass.render.call(this);
37164         this.autoSize.defer(100, this);
37165     },
37166
37167     autoSize : function(){
37168         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37169         if(this.view){
37170             this.view.fitColumns();
37171         }
37172     },
37173
37174     onColumnResize : function(){
37175         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37176         this.autoSize();
37177     },
37178     /**
37179      * Sets the data for the Grid
37180      * accepts a Key => Value object of all the elements avaiable.
37181      * @param {Object} data  to appear in grid.
37182      */
37183     setSource : function(source){
37184         this.store.setSource(source);
37185         //this.autoSize();
37186     },
37187     /**
37188      * Gets all the data from the grid.
37189      * @return {Object} data  data stored in grid
37190      */
37191     getSource : function(){
37192         return this.store.getSource();
37193     }
37194 });/*
37195   
37196  * Licence LGPL
37197  
37198  */
37199  
37200 /**
37201  * @class Roo.grid.Calendar
37202  * @extends Roo.util.Grid
37203  * This class extends the Grid to provide a calendar widget
37204  * <br><br>Usage:<pre><code>
37205  var grid = new Roo.grid.Calendar("my-container-id", {
37206      ds: myDataStore,
37207      cm: myColModel,
37208      selModel: mySelectionModel,
37209      autoSizeColumns: true,
37210      monitorWindowResize: false,
37211      trackMouseOver: true
37212      eventstore : real data store..
37213  });
37214  // set any options
37215  grid.render();
37216   
37217   * @constructor
37218  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37219  * The container MUST have some type of size defined for the grid to fill. The container will be
37220  * automatically set to position relative if it isn't already.
37221  * @param {Object} config A config object that sets properties on this grid.
37222  */
37223 Roo.grid.Calendar = function(container, config){
37224         // initialize the container
37225         this.container = Roo.get(container);
37226         this.container.update("");
37227         this.container.setStyle("overflow", "hidden");
37228     this.container.addClass('x-grid-container');
37229
37230     this.id = this.container.id;
37231
37232     Roo.apply(this, config);
37233     // check and correct shorthanded configs
37234     
37235     var rows = [];
37236     var d =1;
37237     for (var r = 0;r < 6;r++) {
37238         
37239         rows[r]=[];
37240         for (var c =0;c < 7;c++) {
37241             rows[r][c]= '';
37242         }
37243     }
37244     if (this.eventStore) {
37245         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37246         this.eventStore.on('load',this.onLoad, this);
37247         this.eventStore.on('beforeload',this.clearEvents, this);
37248          
37249     }
37250     
37251     this.dataSource = new Roo.data.Store({
37252             proxy: new Roo.data.MemoryProxy(rows),
37253             reader: new Roo.data.ArrayReader({}, [
37254                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37255     });
37256
37257     this.dataSource.load();
37258     this.ds = this.dataSource;
37259     this.ds.xmodule = this.xmodule || false;
37260     
37261     
37262     var cellRender = function(v,x,r)
37263     {
37264         return String.format(
37265             '<div class="fc-day  fc-widget-content"><div>' +
37266                 '<div class="fc-event-container"></div>' +
37267                 '<div class="fc-day-number">{0}</div>'+
37268                 
37269                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37270             '</div></div>', v);
37271     
37272     }
37273     
37274     
37275     this.colModel = new Roo.grid.ColumnModel( [
37276         {
37277             xtype: 'ColumnModel',
37278             xns: Roo.grid,
37279             dataIndex : 'weekday0',
37280             header : 'Sunday',
37281             renderer : cellRender
37282         },
37283         {
37284             xtype: 'ColumnModel',
37285             xns: Roo.grid,
37286             dataIndex : 'weekday1',
37287             header : 'Monday',
37288             renderer : cellRender
37289         },
37290         {
37291             xtype: 'ColumnModel',
37292             xns: Roo.grid,
37293             dataIndex : 'weekday2',
37294             header : 'Tuesday',
37295             renderer : cellRender
37296         },
37297         {
37298             xtype: 'ColumnModel',
37299             xns: Roo.grid,
37300             dataIndex : 'weekday3',
37301             header : 'Wednesday',
37302             renderer : cellRender
37303         },
37304         {
37305             xtype: 'ColumnModel',
37306             xns: Roo.grid,
37307             dataIndex : 'weekday4',
37308             header : 'Thursday',
37309             renderer : cellRender
37310         },
37311         {
37312             xtype: 'ColumnModel',
37313             xns: Roo.grid,
37314             dataIndex : 'weekday5',
37315             header : 'Friday',
37316             renderer : cellRender
37317         },
37318         {
37319             xtype: 'ColumnModel',
37320             xns: Roo.grid,
37321             dataIndex : 'weekday6',
37322             header : 'Saturday',
37323             renderer : cellRender
37324         }
37325     ]);
37326     this.cm = this.colModel;
37327     this.cm.xmodule = this.xmodule || false;
37328  
37329         
37330           
37331     //this.selModel = new Roo.grid.CellSelectionModel();
37332     //this.sm = this.selModel;
37333     //this.selModel.init(this);
37334     
37335     
37336     if(this.width){
37337         this.container.setWidth(this.width);
37338     }
37339
37340     if(this.height){
37341         this.container.setHeight(this.height);
37342     }
37343     /** @private */
37344         this.addEvents({
37345         // raw events
37346         /**
37347          * @event click
37348          * The raw click event for the entire grid.
37349          * @param {Roo.EventObject} e
37350          */
37351         "click" : true,
37352         /**
37353          * @event dblclick
37354          * The raw dblclick event for the entire grid.
37355          * @param {Roo.EventObject} e
37356          */
37357         "dblclick" : true,
37358         /**
37359          * @event contextmenu
37360          * The raw contextmenu event for the entire grid.
37361          * @param {Roo.EventObject} e
37362          */
37363         "contextmenu" : true,
37364         /**
37365          * @event mousedown
37366          * The raw mousedown event for the entire grid.
37367          * @param {Roo.EventObject} e
37368          */
37369         "mousedown" : true,
37370         /**
37371          * @event mouseup
37372          * The raw mouseup event for the entire grid.
37373          * @param {Roo.EventObject} e
37374          */
37375         "mouseup" : true,
37376         /**
37377          * @event mouseover
37378          * The raw mouseover event for the entire grid.
37379          * @param {Roo.EventObject} e
37380          */
37381         "mouseover" : true,
37382         /**
37383          * @event mouseout
37384          * The raw mouseout event for the entire grid.
37385          * @param {Roo.EventObject} e
37386          */
37387         "mouseout" : true,
37388         /**
37389          * @event keypress
37390          * The raw keypress event for the entire grid.
37391          * @param {Roo.EventObject} e
37392          */
37393         "keypress" : true,
37394         /**
37395          * @event keydown
37396          * The raw keydown event for the entire grid.
37397          * @param {Roo.EventObject} e
37398          */
37399         "keydown" : true,
37400
37401         // custom events
37402
37403         /**
37404          * @event cellclick
37405          * Fires when a cell is clicked
37406          * @param {Grid} this
37407          * @param {Number} rowIndex
37408          * @param {Number} columnIndex
37409          * @param {Roo.EventObject} e
37410          */
37411         "cellclick" : true,
37412         /**
37413          * @event celldblclick
37414          * Fires when a cell is double clicked
37415          * @param {Grid} this
37416          * @param {Number} rowIndex
37417          * @param {Number} columnIndex
37418          * @param {Roo.EventObject} e
37419          */
37420         "celldblclick" : true,
37421         /**
37422          * @event rowclick
37423          * Fires when a row is clicked
37424          * @param {Grid} this
37425          * @param {Number} rowIndex
37426          * @param {Roo.EventObject} e
37427          */
37428         "rowclick" : true,
37429         /**
37430          * @event rowdblclick
37431          * Fires when a row is double clicked
37432          * @param {Grid} this
37433          * @param {Number} rowIndex
37434          * @param {Roo.EventObject} e
37435          */
37436         "rowdblclick" : true,
37437         /**
37438          * @event headerclick
37439          * Fires when a header is clicked
37440          * @param {Grid} this
37441          * @param {Number} columnIndex
37442          * @param {Roo.EventObject} e
37443          */
37444         "headerclick" : true,
37445         /**
37446          * @event headerdblclick
37447          * Fires when a header cell is double clicked
37448          * @param {Grid} this
37449          * @param {Number} columnIndex
37450          * @param {Roo.EventObject} e
37451          */
37452         "headerdblclick" : true,
37453         /**
37454          * @event rowcontextmenu
37455          * Fires when a row is right clicked
37456          * @param {Grid} this
37457          * @param {Number} rowIndex
37458          * @param {Roo.EventObject} e
37459          */
37460         "rowcontextmenu" : true,
37461         /**
37462          * @event cellcontextmenu
37463          * Fires when a cell is right clicked
37464          * @param {Grid} this
37465          * @param {Number} rowIndex
37466          * @param {Number} cellIndex
37467          * @param {Roo.EventObject} e
37468          */
37469          "cellcontextmenu" : true,
37470         /**
37471          * @event headercontextmenu
37472          * Fires when a header is right clicked
37473          * @param {Grid} this
37474          * @param {Number} columnIndex
37475          * @param {Roo.EventObject} e
37476          */
37477         "headercontextmenu" : true,
37478         /**
37479          * @event bodyscroll
37480          * Fires when the body element is scrolled
37481          * @param {Number} scrollLeft
37482          * @param {Number} scrollTop
37483          */
37484         "bodyscroll" : true,
37485         /**
37486          * @event columnresize
37487          * Fires when the user resizes a column
37488          * @param {Number} columnIndex
37489          * @param {Number} newSize
37490          */
37491         "columnresize" : true,
37492         /**
37493          * @event columnmove
37494          * Fires when the user moves a column
37495          * @param {Number} oldIndex
37496          * @param {Number} newIndex
37497          */
37498         "columnmove" : true,
37499         /**
37500          * @event startdrag
37501          * Fires when row(s) start being dragged
37502          * @param {Grid} this
37503          * @param {Roo.GridDD} dd The drag drop object
37504          * @param {event} e The raw browser event
37505          */
37506         "startdrag" : true,
37507         /**
37508          * @event enddrag
37509          * Fires when a drag operation is complete
37510          * @param {Grid} this
37511          * @param {Roo.GridDD} dd The drag drop object
37512          * @param {event} e The raw browser event
37513          */
37514         "enddrag" : true,
37515         /**
37516          * @event dragdrop
37517          * Fires when dragged row(s) are dropped on a valid DD target
37518          * @param {Grid} this
37519          * @param {Roo.GridDD} dd The drag drop object
37520          * @param {String} targetId The target drag drop object
37521          * @param {event} e The raw browser event
37522          */
37523         "dragdrop" : true,
37524         /**
37525          * @event dragover
37526          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37527          * @param {Grid} this
37528          * @param {Roo.GridDD} dd The drag drop object
37529          * @param {String} targetId The target drag drop object
37530          * @param {event} e The raw browser event
37531          */
37532         "dragover" : true,
37533         /**
37534          * @event dragenter
37535          *  Fires when the dragged row(s) first cross another DD target while being dragged
37536          * @param {Grid} this
37537          * @param {Roo.GridDD} dd The drag drop object
37538          * @param {String} targetId The target drag drop object
37539          * @param {event} e The raw browser event
37540          */
37541         "dragenter" : true,
37542         /**
37543          * @event dragout
37544          * Fires when the dragged row(s) leave another DD target while being dragged
37545          * @param {Grid} this
37546          * @param {Roo.GridDD} dd The drag drop object
37547          * @param {String} targetId The target drag drop object
37548          * @param {event} e The raw browser event
37549          */
37550         "dragout" : true,
37551         /**
37552          * @event rowclass
37553          * Fires when a row is rendered, so you can change add a style to it.
37554          * @param {GridView} gridview   The grid view
37555          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37556          */
37557         'rowclass' : true,
37558
37559         /**
37560          * @event render
37561          * Fires when the grid is rendered
37562          * @param {Grid} grid
37563          */
37564         'render' : true,
37565             /**
37566              * @event select
37567              * Fires when a date is selected
37568              * @param {DatePicker} this
37569              * @param {Date} date The selected date
37570              */
37571         'select': true,
37572         /**
37573              * @event monthchange
37574              * Fires when the displayed month changes 
37575              * @param {DatePicker} this
37576              * @param {Date} date The selected month
37577              */
37578         'monthchange': true,
37579         /**
37580              * @event evententer
37581              * Fires when mouse over an event
37582              * @param {Calendar} this
37583              * @param {event} Event
37584              */
37585         'evententer': true,
37586         /**
37587              * @event eventleave
37588              * Fires when the mouse leaves an
37589              * @param {Calendar} this
37590              * @param {event}
37591              */
37592         'eventleave': true,
37593         /**
37594              * @event eventclick
37595              * Fires when the mouse click an
37596              * @param {Calendar} this
37597              * @param {event}
37598              */
37599         'eventclick': true,
37600         /**
37601              * @event eventrender
37602              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37603              * @param {Calendar} this
37604              * @param {data} data to be modified
37605              */
37606         'eventrender': true
37607         
37608     });
37609
37610     Roo.grid.Grid.superclass.constructor.call(this);
37611     this.on('render', function() {
37612         this.view.el.addClass('x-grid-cal'); 
37613         
37614         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37615
37616     },this);
37617     
37618     if (!Roo.grid.Calendar.style) {
37619         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37620             
37621             
37622             '.x-grid-cal .x-grid-col' :  {
37623                 height: 'auto !important',
37624                 'vertical-align': 'top'
37625             },
37626             '.x-grid-cal  .fc-event-hori' : {
37627                 height: '14px'
37628             }
37629              
37630             
37631         }, Roo.id());
37632     }
37633
37634     
37635     
37636 };
37637 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37638     /**
37639      * @cfg {Store} eventStore The store that loads events.
37640      */
37641     eventStore : 25,
37642
37643      
37644     activeDate : false,
37645     startDay : 0,
37646     autoWidth : true,
37647     monitorWindowResize : false,
37648
37649     
37650     resizeColumns : function() {
37651         var col = (this.view.el.getWidth() / 7) - 3;
37652         // loop through cols, and setWidth
37653         for(var i =0 ; i < 7 ; i++){
37654             this.cm.setColumnWidth(i, col);
37655         }
37656     },
37657      setDate :function(date) {
37658         
37659         Roo.log('setDate?');
37660         
37661         this.resizeColumns();
37662         var vd = this.activeDate;
37663         this.activeDate = date;
37664 //        if(vd && this.el){
37665 //            var t = date.getTime();
37666 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37667 //                Roo.log('using add remove');
37668 //                
37669 //                this.fireEvent('monthchange', this, date);
37670 //                
37671 //                this.cells.removeClass("fc-state-highlight");
37672 //                this.cells.each(function(c){
37673 //                   if(c.dateValue == t){
37674 //                       c.addClass("fc-state-highlight");
37675 //                       setTimeout(function(){
37676 //                            try{c.dom.firstChild.focus();}catch(e){}
37677 //                       }, 50);
37678 //                       return false;
37679 //                   }
37680 //                   return true;
37681 //                });
37682 //                return;
37683 //            }
37684 //        }
37685         
37686         var days = date.getDaysInMonth();
37687         
37688         var firstOfMonth = date.getFirstDateOfMonth();
37689         var startingPos = firstOfMonth.getDay()-this.startDay;
37690         
37691         if(startingPos < this.startDay){
37692             startingPos += 7;
37693         }
37694         
37695         var pm = date.add(Date.MONTH, -1);
37696         var prevStart = pm.getDaysInMonth()-startingPos;
37697 //        
37698         
37699         
37700         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37701         
37702         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37703         //this.cells.addClassOnOver('fc-state-hover');
37704         
37705         var cells = this.cells.elements;
37706         var textEls = this.textNodes;
37707         
37708         //Roo.each(cells, function(cell){
37709         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37710         //});
37711         
37712         days += startingPos;
37713
37714         // convert everything to numbers so it's fast
37715         var day = 86400000;
37716         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37717         //Roo.log(d);
37718         //Roo.log(pm);
37719         //Roo.log(prevStart);
37720         
37721         var today = new Date().clearTime().getTime();
37722         var sel = date.clearTime().getTime();
37723         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37724         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37725         var ddMatch = this.disabledDatesRE;
37726         var ddText = this.disabledDatesText;
37727         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37728         var ddaysText = this.disabledDaysText;
37729         var format = this.format;
37730         
37731         var setCellClass = function(cal, cell){
37732             
37733             //Roo.log('set Cell Class');
37734             cell.title = "";
37735             var t = d.getTime();
37736             
37737             //Roo.log(d);
37738             
37739             
37740             cell.dateValue = t;
37741             if(t == today){
37742                 cell.className += " fc-today";
37743                 cell.className += " fc-state-highlight";
37744                 cell.title = cal.todayText;
37745             }
37746             if(t == sel){
37747                 // disable highlight in other month..
37748                 cell.className += " fc-state-highlight";
37749                 
37750             }
37751             // disabling
37752             if(t < min) {
37753                 //cell.className = " fc-state-disabled";
37754                 cell.title = cal.minText;
37755                 return;
37756             }
37757             if(t > max) {
37758                 //cell.className = " fc-state-disabled";
37759                 cell.title = cal.maxText;
37760                 return;
37761             }
37762             if(ddays){
37763                 if(ddays.indexOf(d.getDay()) != -1){
37764                     // cell.title = ddaysText;
37765                    // cell.className = " fc-state-disabled";
37766                 }
37767             }
37768             if(ddMatch && format){
37769                 var fvalue = d.dateFormat(format);
37770                 if(ddMatch.test(fvalue)){
37771                     cell.title = ddText.replace("%0", fvalue);
37772                    cell.className = " fc-state-disabled";
37773                 }
37774             }
37775             
37776             if (!cell.initialClassName) {
37777                 cell.initialClassName = cell.dom.className;
37778             }
37779             
37780             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37781         };
37782
37783         var i = 0;
37784         
37785         for(; i < startingPos; i++) {
37786             cells[i].dayName =  (++prevStart);
37787             Roo.log(textEls[i]);
37788             d.setDate(d.getDate()+1);
37789             
37790             //cells[i].className = "fc-past fc-other-month";
37791             setCellClass(this, cells[i]);
37792         }
37793         
37794         var intDay = 0;
37795         
37796         for(; i < days; i++){
37797             intDay = i - startingPos + 1;
37798             cells[i].dayName =  (intDay);
37799             d.setDate(d.getDate()+1);
37800             
37801             cells[i].className = ''; // "x-date-active";
37802             setCellClass(this, cells[i]);
37803         }
37804         var extraDays = 0;
37805         
37806         for(; i < 42; i++) {
37807             //textEls[i].innerHTML = (++extraDays);
37808             
37809             d.setDate(d.getDate()+1);
37810             cells[i].dayName = (++extraDays);
37811             cells[i].className = "fc-future fc-other-month";
37812             setCellClass(this, cells[i]);
37813         }
37814         
37815         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37816         
37817         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37818         
37819         // this will cause all the cells to mis
37820         var rows= [];
37821         var i =0;
37822         for (var r = 0;r < 6;r++) {
37823             for (var c =0;c < 7;c++) {
37824                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37825             }    
37826         }
37827         
37828         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37829         for(i=0;i<cells.length;i++) {
37830             
37831             this.cells.elements[i].dayName = cells[i].dayName ;
37832             this.cells.elements[i].className = cells[i].className;
37833             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37834             this.cells.elements[i].title = cells[i].title ;
37835             this.cells.elements[i].dateValue = cells[i].dateValue ;
37836         }
37837         
37838         
37839         
37840         
37841         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37842         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37843         
37844         ////if(totalRows != 6){
37845             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37846            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37847        // }
37848         
37849         this.fireEvent('monthchange', this, date);
37850         
37851         
37852     },
37853  /**
37854      * Returns the grid's SelectionModel.
37855      * @return {SelectionModel}
37856      */
37857     getSelectionModel : function(){
37858         if(!this.selModel){
37859             this.selModel = new Roo.grid.CellSelectionModel();
37860         }
37861         return this.selModel;
37862     },
37863
37864     load: function() {
37865         this.eventStore.load()
37866         
37867         
37868         
37869     },
37870     
37871     findCell : function(dt) {
37872         dt = dt.clearTime().getTime();
37873         var ret = false;
37874         this.cells.each(function(c){
37875             //Roo.log("check " +c.dateValue + '?=' + dt);
37876             if(c.dateValue == dt){
37877                 ret = c;
37878                 return false;
37879             }
37880             return true;
37881         });
37882         
37883         return ret;
37884     },
37885     
37886     findCells : function(rec) {
37887         var s = rec.data.start_dt.clone().clearTime().getTime();
37888        // Roo.log(s);
37889         var e= rec.data.end_dt.clone().clearTime().getTime();
37890        // Roo.log(e);
37891         var ret = [];
37892         this.cells.each(function(c){
37893              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37894             
37895             if(c.dateValue > e){
37896                 return ;
37897             }
37898             if(c.dateValue < s){
37899                 return ;
37900             }
37901             ret.push(c);
37902         });
37903         
37904         return ret;    
37905     },
37906     
37907     findBestRow: function(cells)
37908     {
37909         var ret = 0;
37910         
37911         for (var i =0 ; i < cells.length;i++) {
37912             ret  = Math.max(cells[i].rows || 0,ret);
37913         }
37914         return ret;
37915         
37916     },
37917     
37918     
37919     addItem : function(rec)
37920     {
37921         // look for vertical location slot in
37922         var cells = this.findCells(rec);
37923         
37924         rec.row = this.findBestRow(cells);
37925         
37926         // work out the location.
37927         
37928         var crow = false;
37929         var rows = [];
37930         for(var i =0; i < cells.length; i++) {
37931             if (!crow) {
37932                 crow = {
37933                     start : cells[i],
37934                     end :  cells[i]
37935                 };
37936                 continue;
37937             }
37938             if (crow.start.getY() == cells[i].getY()) {
37939                 // on same row.
37940                 crow.end = cells[i];
37941                 continue;
37942             }
37943             // different row.
37944             rows.push(crow);
37945             crow = {
37946                 start: cells[i],
37947                 end : cells[i]
37948             };
37949             
37950         }
37951         
37952         rows.push(crow);
37953         rec.els = [];
37954         rec.rows = rows;
37955         rec.cells = cells;
37956         for (var i = 0; i < cells.length;i++) {
37957             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37958             
37959         }
37960         
37961         
37962     },
37963     
37964     clearEvents: function() {
37965         
37966         if (!this.eventStore.getCount()) {
37967             return;
37968         }
37969         // reset number of rows in cells.
37970         Roo.each(this.cells.elements, function(c){
37971             c.rows = 0;
37972         });
37973         
37974         this.eventStore.each(function(e) {
37975             this.clearEvent(e);
37976         },this);
37977         
37978     },
37979     
37980     clearEvent : function(ev)
37981     {
37982         if (ev.els) {
37983             Roo.each(ev.els, function(el) {
37984                 el.un('mouseenter' ,this.onEventEnter, this);
37985                 el.un('mouseleave' ,this.onEventLeave, this);
37986                 el.remove();
37987             },this);
37988             ev.els = [];
37989         }
37990     },
37991     
37992     
37993     renderEvent : function(ev,ctr) {
37994         if (!ctr) {
37995              ctr = this.view.el.select('.fc-event-container',true).first();
37996         }
37997         
37998          
37999         this.clearEvent(ev);
38000             //code
38001        
38002         
38003         
38004         ev.els = [];
38005         var cells = ev.cells;
38006         var rows = ev.rows;
38007         this.fireEvent('eventrender', this, ev);
38008         
38009         for(var i =0; i < rows.length; i++) {
38010             
38011             cls = '';
38012             if (i == 0) {
38013                 cls += ' fc-event-start';
38014             }
38015             if ((i+1) == rows.length) {
38016                 cls += ' fc-event-end';
38017             }
38018             
38019             //Roo.log(ev.data);
38020             // how many rows should it span..
38021             var cg = this.eventTmpl.append(ctr,Roo.apply({
38022                 fccls : cls
38023                 
38024             }, ev.data) , true);
38025             
38026             
38027             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38028             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38029             cg.on('click', this.onEventClick, this, ev);
38030             
38031             ev.els.push(cg);
38032             
38033             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38034             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38035             //Roo.log(cg);
38036              
38037             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38038             cg.setWidth(ebox.right - sbox.x -2);
38039         }
38040     },
38041     
38042     renderEvents: function()
38043     {   
38044         // first make sure there is enough space..
38045         
38046         if (!this.eventTmpl) {
38047             this.eventTmpl = new Roo.Template(
38048                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38049                     '<div class="fc-event-inner">' +
38050                         '<span class="fc-event-time">{time}</span>' +
38051                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38052                     '</div>' +
38053                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38054                 '</div>'
38055             );
38056                 
38057         }
38058                
38059         
38060         
38061         this.cells.each(function(c) {
38062             //Roo.log(c.select('.fc-day-content div',true).first());
38063             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38064         });
38065         
38066         var ctr = this.view.el.select('.fc-event-container',true).first();
38067         
38068         var cls;
38069         this.eventStore.each(function(ev){
38070             
38071             this.renderEvent(ev);
38072              
38073              
38074         }, this);
38075         this.view.layout();
38076         
38077     },
38078     
38079     onEventEnter: function (e, el,event,d) {
38080         this.fireEvent('evententer', this, el, event);
38081     },
38082     
38083     onEventLeave: function (e, el,event,d) {
38084         this.fireEvent('eventleave', this, el, event);
38085     },
38086     
38087     onEventClick: function (e, el,event,d) {
38088         this.fireEvent('eventclick', this, el, event);
38089     },
38090     
38091     onMonthChange: function () {
38092         this.store.load();
38093     },
38094     
38095     onLoad: function () {
38096         
38097         //Roo.log('calendar onload');
38098 //         
38099         if(this.eventStore.getCount() > 0){
38100             
38101            
38102             
38103             this.eventStore.each(function(d){
38104                 
38105                 
38106                 // FIXME..
38107                 var add =   d.data;
38108                 if (typeof(add.end_dt) == 'undefined')  {
38109                     Roo.log("Missing End time in calendar data: ");
38110                     Roo.log(d);
38111                     return;
38112                 }
38113                 if (typeof(add.start_dt) == 'undefined')  {
38114                     Roo.log("Missing Start time in calendar data: ");
38115                     Roo.log(d);
38116                     return;
38117                 }
38118                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38119                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38120                 add.id = add.id || d.id;
38121                 add.title = add.title || '??';
38122                 
38123                 this.addItem(d);
38124                 
38125              
38126             },this);
38127         }
38128         
38129         this.renderEvents();
38130     }
38131     
38132
38133 });
38134 /*
38135  grid : {
38136                 xtype: 'Grid',
38137                 xns: Roo.grid,
38138                 listeners : {
38139                     render : function ()
38140                     {
38141                         _this.grid = this;
38142                         
38143                         if (!this.view.el.hasClass('course-timesheet')) {
38144                             this.view.el.addClass('course-timesheet');
38145                         }
38146                         if (this.tsStyle) {
38147                             this.ds.load({});
38148                             return; 
38149                         }
38150                         Roo.log('width');
38151                         Roo.log(_this.grid.view.el.getWidth());
38152                         
38153                         
38154                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38155                             '.course-timesheet .x-grid-row' : {
38156                                 height: '80px'
38157                             },
38158                             '.x-grid-row td' : {
38159                                 'vertical-align' : 0
38160                             },
38161                             '.course-edit-link' : {
38162                                 'color' : 'blue',
38163                                 'text-overflow' : 'ellipsis',
38164                                 'overflow' : 'hidden',
38165                                 'white-space' : 'nowrap',
38166                                 'cursor' : 'pointer'
38167                             },
38168                             '.sub-link' : {
38169                                 'color' : 'green'
38170                             },
38171                             '.de-act-sup-link' : {
38172                                 'color' : 'purple',
38173                                 'text-decoration' : 'line-through'
38174                             },
38175                             '.de-act-link' : {
38176                                 'color' : 'red',
38177                                 'text-decoration' : 'line-through'
38178                             },
38179                             '.course-timesheet .course-highlight' : {
38180                                 'border-top-style': 'dashed !important',
38181                                 'border-bottom-bottom': 'dashed !important'
38182                             },
38183                             '.course-timesheet .course-item' : {
38184                                 'font-family'   : 'tahoma, arial, helvetica',
38185                                 'font-size'     : '11px',
38186                                 'overflow'      : 'hidden',
38187                                 'padding-left'  : '10px',
38188                                 'padding-right' : '10px',
38189                                 'padding-top' : '10px' 
38190                             }
38191                             
38192                         }, Roo.id());
38193                                 this.ds.load({});
38194                     }
38195                 },
38196                 autoWidth : true,
38197                 monitorWindowResize : false,
38198                 cellrenderer : function(v,x,r)
38199                 {
38200                     return v;
38201                 },
38202                 sm : {
38203                     xtype: 'CellSelectionModel',
38204                     xns: Roo.grid
38205                 },
38206                 dataSource : {
38207                     xtype: 'Store',
38208                     xns: Roo.data,
38209                     listeners : {
38210                         beforeload : function (_self, options)
38211                         {
38212                             options.params = options.params || {};
38213                             options.params._month = _this.monthField.getValue();
38214                             options.params.limit = 9999;
38215                             options.params['sort'] = 'when_dt';    
38216                             options.params['dir'] = 'ASC';    
38217                             this.proxy.loadResponse = this.loadResponse;
38218                             Roo.log("load?");
38219                             //this.addColumns();
38220                         },
38221                         load : function (_self, records, options)
38222                         {
38223                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38224                                 // if you click on the translation.. you can edit it...
38225                                 var el = Roo.get(this);
38226                                 var id = el.dom.getAttribute('data-id');
38227                                 var d = el.dom.getAttribute('data-date');
38228                                 var t = el.dom.getAttribute('data-time');
38229                                 //var id = this.child('span').dom.textContent;
38230                                 
38231                                 //Roo.log(this);
38232                                 Pman.Dialog.CourseCalendar.show({
38233                                     id : id,
38234                                     when_d : d,
38235                                     when_t : t,
38236                                     productitem_active : id ? 1 : 0
38237                                 }, function() {
38238                                     _this.grid.ds.load({});
38239                                 });
38240                            
38241                            });
38242                            
38243                            _this.panel.fireEvent('resize', [ '', '' ]);
38244                         }
38245                     },
38246                     loadResponse : function(o, success, response){
38247                             // this is overridden on before load..
38248                             
38249                             Roo.log("our code?");       
38250                             //Roo.log(success);
38251                             //Roo.log(response)
38252                             delete this.activeRequest;
38253                             if(!success){
38254                                 this.fireEvent("loadexception", this, o, response);
38255                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38256                                 return;
38257                             }
38258                             var result;
38259                             try {
38260                                 result = o.reader.read(response);
38261                             }catch(e){
38262                                 Roo.log("load exception?");
38263                                 this.fireEvent("loadexception", this, o, response, e);
38264                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38265                                 return;
38266                             }
38267                             Roo.log("ready...");        
38268                             // loop through result.records;
38269                             // and set this.tdate[date] = [] << array of records..
38270                             _this.tdata  = {};
38271                             Roo.each(result.records, function(r){
38272                                 //Roo.log(r.data);
38273                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38274                                     _this.tdata[r.data.when_dt.format('j')] = [];
38275                                 }
38276                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38277                             });
38278                             
38279                             //Roo.log(_this.tdata);
38280                             
38281                             result.records = [];
38282                             result.totalRecords = 6;
38283                     
38284                             // let's generate some duumy records for the rows.
38285                             //var st = _this.dateField.getValue();
38286                             
38287                             // work out monday..
38288                             //st = st.add(Date.DAY, -1 * st.format('w'));
38289                             
38290                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38291                             
38292                             var firstOfMonth = date.getFirstDayOfMonth();
38293                             var days = date.getDaysInMonth();
38294                             var d = 1;
38295                             var firstAdded = false;
38296                             for (var i = 0; i < result.totalRecords ; i++) {
38297                                 //var d= st.add(Date.DAY, i);
38298                                 var row = {};
38299                                 var added = 0;
38300                                 for(var w = 0 ; w < 7 ; w++){
38301                                     if(!firstAdded && firstOfMonth != w){
38302                                         continue;
38303                                     }
38304                                     if(d > days){
38305                                         continue;
38306                                     }
38307                                     firstAdded = true;
38308                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38309                                     row['weekday'+w] = String.format(
38310                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38311                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38312                                                     d,
38313                                                     date.format('Y-m-')+dd
38314                                                 );
38315                                     added++;
38316                                     if(typeof(_this.tdata[d]) != 'undefined'){
38317                                         Roo.each(_this.tdata[d], function(r){
38318                                             var is_sub = '';
38319                                             var deactive = '';
38320                                             var id = r.id;
38321                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38322                                             if(r.parent_id*1>0){
38323                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38324                                                 id = r.parent_id;
38325                                             }
38326                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38327                                                 deactive = 'de-act-link';
38328                                             }
38329                                             
38330                                             row['weekday'+w] += String.format(
38331                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38332                                                     id, //0
38333                                                     r.product_id_name, //1
38334                                                     r.when_dt.format('h:ia'), //2
38335                                                     is_sub, //3
38336                                                     deactive, //4
38337                                                     desc // 5
38338                                             );
38339                                         });
38340                                     }
38341                                     d++;
38342                                 }
38343                                 
38344                                 // only do this if something added..
38345                                 if(added > 0){ 
38346                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38347                                 }
38348                                 
38349                                 
38350                                 // push it twice. (second one with an hour..
38351                                 
38352                             }
38353                             //Roo.log(result);
38354                             this.fireEvent("load", this, o, o.request.arg);
38355                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38356                         },
38357                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38358                     proxy : {
38359                         xtype: 'HttpProxy',
38360                         xns: Roo.data,
38361                         method : 'GET',
38362                         url : baseURL + '/Roo/Shop_course.php'
38363                     },
38364                     reader : {
38365                         xtype: 'JsonReader',
38366                         xns: Roo.data,
38367                         id : 'id',
38368                         fields : [
38369                             {
38370                                 'name': 'id',
38371                                 'type': 'int'
38372                             },
38373                             {
38374                                 'name': 'when_dt',
38375                                 'type': 'string'
38376                             },
38377                             {
38378                                 'name': 'end_dt',
38379                                 'type': 'string'
38380                             },
38381                             {
38382                                 'name': 'parent_id',
38383                                 'type': 'int'
38384                             },
38385                             {
38386                                 'name': 'product_id',
38387                                 'type': 'int'
38388                             },
38389                             {
38390                                 'name': 'productitem_id',
38391                                 'type': 'int'
38392                             },
38393                             {
38394                                 'name': 'guid',
38395                                 'type': 'int'
38396                             }
38397                         ]
38398                     }
38399                 },
38400                 toolbar : {
38401                     xtype: 'Toolbar',
38402                     xns: Roo,
38403                     items : [
38404                         {
38405                             xtype: 'Button',
38406                             xns: Roo.Toolbar,
38407                             listeners : {
38408                                 click : function (_self, e)
38409                                 {
38410                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38411                                     sd.setMonth(sd.getMonth()-1);
38412                                     _this.monthField.setValue(sd.format('Y-m-d'));
38413                                     _this.grid.ds.load({});
38414                                 }
38415                             },
38416                             text : "Back"
38417                         },
38418                         {
38419                             xtype: 'Separator',
38420                             xns: Roo.Toolbar
38421                         },
38422                         {
38423                             xtype: 'MonthField',
38424                             xns: Roo.form,
38425                             listeners : {
38426                                 render : function (_self)
38427                                 {
38428                                     _this.monthField = _self;
38429                                    // _this.monthField.set  today
38430                                 },
38431                                 select : function (combo, date)
38432                                 {
38433                                     _this.grid.ds.load({});
38434                                 }
38435                             },
38436                             value : (function() { return new Date(); })()
38437                         },
38438                         {
38439                             xtype: 'Separator',
38440                             xns: Roo.Toolbar
38441                         },
38442                         {
38443                             xtype: 'TextItem',
38444                             xns: Roo.Toolbar,
38445                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38446                         },
38447                         {
38448                             xtype: 'Fill',
38449                             xns: Roo.Toolbar
38450                         },
38451                         {
38452                             xtype: 'Button',
38453                             xns: Roo.Toolbar,
38454                             listeners : {
38455                                 click : function (_self, e)
38456                                 {
38457                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38458                                     sd.setMonth(sd.getMonth()+1);
38459                                     _this.monthField.setValue(sd.format('Y-m-d'));
38460                                     _this.grid.ds.load({});
38461                                 }
38462                             },
38463                             text : "Next"
38464                         }
38465                     ]
38466                 },
38467                  
38468             }
38469         };
38470         
38471         *//*
38472  * Based on:
38473  * Ext JS Library 1.1.1
38474  * Copyright(c) 2006-2007, Ext JS, LLC.
38475  *
38476  * Originally Released Under LGPL - original licence link has changed is not relivant.
38477  *
38478  * Fork - LGPL
38479  * <script type="text/javascript">
38480  */
38481  
38482 /**
38483  * @class Roo.LoadMask
38484  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38485  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38486  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38487  * element's UpdateManager load indicator and will be destroyed after the initial load.
38488  * @constructor
38489  * Create a new LoadMask
38490  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38491  * @param {Object} config The config object
38492  */
38493 Roo.LoadMask = function(el, config){
38494     this.el = Roo.get(el);
38495     Roo.apply(this, config);
38496     if(this.store){
38497         this.store.on('beforeload', this.onBeforeLoad, this);
38498         this.store.on('load', this.onLoad, this);
38499         this.store.on('loadexception', this.onLoadException, this);
38500         this.removeMask = false;
38501     }else{
38502         var um = this.el.getUpdateManager();
38503         um.showLoadIndicator = false; // disable the default indicator
38504         um.on('beforeupdate', this.onBeforeLoad, this);
38505         um.on('update', this.onLoad, this);
38506         um.on('failure', this.onLoad, this);
38507         this.removeMask = true;
38508     }
38509 };
38510
38511 Roo.LoadMask.prototype = {
38512     /**
38513      * @cfg {Boolean} removeMask
38514      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38515      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38516      */
38517     removeMask : false,
38518     /**
38519      * @cfg {String} msg
38520      * The text to display in a centered loading message box (defaults to 'Loading...')
38521      */
38522     msg : 'Loading...',
38523     /**
38524      * @cfg {String} msgCls
38525      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38526      */
38527     msgCls : 'x-mask-loading',
38528
38529     /**
38530      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38531      * @type Boolean
38532      */
38533     disabled: false,
38534
38535     /**
38536      * Disables the mask to prevent it from being displayed
38537      */
38538     disable : function(){
38539        this.disabled = true;
38540     },
38541
38542     /**
38543      * Enables the mask so that it can be displayed
38544      */
38545     enable : function(){
38546         this.disabled = false;
38547     },
38548     
38549     onLoadException : function()
38550     {
38551         Roo.log(arguments);
38552         
38553         if (typeof(arguments[3]) != 'undefined') {
38554             Roo.MessageBox.alert("Error loading",arguments[3]);
38555         } 
38556         /*
38557         try {
38558             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38559                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38560             }   
38561         } catch(e) {
38562             
38563         }
38564         */
38565     
38566         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38567     },
38568     // private
38569     onLoad : function()
38570     {
38571         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38572     },
38573
38574     // private
38575     onBeforeLoad : function(){
38576         if(!this.disabled){
38577             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38578         }
38579     },
38580
38581     // private
38582     destroy : function(){
38583         if(this.store){
38584             this.store.un('beforeload', this.onBeforeLoad, this);
38585             this.store.un('load', this.onLoad, this);
38586             this.store.un('loadexception', this.onLoadException, this);
38587         }else{
38588             var um = this.el.getUpdateManager();
38589             um.un('beforeupdate', this.onBeforeLoad, this);
38590             um.un('update', this.onLoad, this);
38591             um.un('failure', this.onLoad, this);
38592         }
38593     }
38594 };/*
38595  * Based on:
38596  * Ext JS Library 1.1.1
38597  * Copyright(c) 2006-2007, Ext JS, LLC.
38598  *
38599  * Originally Released Under LGPL - original licence link has changed is not relivant.
38600  *
38601  * Fork - LGPL
38602  * <script type="text/javascript">
38603  */
38604
38605
38606 /**
38607  * @class Roo.XTemplate
38608  * @extends Roo.Template
38609  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38610 <pre><code>
38611 var t = new Roo.XTemplate(
38612         '&lt;select name="{name}"&gt;',
38613                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38614         '&lt;/select&gt;'
38615 );
38616  
38617 // then append, applying the master template values
38618  </code></pre>
38619  *
38620  * Supported features:
38621  *
38622  *  Tags:
38623
38624 <pre><code>
38625       {a_variable} - output encoded.
38626       {a_variable.format:("Y-m-d")} - call a method on the variable
38627       {a_variable:raw} - unencoded output
38628       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38629       {a_variable:this.method_on_template(...)} - call a method on the template object.
38630  
38631 </code></pre>
38632  *  The tpl tag:
38633 <pre><code>
38634         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38635         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38636         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38637         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38638   
38639         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38640         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38641 </code></pre>
38642  *      
38643  */
38644 Roo.XTemplate = function()
38645 {
38646     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38647     if (this.html) {
38648         this.compile();
38649     }
38650 };
38651
38652
38653 Roo.extend(Roo.XTemplate, Roo.Template, {
38654
38655     /**
38656      * The various sub templates
38657      */
38658     tpls : false,
38659     /**
38660      *
38661      * basic tag replacing syntax
38662      * WORD:WORD()
38663      *
38664      * // you can fake an object call by doing this
38665      *  x.t:(test,tesT) 
38666      * 
38667      */
38668     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38669
38670     /**
38671      * compile the template
38672      *
38673      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38674      *
38675      */
38676     compile: function()
38677     {
38678         var s = this.html;
38679      
38680         s = ['<tpl>', s, '</tpl>'].join('');
38681     
38682         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38683             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38684             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38685             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38686             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38687             m,
38688             id     = 0,
38689             tpls   = [];
38690     
38691         while(true == !!(m = s.match(re))){
38692             var forMatch   = m[0].match(nameRe),
38693                 ifMatch   = m[0].match(ifRe),
38694                 execMatch   = m[0].match(execRe),
38695                 namedMatch   = m[0].match(namedRe),
38696                 
38697                 exp  = null, 
38698                 fn   = null,
38699                 exec = null,
38700                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38701                 
38702             if (ifMatch) {
38703                 // if - puts fn into test..
38704                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38705                 if(exp){
38706                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38707                 }
38708             }
38709             
38710             if (execMatch) {
38711                 // exec - calls a function... returns empty if true is  returned.
38712                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38713                 if(exp){
38714                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38715                 }
38716             }
38717             
38718             
38719             if (name) {
38720                 // for = 
38721                 switch(name){
38722                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38723                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38724                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38725                 }
38726             }
38727             var uid = namedMatch ? namedMatch[1] : id;
38728             
38729             
38730             tpls.push({
38731                 id:     namedMatch ? namedMatch[1] : id,
38732                 target: name,
38733                 exec:   exec,
38734                 test:   fn,
38735                 body:   m[1] || ''
38736             });
38737             if (namedMatch) {
38738                 s = s.replace(m[0], '');
38739             } else { 
38740                 s = s.replace(m[0], '{xtpl'+ id + '}');
38741             }
38742             ++id;
38743         }
38744         this.tpls = [];
38745         for(var i = tpls.length-1; i >= 0; --i){
38746             this.compileTpl(tpls[i]);
38747             this.tpls[tpls[i].id] = tpls[i];
38748         }
38749         this.master = tpls[tpls.length-1];
38750         return this;
38751     },
38752     /**
38753      * same as applyTemplate, except it's done to one of the subTemplates
38754      * when using named templates, you can do:
38755      *
38756      * var str = pl.applySubTemplate('your-name', values);
38757      *
38758      * 
38759      * @param {Number} id of the template
38760      * @param {Object} values to apply to template
38761      * @param {Object} parent (normaly the instance of this object)
38762      */
38763     applySubTemplate : function(id, values, parent)
38764     {
38765         
38766         
38767         var t = this.tpls[id];
38768         
38769         
38770         try { 
38771             if(t.test && !t.test.call(this, values, parent)){
38772                 return '';
38773             }
38774         } catch(e) {
38775             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38776             Roo.log(e.toString());
38777             Roo.log(t.test);
38778             return ''
38779         }
38780         try { 
38781             
38782             if(t.exec && t.exec.call(this, values, parent)){
38783                 return '';
38784             }
38785         } catch(e) {
38786             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38787             Roo.log(e.toString());
38788             Roo.log(t.exec);
38789             return ''
38790         }
38791         try {
38792             var vs = t.target ? t.target.call(this, values, parent) : values;
38793             parent = t.target ? values : parent;
38794             if(t.target && vs instanceof Array){
38795                 var buf = [];
38796                 for(var i = 0, len = vs.length; i < len; i++){
38797                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38798                 }
38799                 return buf.join('');
38800             }
38801             return t.compiled.call(this, vs, parent);
38802         } catch (e) {
38803             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38804             Roo.log(e.toString());
38805             Roo.log(t.compiled);
38806             return '';
38807         }
38808     },
38809
38810     compileTpl : function(tpl)
38811     {
38812         var fm = Roo.util.Format;
38813         var useF = this.disableFormats !== true;
38814         var sep = Roo.isGecko ? "+" : ",";
38815         var undef = function(str) {
38816             Roo.log("Property not found :"  + str);
38817             return '';
38818         };
38819         
38820         var fn = function(m, name, format, args)
38821         {
38822             //Roo.log(arguments);
38823             args = args ? args.replace(/\\'/g,"'") : args;
38824             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38825             if (typeof(format) == 'undefined') {
38826                 format= 'htmlEncode';
38827             }
38828             if (format == 'raw' ) {
38829                 format = false;
38830             }
38831             
38832             if(name.substr(0, 4) == 'xtpl'){
38833                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38834             }
38835             
38836             // build an array of options to determine if value is undefined..
38837             
38838             // basically get 'xxxx.yyyy' then do
38839             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38840             //    (function () { Roo.log("Property not found"); return ''; })() :
38841             //    ......
38842             
38843             var udef_ar = [];
38844             var lookfor = '';
38845             Roo.each(name.split('.'), function(st) {
38846                 lookfor += (lookfor.length ? '.': '') + st;
38847                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38848             });
38849             
38850             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38851             
38852             
38853             if(format && useF){
38854                 
38855                 args = args ? ',' + args : "";
38856                  
38857                 if(format.substr(0, 5) != "this."){
38858                     format = "fm." + format + '(';
38859                 }else{
38860                     format = 'this.call("'+ format.substr(5) + '", ';
38861                     args = ", values";
38862                 }
38863                 
38864                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38865             }
38866              
38867             if (args.length) {
38868                 // called with xxyx.yuu:(test,test)
38869                 // change to ()
38870                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38871             }
38872             // raw.. - :raw modifier..
38873             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38874             
38875         };
38876         var body;
38877         // branched to use + in gecko and [].join() in others
38878         if(Roo.isGecko){
38879             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38880                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38881                     "';};};";
38882         }else{
38883             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38884             body.push(tpl.body.replace(/(\r\n|\n)/g,
38885                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38886             body.push("'].join('');};};");
38887             body = body.join('');
38888         }
38889         
38890         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38891        
38892         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38893         eval(body);
38894         
38895         return this;
38896     },
38897
38898     applyTemplate : function(values){
38899         return this.master.compiled.call(this, values, {});
38900         //var s = this.subs;
38901     },
38902
38903     apply : function(){
38904         return this.applyTemplate.apply(this, arguments);
38905     }
38906
38907  });
38908
38909 Roo.XTemplate.from = function(el){
38910     el = Roo.getDom(el);
38911     return new Roo.XTemplate(el.value || el.innerHTML);
38912 };